Merge branch 'master' of https://github.com/tobspr/shapez.io
@ -11,6 +11,7 @@ module.exports = {
|
||||
);
|
||||
return commitHash.replace(/^\s+|\s+$/g, "");
|
||||
},
|
||||
|
||||
getAllResourceImages() {
|
||||
return glob
|
||||
.sync("res/**/*.@(png|svg|jpg)", { cwd: ".." })
|
||||
@ -24,18 +25,6 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
getAllAtlasImages() {
|
||||
return glob
|
||||
.sync("res_built/atlas/*.png", { cwd: ".." })
|
||||
.map(s => s.replace("res_built/atlas/", "res/"));
|
||||
},
|
||||
|
||||
getAllSounds() {
|
||||
return glob
|
||||
.sync("res_built/sounds/**/*.mp3", { cwd: ".." })
|
||||
.map(s => s.replace("res_built/sounds/", "res/sounds/"));
|
||||
},
|
||||
|
||||
getVersion() {
|
||||
return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString());
|
||||
},
|
||||
|
19
gulp/cordova.js
vendored
@ -132,25 +132,6 @@ function gulptasksCordova($, gulp, buildFolder) {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// gulp.task("pushToStagingRepo", (cb) => {
|
||||
// var cmd = spawn('../push-pgb.sh', ['https://TOKEN@github.com/tobspr/shapezapp-cordova-buildslave.git'],
|
||||
// { stdio: 'inherit', stdout: 'inherit', stderr: 'inherit', shell: true });
|
||||
// cmd.on('close', function (code) {
|
||||
// console.log('push staging exited with code ' + code + " / " + cmd.stdout + " / " + cmd.stderr);
|
||||
// cb(code);
|
||||
// });
|
||||
|
||||
// });
|
||||
|
||||
// gulp.task("pushToProdRepo", (cb) => {
|
||||
// var cmd = spawn('../push-pgb.sh', ['https://TOKEN@github.com/tobspr/shapezapp-cordova-buildslave-release.git'],
|
||||
// { stdio: 'inherit', stdout: 'inherit', stderr: 'inherit', shell: true });
|
||||
// cmd.on('close', function (code) {
|
||||
// console.log('push prod exited with code ' + code + " / " + cmd.stdout + " / " + cmd.stderr);
|
||||
// cb(code);
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -77,7 +77,6 @@ function gulptasksCSS($, gulp, buildFolder, browserSync) {
|
||||
.pipe($.plumber())
|
||||
.pipe($.sass.sync({ outputStyle: "compressed" }).on("error", $.sass.logError))
|
||||
.pipe($.postcss(postcssPlugins(true, { cachebust: true })))
|
||||
// .pipe($.cssbeautify())
|
||||
.pipe(gulp.dest(buildFolder))
|
||||
);
|
||||
});
|
||||
@ -90,7 +89,6 @@ function gulptasksCSS($, gulp, buildFolder, browserSync) {
|
||||
.pipe($.plumber())
|
||||
.pipe($.sass.sync({ outputStyle: "compressed" }).on("error", $.sass.logError))
|
||||
.pipe($.postcss(postcssPlugins(true, { cachebust: false })))
|
||||
// .pipe($.cssbeautify())
|
||||
.pipe(gulp.dest(buildFolder))
|
||||
);
|
||||
});
|
||||
|
67
gulp/html.js
@ -9,7 +9,7 @@ function computeIntegrityHash(fullPath, algorithm = "sha256") {
|
||||
return algorithm + "-" + hash;
|
||||
}
|
||||
|
||||
function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
function gulptasksHTML($, gulp, buildFolder) {
|
||||
const commitHash = buildUtils.getRevision();
|
||||
async function buildHtml(
|
||||
apiUrl,
|
||||
@ -27,9 +27,8 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
return gulp
|
||||
.src("../src/html/" + (standalone ? "index.standalone.html" : "index.html"))
|
||||
.pipe(
|
||||
$.dom(function () {
|
||||
// @ts-ignore
|
||||
const document = /** @type {Document} */ (this);
|
||||
$.dom(/** @this {Document} **/ function () {
|
||||
const document = this;
|
||||
|
||||
// Preconnect to api
|
||||
const prefetchLink = document.createElement("link");
|
||||
@ -38,21 +37,6 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
prefetchLink.setAttribute("crossorigin", "anonymous");
|
||||
document.head.appendChild(prefetchLink);
|
||||
|
||||
// // Append css preload
|
||||
// const cssPreload = document.createElement("link");
|
||||
// cssPreload.rel = "preload";
|
||||
// cssPreload.href = cachebust("main.css");
|
||||
// cssPreload.setAttribute("as", "style");
|
||||
// document.head.appendChild(cssPreload);
|
||||
// document.head.appendChild(prefetchLink);
|
||||
|
||||
// // Append js preload
|
||||
// const jsPreload = document.createElement("link");
|
||||
// jsPreload.rel = "preload";
|
||||
// jsPreload.href = cachebust("bundle.js");
|
||||
// jsPreload.setAttribute("as", "script");
|
||||
// document.head.appendChild(jsPreload);
|
||||
|
||||
// Append css
|
||||
const css = document.createElement("link");
|
||||
css.rel = "stylesheet";
|
||||
@ -68,17 +52,6 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
}
|
||||
document.head.appendChild(css);
|
||||
|
||||
if (analytics) {
|
||||
// Logrocket
|
||||
// const logrocketScript = document.createElement("script");
|
||||
// logrocketScript.src = "https://cdn.lr-ingest.io/LogRocket.min.js";
|
||||
// logrocketScript.setAttribute("crossorigin", "anonymous");
|
||||
// document.head.appendChild(logrocketScript);
|
||||
// const logrocketInit = document.createElement("script");
|
||||
// logrocketInit.textContent = "window.LogRocket && window.LogRocket.init('TODO: GET LOGROCKET ID');";
|
||||
// document.head.appendChild(logrocketInit);
|
||||
}
|
||||
|
||||
if (app) {
|
||||
// Append cordova link
|
||||
const cdv = document.createElement("script");
|
||||
@ -114,18 +87,9 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
|
||||
// Do not need to preload in app or standalone
|
||||
if (!hasLocalFiles) {
|
||||
// Preload images
|
||||
const images = buildUtils.getAllResourceImages();
|
||||
|
||||
// Preload essentials
|
||||
const preloads = ["fonts/GameFont.woff2"];
|
||||
|
||||
// for (let i = 0; i < images.length; ++i) {
|
||||
// if (preloads.indexOf(images[i]) < 0) {
|
||||
// preloads.push(images[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
preloads.forEach(src => {
|
||||
const preloadLink = document.createElement("link");
|
||||
preloadLink.rel = "preload";
|
||||
@ -138,23 +102,6 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
}
|
||||
document.head.appendChild(preloadLink);
|
||||
});
|
||||
|
||||
// Sound preloads
|
||||
// const sounds = buildUtils.getAllSounds();
|
||||
// sounds.forEach((src) => {
|
||||
|
||||
// if (src.indexOf("sounds/music/") >= 0) {
|
||||
// // skip music
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const preloadLink = document.createElement("link");
|
||||
// preloadLink.rel = "preload";
|
||||
// preloadLink.href = cachebust(src);
|
||||
// // preloadLink.setAttribute("crossorigin", "anonymous");
|
||||
// preloadLink.setAttribute("as", "fetch");
|
||||
// document.head.appendChild(preloadLink);
|
||||
// });
|
||||
}
|
||||
|
||||
const loadingSvg = `background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHN0eWxlPSJtYXJnaW46YXV0bztiYWNrZ3JvdW5kOjAgMCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgZGlzcGxheT0iYmxvY2siPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzM5Mzc0NyIgc3Ryb2tlLXdpZHRoPSIzIiByPSI0MiIgc3Ryb2tlLWRhc2hhcnJheT0iMTk3LjkyMDMzNzE3NjE1Njk4IDY3Ljk3MzQ0NTcyNTM4NTY2IiB0cmFuc2Zvcm09InJvdGF0ZSg0OC4yNjUgNTAgNTApIj48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGR1cj0iNS41NTU1NTU1NTU1NTU1NTVzIiB2YWx1ZXM9IjAgNTAgNTA7MzYwIDUwIDUwIiBrZXlUaW1lcz0iMDsxIi8+PC9jaXJjbGU+PC9zdmc+")`;
|
||||
@ -167,7 +114,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
font-display: swap;
|
||||
src: url('${cachebust("res/fonts/GameFont.woff2")}') format('woff2');
|
||||
}
|
||||
|
||||
|
||||
#ll_fp {
|
||||
font-family: GameFont;
|
||||
font-size: 14px;
|
||||
@ -177,7 +124,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
left: 0;
|
||||
opacity: 0.05;
|
||||
}
|
||||
|
||||
|
||||
#ll_p {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
@ -190,7 +137,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
#ll_p > div {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
@ -201,7 +148,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
||||
font-family: 'GameFont', sans-serif;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
|
||||
#ll_p > span {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
|
@ -6,12 +6,6 @@ function requireUncached(module) {
|
||||
}
|
||||
|
||||
function gulptasksJS($, gulp, buildFolder, browserSync) {
|
||||
gulp.task("js.prettify", () => {
|
||||
return gulp
|
||||
.src(path.join(buildFolder, "bundle.js"))
|
||||
.pipe($.jsbeautifier(require("./jsbeautify.json")))
|
||||
.pipe(gulp.dest(buildFolder));
|
||||
});
|
||||
|
||||
//// DEV
|
||||
|
||||
@ -71,6 +65,7 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
|
||||
gulp.task("js.staging", gulp.parallel("js.staging.transpiled", "js.staging.latest"));
|
||||
|
||||
//// PROD
|
||||
|
||||
gulp.task("js.prod.transpiled", () => {
|
||||
return gulp
|
||||
.src("../src/js/main.js")
|
||||
@ -167,8 +162,6 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
|
||||
)
|
||||
.pipe(gulp.dest(buildFolder));
|
||||
});
|
||||
|
||||
// TODO: Tasks for te app
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -3,7 +3,6 @@
|
||||
"version": "1.0.0",
|
||||
"description": "builder",
|
||||
"private": true,
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"gulp": "gulp"
|
||||
},
|
||||
@ -41,7 +40,6 @@
|
||||
"lz-string": "^1.4.4",
|
||||
"markdown-loader": "^5.1.0",
|
||||
"node-sri": "^1.1.1",
|
||||
"obfuscator-loader": "^1.1.2",
|
||||
"phonegap-plugin-mobile-accessibility": "^1.0.5",
|
||||
"promise-polyfill": "^8.1.0",
|
||||
"query-string": "^6.8.1",
|
||||
@ -54,7 +52,6 @@
|
||||
"uglify-template-string-loader": "^1.1.0",
|
||||
"unused-files-webpack-plugin": "^3.4.0",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-bundle-analyzer": "^3.0.3",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-deep-scope-plugin": "^1.6.0",
|
||||
"webpack-plugin-replace": "^1.1.1",
|
||||
@ -76,8 +73,6 @@
|
||||
"gulp-cache": "^1.1.3",
|
||||
"gulp-cached": "^1.1.1",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-cssbeautify": "^2.0.1",
|
||||
"gulp-csslint": "^1.0.1",
|
||||
"gulp-dom": "^1.0.0",
|
||||
"gulp-flatten": "^0.4.0",
|
||||
"gulp-fluent-ffmpeg": "^2.0.0",
|
||||
@ -85,8 +80,6 @@
|
||||
"gulp-htmlmin": "^5.0.1",
|
||||
"gulp-if": "^3.0.0",
|
||||
"gulp-imagemin": "^7.1.0",
|
||||
"gulp-javascript-obfuscator": "^1.1.5",
|
||||
"gulp-jsbeautifier": "^3.0.0",
|
||||
"gulp-load-plugins": "^2.0.3",
|
||||
"gulp-phonegap-build": "^0.1.5",
|
||||
"gulp-plumber": "^1.2.1",
|
||||
@ -104,13 +97,11 @@
|
||||
"imagemin-pngquant": "^9.0.0",
|
||||
"jimp": "^0.6.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"onesky-fetch": "^0.0.7",
|
||||
"postcss-assets": "^5.0.0",
|
||||
"postcss-preset-env": "^6.5.0",
|
||||
"postcss-round-subpixels": "^1.2.0",
|
||||
"postcss-unprefix": "^2.1.3",
|
||||
"sass-unused": "^0.3.0",
|
||||
"speed-measure-webpack-plugin": "^1.3.1",
|
||||
"strip-json-comments": "^3.0.1",
|
||||
"trim": "^0.0.1",
|
||||
"webpack-stream": "^5.2.1",
|
||||
|
@ -1,11 +1,11 @@
|
||||
const packager = require("electron-packager");
|
||||
const path = require("path");
|
||||
const buildutils = require("./buildutils");
|
||||
const { getVersion } = require("./buildutils");
|
||||
const fs = require("fs");
|
||||
const fse = require("fs-extra");
|
||||
const execSync = require("child_process").execSync;
|
||||
|
||||
function gulptasksStandalone($, gulp, buildFolder) {
|
||||
function gulptasksStandalone($, gulp) {
|
||||
const electronBaseDir = path.join(__dirname, "..", "electron");
|
||||
|
||||
const tempDestDir = path.join(__dirname, "..", "tmp_standalone_files");
|
||||
@ -47,49 +47,7 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
||||
});
|
||||
|
||||
gulp.task("standalone.prepare.minifyCode", () => {
|
||||
return (
|
||||
gulp
|
||||
.src(path.join(electronBaseDir, "*.js"))
|
||||
// .pipe(
|
||||
// $.terser({
|
||||
// ecma: 6,
|
||||
// parse: {},
|
||||
// module: false,
|
||||
// toplevel: true,
|
||||
// keep_classnames: false,
|
||||
// keep_fnames: false,
|
||||
// safari10: false,
|
||||
// compress: {
|
||||
// arguments: false, // breaks
|
||||
// drop_console: false,
|
||||
// // keep_fargs: false,
|
||||
// keep_infinity: true,
|
||||
// passes: 2,
|
||||
// module: false,
|
||||
// toplevel: true,
|
||||
// unsafe_math: true,
|
||||
// unsafe_arrows: false,
|
||||
// warnings: true,
|
||||
// },
|
||||
// mangle: {
|
||||
// eval: true,
|
||||
// keep_classnames: false,
|
||||
// keep_fnames: false,
|
||||
// module: false,
|
||||
// toplevel: true,
|
||||
// safari10: false,
|
||||
// },
|
||||
// output: {
|
||||
// comments: false,
|
||||
// ascii_only: true,
|
||||
// beautify: false,
|
||||
// braces: false,
|
||||
// ecma: 6,
|
||||
// },
|
||||
// })
|
||||
// )
|
||||
.pipe(gulp.dest(tempDestBuildDir))
|
||||
);
|
||||
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
|
||||
});
|
||||
|
||||
gulp.task("standalone.prepare.copyGamefiles", () => {
|
||||
@ -122,15 +80,14 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
||||
* @param {'win32'|'linux'|'darwin'} platform
|
||||
* @param {'x64'|'ia32'} arch
|
||||
* @param {function():void} cb
|
||||
* @param {boolean=} isRelease
|
||||
*/
|
||||
function packageStandalone(platform, arch, cb, isRelease = false) {
|
||||
function packageStandalone(platform, arch, cb) {
|
||||
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
|
||||
|
||||
packager({
|
||||
dir: tempDestBuildDir,
|
||||
appCopyright: "Tobias Springer",
|
||||
appVersion: buildutils.getVersion(),
|
||||
appVersion: getVersion(),
|
||||
buildVersion: "1.0.0",
|
||||
arch,
|
||||
platform,
|
||||
@ -164,19 +121,6 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
||||
'#!/usr/bin/env bash\n./shapezio --no-sandbox "$@"\n'
|
||||
);
|
||||
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
|
||||
} else if (platform === "win32") {
|
||||
// Optional: Create a playable copy. Shouldn't be required
|
||||
// const playablePath = appPath + "_playable";
|
||||
// fse.copySync(appPath, playablePath);
|
||||
// fs.writeFileSync(path.join(playablePath, "steam_appid.txt"), "1134480");
|
||||
// fs.writeFileSync(
|
||||
// path.join(playablePath, "play.bat"),
|
||||
// "start shapezio --dev --disable-direct-composition --in-process-gpu\r\n"
|
||||
// );
|
||||
// fs.writeFileSync(
|
||||
// path.join(playablePath, "play_local.bat"),
|
||||
// "start shapezio --local --dev --disable-direct-composition --in-process-gpu\r\n"
|
||||
// );
|
||||
}
|
||||
|
||||
if (platform === "darwin") {
|
||||
@ -226,11 +170,11 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
||||
);
|
||||
}
|
||||
|
||||
gulp.task("standalone.package.prod.win64", cb => packageStandalone("win32", "x64", cb, true));
|
||||
gulp.task("standalone.package.prod.win32", cb => packageStandalone("win32", "ia32", cb, true));
|
||||
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb, true));
|
||||
gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb, true));
|
||||
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb, true));
|
||||
gulp.task("standalone.package.prod.win64", cb => packageStandalone("win32", "x64", cb));
|
||||
gulp.task("standalone.package.prod.win32", cb => packageStandalone("win32", "ia32", cb));
|
||||
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb));
|
||||
gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb));
|
||||
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb));
|
||||
|
||||
gulp.task(
|
||||
"standalone.package.prod",
|
||||
@ -240,8 +184,6 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
||||
"standalone.package.prod.win64",
|
||||
"standalone.package.prod.linux64",
|
||||
"standalone.package.prod.darwin64"
|
||||
// "standalone.package.prod.win32",
|
||||
// "standalone.package.prod.linux32",
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -5,7 +5,7 @@ const yaml = require("gulp-yaml");
|
||||
const translationsSourceDir = path.join(__dirname, "..", "translations");
|
||||
const translationsJsonDir = path.join(__dirname, "..", "src", "js", "built-temp");
|
||||
|
||||
function gulptasksTranslations($, gulp, buildFolder) {
|
||||
function gulptasksTranslations($, gulp) {
|
||||
gulp.task("translations.convertToJson", () => {
|
||||
return gulp
|
||||
.src(path.join(translationsSourceDir, "*.yaml"))
|
||||
|
@ -2,9 +2,8 @@
|
||||
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const utils = require("./buildutils");
|
||||
const { getRevision, getVersion, getAllResourceImages } = require("./buildutils");
|
||||
const lzString = require("lz-string");
|
||||
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
const CircularDependencyPlugin = require("circular-dependency-plugin");
|
||||
|
||||
module.exports = ({ watch = false, standalone = false }) => {
|
||||
@ -41,9 +40,9 @@ module.exports = ({ watch = false, standalone = false }) => {
|
||||
G_IS_BROWSER: "true",
|
||||
G_IS_STANDALONE: standalone ? "true" : "false",
|
||||
G_BUILD_TIME: "" + new Date().getTime(),
|
||||
G_BUILD_COMMIT_HASH: JSON.stringify(utils.getRevision()),
|
||||
G_BUILD_VERSION: JSON.stringify(utils.getVersion()),
|
||||
G_ALL_UI_IMAGES: JSON.stringify(utils.getAllResourceImages()),
|
||||
G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()),
|
||||
G_BUILD_VERSION: JSON.stringify(getVersion()),
|
||||
G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()),
|
||||
}),
|
||||
|
||||
new CircularDependencyPlugin({
|
||||
@ -60,7 +59,6 @@ module.exports = ({ watch = false, standalone = false }) => {
|
||||
// set the current working directory for displaying module paths
|
||||
cwd: path.join(__dirname, "..", "src", "js"),
|
||||
}),
|
||||
// new BundleAnalyzerPlugin()
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
@ -94,6 +92,16 @@ module.exports = ({ watch = false, standalone = false }) => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.worker\.js$/,
|
||||
use: {
|
||||
loader: "worker-loader",
|
||||
options: {
|
||||
fallback: false,
|
||||
inline: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.ya?ml$/,
|
||||
type: "json", // Required by Webpack v4
|
||||
|
@ -2,14 +2,12 @@
|
||||
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const utils = require("./buildutils");
|
||||
const { getRevision, getVersion, getAllResourceImages } = require("./buildutils");
|
||||
const lzString = require("lz-string");
|
||||
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const StringReplacePlugin = require("string-replace-webpack-plugin");
|
||||
// const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
|
||||
const UnusedFilesPlugin = require("unused-files-webpack-plugin").UnusedFilesWebpackPlugin;
|
||||
// const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
|
||||
|
||||
module.exports = ({
|
||||
enableAssert = false,
|
||||
@ -34,9 +32,9 @@ module.exports = ({
|
||||
G_APP_ENVIRONMENT: JSON.stringify(environment),
|
||||
G_HAVE_ASSERT: enableAssert ? "true" : "false",
|
||||
G_BUILD_TIME: "" + new Date().getTime(),
|
||||
G_BUILD_COMMIT_HASH: JSON.stringify(utils.getRevision()),
|
||||
G_BUILD_VERSION: JSON.stringify(utils.getVersion()),
|
||||
G_ALL_UI_IMAGES: JSON.stringify(utils.getAllResourceImages()),
|
||||
G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()),
|
||||
G_BUILD_VERSION: JSON.stringify(getVersion()),
|
||||
G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()),
|
||||
};
|
||||
|
||||
const minifyNames = environment === "prod";
|
||||
@ -143,9 +141,9 @@ module.exports = ({
|
||||
ecma: es6 ? 6 : 5,
|
||||
preamble:
|
||||
"/* shapez.io Codebase - Copyright 2020 Tobias Springer - " +
|
||||
utils.getVersion() +
|
||||
getVersion() +
|
||||
" @ " +
|
||||
utils.getRevision() +
|
||||
getRevision() +
|
||||
" */",
|
||||
},
|
||||
},
|
||||
@ -164,15 +162,6 @@ module.exports = ({
|
||||
cwd: path.join(__dirname, "..", "src", "js"),
|
||||
patterns: ["../src/js/**/*.js"],
|
||||
}),
|
||||
|
||||
// new webpack.SourceMapDevToolPlugin({
|
||||
// filename: "[name].map",
|
||||
// publicPath: "/v/" + utils.getRevision() + "/",
|
||||
// }),
|
||||
// new ReplaceCompressBlocks()
|
||||
// new webpack.optimize.ModuleConcatenationPlugin()
|
||||
// new WebpackDeepScopeAnalysisPlugin()
|
||||
// new BundleAnalyzerPlugin()
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
|
799
gulp/yarn.lock
@ -9,7 +9,7 @@
|
||||
"scripts": {
|
||||
"dev": "cd gulp && yarn gulp main.serveDev",
|
||||
"tslint": "cd src/js && tsc",
|
||||
"lint": "npx eslint src/js",
|
||||
"lint": "eslint src/js",
|
||||
"prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.*",
|
||||
"publishOnItchWindows": "butler push tmp_standalone_files/shapez.io-standalone-win32-x64 tobspr/shapezio:windows --userversion-file version",
|
||||
"publishOnItchLinux": "butler push tmp_standalone_files/shapez.io-standalone-linux-x64 tobspr/shapezio:linux --userversion-file version",
|
||||
@ -34,6 +34,7 @@
|
||||
"clipboard-copy": "^3.1.0",
|
||||
"colors": "^1.3.3",
|
||||
"core-js": "3",
|
||||
"crc": "^3.8.0",
|
||||
"cssnano-preset-advanced": "^4.0.7",
|
||||
"email-validator": "^2.0.4",
|
||||
"eslint": "7.1.0",
|
||||
@ -46,7 +47,6 @@
|
||||
"lz-string": "^1.4.4",
|
||||
"markdown-loader": "^4.0.0",
|
||||
"match-all": "^1.2.5",
|
||||
"obfuscator-loader": "^1.1.2",
|
||||
"phonegap-plugin-mobile-accessibility": "^1.0.5",
|
||||
"promise-polyfill": "^8.1.0",
|
||||
"query-string": "^6.8.1",
|
||||
@ -86,14 +86,12 @@
|
||||
"imagemin-pngquant": "^8.0.0",
|
||||
"jimp": "^0.6.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"onesky-fetch": "^0.0.7",
|
||||
"postcss-assets": "^5.0.0",
|
||||
"postcss-preset-env": "^6.5.0",
|
||||
"postcss-round-subpixels": "^1.2.0",
|
||||
"postcss-unprefix": "^2.1.3",
|
||||
"prettier": "^2.0.4",
|
||||
"sass-unused": "^0.3.0",
|
||||
"speed-measure-webpack-plugin": "^1.3.1",
|
||||
"strip-json-comments": "^3.0.1",
|
||||
"trim": "^0.0.1",
|
||||
"yarn": "^1.22.4"
|
||||
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1003 KiB |
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 523 KiB After Width: | Height: | Size: 479 KiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 913 KiB |
@ -256,10 +256,6 @@
|
||||
<key type="filename">sprites/buildings/rotater.png</key>
|
||||
<key type="filename">sprites/buildings/trash.png</key>
|
||||
<key type="filename">sprites/misc/wires_overlay_tile.png</key>
|
||||
<key type="filename">sprites/wires/pin_negative_accept.png</key>
|
||||
<key type="filename">sprites/wires/pin_negative_eject.png</key>
|
||||
<key type="filename">sprites/wires/pin_positive_accept.png</key>
|
||||
<key type="filename">sprites/wires/pin_positive_eject.png</key>
|
||||
<struct type="IndividualSpriteSettings">
|
||||
<key>pivotPoint</key>
|
||||
<point_f>0.5,0.5</point_f>
|
||||
@ -358,19 +354,9 @@
|
||||
<key type="filename">sprites/blueprints/belt_left.png</key>
|
||||
<key type="filename">sprites/blueprints/belt_right.png</key>
|
||||
<key type="filename">sprites/blueprints/belt_top.png</key>
|
||||
<key type="filename">sprites/blueprints/wire_crossings-merger.png</key>
|
||||
<key type="filename">sprites/blueprints/wire_crossings.png</key>
|
||||
<key type="filename">sprites/blueprints/wire_left.png</key>
|
||||
<key type="filename">sprites/blueprints/wire_right.png</key>
|
||||
<key type="filename">sprites/blueprints/wire_top.png</key>
|
||||
<key type="filename">sprites/buildings/belt_left.png</key>
|
||||
<key type="filename">sprites/buildings/belt_right.png</key>
|
||||
<key type="filename">sprites/buildings/belt_top.png</key>
|
||||
<key type="filename">sprites/buildings/wire_crossings-merger.png</key>
|
||||
<key type="filename">sprites/buildings/wire_crossings.png</key>
|
||||
<key type="filename">sprites/buildings/wire_left.png</key>
|
||||
<key type="filename">sprites/buildings/wire_right.png</key>
|
||||
<key type="filename">sprites/buildings/wire_top.png</key>
|
||||
<struct type="IndividualSpriteSettings">
|
||||
<key>pivotPoint</key>
|
||||
<point_f>0.5,0.5</point_f>
|
||||
@ -385,28 +371,6 @@
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/blueprints/advanced_processor.png</key>
|
||||
<key type="filename">sprites/blueprints/energy_generator.png</key>
|
||||
<key type="filename">sprites/blueprints/painter-double.png</key>
|
||||
<key type="filename">sprites/blueprints/trash-storage.png</key>
|
||||
<key type="filename">sprites/buildings/advanced_processor.png</key>
|
||||
<key type="filename">sprites/buildings/energy_generator.png</key>
|
||||
<key type="filename">sprites/buildings/painter-double.png</key>
|
||||
<key type="filename">sprites/misc/energy_generator_overlay.png</key>
|
||||
<struct type="IndividualSpriteSettings">
|
||||
<key>pivotPoint</key>
|
||||
<point_f>0.5,0.5</point_f>
|
||||
<key>spriteScale</key>
|
||||
<double>1</double>
|
||||
<key>scale9Enabled</key>
|
||||
<false/>
|
||||
<key>scale9Borders</key>
|
||||
<rect>96,96,192,192</rect>
|
||||
<key>scale9Paddings</key>
|
||||
<rect>96,96,192,192</rect>
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/blueprints/cutter-quad.png</key>
|
||||
<key type="filename">sprites/blueprints/painter-quad.png</key>
|
||||
<key type="filename">sprites/buildings/cutter-quad.png</key>
|
||||
@ -481,6 +445,23 @@
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/blueprints/painter-double.png</key>
|
||||
<key type="filename">sprites/blueprints/trash-storage.png</key>
|
||||
<key type="filename">sprites/buildings/painter-double.png</key>
|
||||
<struct type="IndividualSpriteSettings">
|
||||
<key>pivotPoint</key>
|
||||
<point_f>0.5,0.5</point_f>
|
||||
<key>spriteScale</key>
|
||||
<double>1</double>
|
||||
<key>scale9Enabled</key>
|
||||
<false/>
|
||||
<key>scale9Borders</key>
|
||||
<rect>96,96,192,192</rect>
|
||||
<key>scale9Paddings</key>
|
||||
<rect>96,96,192,192</rect>
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/buildings/cutter.png</key>
|
||||
<key type="filename">sprites/buildings/mixer.png</key>
|
||||
<key type="filename">sprites/buildings/painter.png</key>
|
||||
@ -568,21 +549,6 @@
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/misc/lock_direction_indicator.png</key>
|
||||
<struct type="IndividualSpriteSettings">
|
||||
<key>pivotPoint</key>
|
||||
<point_f>0.5,0.5</point_f>
|
||||
<key>spriteScale</key>
|
||||
<double>1</double>
|
||||
<key>scale9Enabled</key>
|
||||
<false/>
|
||||
<key>scale9Borders</key>
|
||||
<rect>12,12,24,24</rect>
|
||||
<key>scale9Paddings</key>
|
||||
<rect>12,12,24,24</rect>
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/misc/storage_overlay.png</key>
|
||||
<struct type="IndividualSpriteSettings">
|
||||
<key>pivotPoint</key>
|
||||
@ -598,27 +564,6 @@
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/wires/battery_empty.png</key>
|
||||
<key type="filename">sprites/wires/battery_full.png</key>
|
||||
<key type="filename">sprites/wires/battery_low.png</key>
|
||||
<key type="filename">sprites/wires/battery_medium.png</key>
|
||||
<key type="filename">sprites/wires/negative_energy.png</key>
|
||||
<key type="filename">sprites/wires/positive_energy.png</key>
|
||||
<key type="filename">sprites/wires/waste_piled.png</key>
|
||||
<struct type="IndividualSpriteSettings">
|
||||
<key>pivotPoint</key>
|
||||
<point_f>0.5,0.5</point_f>
|
||||
<key>spriteScale</key>
|
||||
<double>1</double>
|
||||
<key>scale9Enabled</key>
|
||||
<false/>
|
||||
<key>scale9Borders</key>
|
||||
<rect>16,16,32,32</rect>
|
||||
<key>scale9Paddings</key>
|
||||
<rect>16,16,32,32</rect>
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
</map>
|
||||
<key>fileList</key>
|
||||
<array>
|
||||
|
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.1 KiB |
@ -318,7 +318,7 @@ input {
|
||||
|
||||
canvas {
|
||||
pointer-events: all;
|
||||
image-rendering: auto;
|
||||
image-rendering: pixelated;
|
||||
// &.smoothed {
|
||||
// }
|
||||
// &.unsmoothed {
|
||||
|
@ -1,5 +1,4 @@
|
||||
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt,
|
||||
energy_generator, wire, advanced_processor, wire_crossings;
|
||||
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt;
|
||||
|
||||
@each $building in $buildings {
|
||||
[data-icon="building_icons/#{$building}.png"] {
|
||||
|
@ -14,13 +14,9 @@ import { AdProviderInterface } from "./platform/ad_provider";
|
||||
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
||||
import { AnalyticsInterface } from "./platform/analytics";
|
||||
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
||||
import { NoGameAnalytics } from "./platform/browser/no_game_analytics";
|
||||
import { SoundImplBrowser } from "./platform/browser/sound";
|
||||
import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper";
|
||||
import { PlatformWrapperImplElectron } from "./platform/electron/wrapper";
|
||||
import { GameAnalyticsInterface } from "./platform/game_analytics";
|
||||
import { SoundInterface } from "./platform/sound";
|
||||
import { StorageInterface } from "./platform/storage";
|
||||
import { PlatformWrapperInterface } from "./platform/wrapper";
|
||||
import { ApplicationSettings } from "./profile/application_settings";
|
||||
import { SavegameManager } from "./savegame/savegame_manager";
|
||||
@ -34,6 +30,12 @@ import { PreloadState } from "./states/preload";
|
||||
import { SettingsState } from "./states/settings";
|
||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||
|
||||
/**
|
||||
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
||||
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
|
||||
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
|
||||
*/
|
||||
|
||||
const logger = createLogger("application");
|
||||
|
||||
// Set the name of the hidden property and the change event for visibility
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Signal } from "./signal";
|
||||
|
||||
import BackgroundAnimationFrameEmitterWorker from "worker-loader?inline=true&fallback=false!../webworkers/background_animation_frame_emittter.worker";
|
||||
// @ts-ignore
|
||||
import BackgroundAnimationFrameEmitterWorker from "../webworkers/background_animation_frame_emittter.worker";
|
||||
|
||||
import { createLogger } from "./logging";
|
||||
const logger = createLogger("animation_frame");
|
||||
|
@ -1,5 +1,8 @@
|
||||
import CompressionWorker from "worker-loader?inline=true&fallback=false!../webworkers/compression.worker";
|
||||
// @ts-ignore
|
||||
import CompressionWorker from "../webworkers/compression.worker";
|
||||
|
||||
import { createLogger } from "./logging";
|
||||
import { round2Digits } from "./utils";
|
||||
|
||||
const logger = createLogger("async_compression");
|
||||
|
||||
@ -50,7 +53,15 @@ class AsynCompression {
|
||||
}
|
||||
|
||||
const duration = performance.now() - jobData.startTime;
|
||||
// log(this, "Got response from worker within", duration.toFixed(2), "ms");
|
||||
logger.log(
|
||||
"Got job",
|
||||
jobId,
|
||||
"response within",
|
||||
round2Digits(duration),
|
||||
"ms: ",
|
||||
result.length,
|
||||
"bytes"
|
||||
);
|
||||
const resolver = jobData.resolver;
|
||||
delete this.currentJobs[jobId];
|
||||
resolver(result);
|
||||
@ -70,12 +81,13 @@ class AsynCompression {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compresses file
|
||||
* @param {string} text
|
||||
* Compresses any object
|
||||
* @param {any} obj
|
||||
*/
|
||||
compressFileAsync(text) {
|
||||
return this.internalQueueJob("compressFile", {
|
||||
text,
|
||||
compressObjectAsync(obj) {
|
||||
logger.log("Compressing object async (optimized)");
|
||||
return this.internalQueueJob("compressObject", {
|
||||
obj,
|
||||
compressionPrefix,
|
||||
});
|
||||
}
|
||||
@ -98,6 +110,8 @@ class AsynCompression {
|
||||
resolver: resolve,
|
||||
startTime: performance.now(),
|
||||
};
|
||||
|
||||
logger.log("Posting job", job, "/", jobId);
|
||||
this.worker.postMessage({ jobId, job, data });
|
||||
});
|
||||
}
|
||||
|
@ -1,20 +1,38 @@
|
||||
/**
|
||||
* @typedef {{ w: number, h: number }} Size
|
||||
* @typedef {{ x: number, y: number }} Position
|
||||
* @typedef {{
|
||||
* frame: { x: number, y: number, w: number, h: number },
|
||||
* rotated: false,
|
||||
* spriteSourceSize: { x: number, y: number, w: number, h: number },
|
||||
* sourceSize: { w: number, h: number},
|
||||
* trimmed: true
|
||||
* frame: Position & Size,
|
||||
* rotated: boolean,
|
||||
* spriteSourceSize: Position & Size,
|
||||
* sourceSize: Size,
|
||||
* trimmed: boolean
|
||||
* }} SpriteDefinition
|
||||
*
|
||||
* @typedef {{
|
||||
* app: string,
|
||||
* version: string,
|
||||
* image: string,
|
||||
* format: string,
|
||||
* size: Size,
|
||||
* scale: string,
|
||||
* smartupdate: string
|
||||
* }} AtlasMeta
|
||||
*
|
||||
* @typedef {{
|
||||
* frames: Object.<string, SpriteDefinition>,
|
||||
* meta: AtlasMeta
|
||||
* }} SourceData
|
||||
*/
|
||||
|
||||
export class AtlasDefinition {
|
||||
constructor(sourceData) {
|
||||
this.sourceFileName = sourceData.meta.image;
|
||||
this.meta = sourceData.meta;
|
||||
|
||||
/** @type {Object.<string, SpriteDefinition>} */
|
||||
this.sourceData = sourceData.frames;
|
||||
/**
|
||||
* @param {SourceData} sourceData
|
||||
*/
|
||||
constructor({ frames, meta }) {
|
||||
this.meta = meta;
|
||||
this.sourceData = frames;
|
||||
this.sourceFileName = meta.image;
|
||||
}
|
||||
|
||||
getFullSourcePath() {
|
||||
@ -22,6 +40,7 @@ export class AtlasDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {AtlasDefinition[]} **/
|
||||
export const atlasFiles = require
|
||||
// @ts-ignore
|
||||
.context("../../../res_built/atlas/", false, /.*\.json/i)
|
||||
|
@ -115,7 +115,6 @@ export class BackgroundResourcesLoader {
|
||||
})
|
||||
.then(() => {
|
||||
logger.log("⏰ Finish load: bare game");
|
||||
Loader.createAtlasLinks();
|
||||
this.bareGameReady = true;
|
||||
initBuildingCodesAfterResourcesLoaded();
|
||||
this.signalBareGameLoaded.dispatch();
|
||||
|
@ -95,15 +95,18 @@ export class BufferMaintainer {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} subKey
|
||||
* @param {function(HTMLCanvasElement, CanvasRenderingContext2D, number, number, number, object?) : void} redrawMethod
|
||||
* @param {object=} additionalParams
|
||||
* @param {object} param0
|
||||
* @param {string} param0.key
|
||||
* @param {string} param0.subKey
|
||||
* @param {number} param0.w
|
||||
* @param {number} param0.h
|
||||
* @param {number} param0.dpi
|
||||
* @param {function(HTMLCanvasElement, CanvasRenderingContext2D, number, number, number, object?) : void} param0.redrawMethod
|
||||
* @param {object=} param0.additionalParams
|
||||
* @returns {HTMLCanvasElement}
|
||||
*
|
||||
*/
|
||||
getForKey(key, subKey, w, h, dpi, redrawMethod, additionalParams) {
|
||||
getForKey({ key, subKey, w, h, dpi, redrawMethod, additionalParams }) {
|
||||
// First, create parent key
|
||||
let parent = this.cache.get(key);
|
||||
if (!parent) {
|
||||
|
@ -34,6 +34,7 @@ export const globalConfig = {
|
||||
assetsDpi: 192 / 32,
|
||||
assetsSharpness: 1.2,
|
||||
shapesSharpness: 1.4,
|
||||
mapChunkSharpness: 1.0,
|
||||
|
||||
// Production analytics
|
||||
statisticsGraphDpi: 2.5,
|
||||
@ -45,18 +46,14 @@ export const globalConfig = {
|
||||
|
||||
// Map
|
||||
mapChunkSize: 16,
|
||||
mapChunkPrerenderMinZoom: -1,
|
||||
mapChunkOverviewMinZoom: 0.7,
|
||||
mapChunkOverviewMinZoom: 0.9,
|
||||
|
||||
// Belt speeds
|
||||
// NOTICE: Update webpack.production.config too!
|
||||
beltSpeedItemsPerSecond: 2,
|
||||
minerSpeedItemsPerSecond: 0, // COMPUTED
|
||||
|
||||
beltItemSpacingByLayer: {
|
||||
regular: 0.63,
|
||||
wires: 0.4,
|
||||
},
|
||||
itemSpacingOnBelts: 0.63,
|
||||
|
||||
wiresSpeedItemsPerSecond: 6,
|
||||
|
||||
@ -116,14 +113,12 @@ globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5
|
||||
// Dynamic calculations
|
||||
if (globalConfig.debug.disableMapOverview) {
|
||||
globalConfig.mapChunkOverviewMinZoom = 0;
|
||||
globalConfig.mapChunkPrerenderMinZoom = 0;
|
||||
}
|
||||
|
||||
// Stuff for making the trailer
|
||||
if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
|
||||
globalConfig.debug.framePausesBetweenTicks = 32;
|
||||
// globalConfig.mapChunkOverviewMinZoom = 0.0;
|
||||
// globalConfig.mapChunkPrerenderMinZoom = globalConfig.mapChunkOverviewMinZoom;
|
||||
// globalConfig.debug.instantBelts = true;
|
||||
// globalConfig.debug.instantProcessors = true;
|
||||
// globalConfig.debug.instantMiners = true;
|
||||
|
@ -15,8 +15,8 @@ export function getDeviceDPI() {
|
||||
* @returns {number} Smoothed dpi
|
||||
*/
|
||||
export function smoothenDpi(dpi) {
|
||||
if (dpi < 0.05) {
|
||||
return 0.05;
|
||||
if (dpi < 0.02) {
|
||||
return 0.02;
|
||||
} else if (dpi < 0.1) {
|
||||
return round2Digits(dpi);
|
||||
} else if (dpi < 1) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Rectangle } from "./rectangle";
|
||||
import { globalConfig } from "./config";
|
||||
|
||||
/* typehints:start */
|
||||
import { GameRoot } from "../game/root";
|
||||
/* typehints:end */
|
||||
/**
|
||||
* @typedef {import("../game/root").GameRoot} GameRoot
|
||||
* @typedef {import("./rectangle").Rectangle} Rectangle
|
||||
*/
|
||||
|
||||
export class DrawParameters {
|
||||
constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {
|
||||
|
@ -1,18 +1,13 @@
|
||||
/* typehints:start */
|
||||
import { AtlasSprite } from "./sprites";
|
||||
import { DrawParameters } from "./draw_parameters";
|
||||
/* typehints:end */
|
||||
|
||||
import { Vector } from "./vector";
|
||||
import { Rectangle } from "./rectangle";
|
||||
import { createLogger } from "./logging";
|
||||
|
||||
const logger = createLogger("draw_utils");
|
||||
/**
|
||||
* @typedef {import("./sprites").AtlasSprite} AtlasSprite
|
||||
* @typedef {import("./draw_parameters").DrawParameters} DrawParameters
|
||||
*/
|
||||
|
||||
export function initDrawUtils() {
|
||||
CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) {
|
||||
this.beginPath();
|
||||
|
||||
if (r < 0.05) {
|
||||
this.beginPath();
|
||||
this.rect(x, y, w, h);
|
||||
return;
|
||||
}
|
||||
@ -20,25 +15,26 @@ export function initDrawUtils() {
|
||||
if (w < 2 * r) {
|
||||
r = w / 2;
|
||||
}
|
||||
|
||||
if (h < 2 * r) {
|
||||
r = h / 2;
|
||||
}
|
||||
this.beginPath();
|
||||
|
||||
this.moveTo(x + r, y);
|
||||
this.arcTo(x + w, y, x + w, y + h, r);
|
||||
this.arcTo(x + w, y + h, x, y + h, r);
|
||||
this.arcTo(x, y + h, x, y, r);
|
||||
this.arcTo(x, y, x + w, y, r);
|
||||
// this.closePath();
|
||||
};
|
||||
|
||||
CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) {
|
||||
this.beginPath();
|
||||
|
||||
if (r < 0.05) {
|
||||
this.beginPath();
|
||||
this.rect(x, y, 1, 1);
|
||||
return;
|
||||
}
|
||||
this.beginPath();
|
||||
|
||||
this.arc(x, y, r, 0, 2.0 * Math.PI);
|
||||
};
|
||||
}
|
||||
@ -62,259 +58,3 @@ export function drawRotatedSprite({ parameters, sprite, x, y, angle, size, offse
|
||||
parameters.context.rotate(-angle);
|
||||
parameters.context.translate(-x, -y);
|
||||
}
|
||||
|
||||
export function drawLineFast(context, { x1, x2, y1, y2, color = null, lineSize = 1 }) {
|
||||
const dX = x2 - x1;
|
||||
const dY = y2 - y1;
|
||||
|
||||
const angle = Math.atan2(dY, dX) + 0.0 * Math.PI;
|
||||
const len = Math.hypot(dX, dY);
|
||||
|
||||
context.translate(x1, y1);
|
||||
context.rotate(angle);
|
||||
|
||||
if (color) {
|
||||
context.fillStyle = color;
|
||||
}
|
||||
|
||||
context.fillRect(0, -lineSize / 2, len, lineSize);
|
||||
|
||||
context.rotate(-angle);
|
||||
context.translate(-x1, -y1);
|
||||
}
|
||||
|
||||
const INSIDE = 0;
|
||||
const LEFT = 1;
|
||||
const RIGHT = 2;
|
||||
const BOTTOM = 4;
|
||||
const TOP = 8;
|
||||
|
||||
// https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
|
||||
|
||||
function computeOutCode(x, y, xmin, xmax, ymin, ymax) {
|
||||
let code = INSIDE;
|
||||
|
||||
if (x < xmin)
|
||||
// to the left of clip window
|
||||
code |= LEFT;
|
||||
else if (x > xmax)
|
||||
// to the right of clip window
|
||||
code |= RIGHT;
|
||||
if (y < ymin)
|
||||
// below the clip window
|
||||
code |= BOTTOM;
|
||||
else if (y > ymax)
|
||||
// above the clip window
|
||||
code |= TOP;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Cohen–Sutherland clipping algorithm clips a line from
|
||||
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
|
||||
// diagonal from (xmin, ymin) to (xmax, ymax).
|
||||
/**
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} context
|
||||
*/
|
||||
export function drawLineFastClipped(context, rect, { x0, y0, x1, y1, color = null, lineSize = 1 }) {
|
||||
const xmin = rect.x;
|
||||
const ymin = rect.y;
|
||||
const xmax = rect.right();
|
||||
const ymax = rect.bottom();
|
||||
|
||||
// compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
|
||||
let outcode0 = computeOutCode(x0, y0, xmin, xmax, ymin, ymax);
|
||||
let outcode1 = computeOutCode(x1, y1, xmin, xmax, ymin, ymax);
|
||||
let accept = false;
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
if (!(outcode0 | outcode1)) {
|
||||
// bitwise OR is 0: both points inside window; trivially accept and exit loop
|
||||
accept = true;
|
||||
break;
|
||||
} else if (outcode0 & outcode1) {
|
||||
// bitwise AND is not 0: both points share an outside zone (LEFT, RIGHT, TOP,
|
||||
// or BOTTOM), so both must be outside window; exit loop (accept is false)
|
||||
break;
|
||||
} else {
|
||||
// failed both tests, so calculate the line segment to clip
|
||||
// from an outside point to an intersection with clip edge
|
||||
let x, y;
|
||||
|
||||
// At least one endpoint is outside the clip rectangle; pick it.
|
||||
let outcodeOut = outcode0 ? outcode0 : outcode1;
|
||||
|
||||
// Now find the intersection point;
|
||||
// use formulas:
|
||||
// slope = (y1 - y0) / (x1 - x0)
|
||||
// x = x0 + (1 / slope) * (ym - y0), where ym is ymin or ymax
|
||||
// y = y0 + slope * (xm - x0), where xm is xmin or xmax
|
||||
// No need to worry about divide-by-zero because, in each case, the
|
||||
// outcode bit being tested guarantees the denominator is non-zero
|
||||
if (outcodeOut & TOP) {
|
||||
// point is above the clip window
|
||||
x = x0 + ((x1 - x0) * (ymax - y0)) / (y1 - y0);
|
||||
y = ymax;
|
||||
} else if (outcodeOut & BOTTOM) {
|
||||
// point is below the clip window
|
||||
x = x0 + ((x1 - x0) * (ymin - y0)) / (y1 - y0);
|
||||
y = ymin;
|
||||
} else if (outcodeOut & RIGHT) {
|
||||
// point is to the right of clip window
|
||||
y = y0 + ((y1 - y0) * (xmax - x0)) / (x1 - x0);
|
||||
x = xmax;
|
||||
} else if (outcodeOut & LEFT) {
|
||||
// point is to the left of clip window
|
||||
y = y0 + ((y1 - y0) * (xmin - x0)) / (x1 - x0);
|
||||
x = xmin;
|
||||
}
|
||||
|
||||
// Now we move outside point to intersection point to clip
|
||||
// and get ready for next pass.
|
||||
if (outcodeOut == outcode0) {
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
outcode0 = computeOutCode(x0, y0, xmin, xmax, ymin, ymax);
|
||||
} else {
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
outcode1 = computeOutCode(x1, y1, xmin, xmax, ymin, ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (accept) {
|
||||
// Following functions are left for implementation by user based on
|
||||
// their platform (OpenGL/graphics.h etc.)
|
||||
// DrawRectangle(xmin, ymin, xmax, ymax);
|
||||
// LineSegment(x0, y0, x1, y1);
|
||||
drawLineFast(context, {
|
||||
x1: x0,
|
||||
y1: y0,
|
||||
x2: x1,
|
||||
y2: y1,
|
||||
color,
|
||||
lineSize,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an HSL color value to RGB. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*
|
||||
* @param {number} h The hue
|
||||
* @param {number} s The saturation
|
||||
* @param {number} l The lightness
|
||||
* @return {Array} The RGB representation
|
||||
*/
|
||||
export function hslToRgb(h, s, l) {
|
||||
let r;
|
||||
let g;
|
||||
let b;
|
||||
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
// tslint:disable-next-line:no-shadowed-variable
|
||||
const hue2rgb = function (p, q, t) {
|
||||
if (t < 0) {
|
||||
t += 1;
|
||||
}
|
||||
if (t > 1) {
|
||||
t -= 1;
|
||||
}
|
||||
if (t < 1 / 6) {
|
||||
return p + (q - p) * 6 * t;
|
||||
}
|
||||
if (t < 1 / 2) {
|
||||
return q;
|
||||
}
|
||||
if (t < 2 / 3) {
|
||||
return p + (q - p) * (2 / 3 - t) * 6;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
let p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
|
||||
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||||
}
|
||||
|
||||
export function wrapText(context, text, x, y, maxWidth, lineHeight, stroke = false) {
|
||||
var words = text.split(" ");
|
||||
var line = "";
|
||||
|
||||
for (var n = 0; n < words.length; n++) {
|
||||
var testLine = line + words[n] + " ";
|
||||
var metrics = context.measureText(testLine);
|
||||
var testWidth = metrics.width;
|
||||
if (testWidth > maxWidth && n > 0) {
|
||||
if (stroke) {
|
||||
context.strokeText(line, x, y);
|
||||
} else {
|
||||
context.fillText(line, x, y);
|
||||
}
|
||||
line = words[n] + " ";
|
||||
y += lineHeight;
|
||||
} else {
|
||||
line = testLine;
|
||||
}
|
||||
}
|
||||
|
||||
if (stroke) {
|
||||
context.strokeText(line, x, y);
|
||||
} else {
|
||||
context.fillText(line, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rotated trapez, used for spotlight culling
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} w
|
||||
* @param {number} h
|
||||
* @param {number} leftHeight
|
||||
* @param {number} angle
|
||||
*/
|
||||
export function rotateTrapezRightFaced(x, y, w, h, leftHeight, angle) {
|
||||
const halfY = y + h / 2;
|
||||
const points = [
|
||||
new Vector(x, halfY - leftHeight / 2),
|
||||
new Vector(x + w, y),
|
||||
new Vector(x, halfY + leftHeight / 2),
|
||||
new Vector(x + w, y + h),
|
||||
];
|
||||
|
||||
return Rectangle.getAroundPointsRotated(points, angle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts values from 0 .. 255 to values like 07, 7f, 5d etc
|
||||
* @param {number} value
|
||||
* @returns {string}
|
||||
*/
|
||||
export function mapClampedColorValueToHex(value) {
|
||||
const hex = "0123456789abcdef";
|
||||
return hex[Math.floor(value / 16)] + hex[value % 16];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts rgb to a hex string
|
||||
* @param {number} r
|
||||
* @param {number} g
|
||||
* @param {number} b
|
||||
* @returns {string}
|
||||
*/
|
||||
export function rgbToHex(r, g, b) {
|
||||
return mapClampedColorValueToHex(r) + mapClampedColorValueToHex(g) + mapClampedColorValueToHex(b);
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { SingletonFactory } from "./singleton_factory";
|
||||
import { Factory } from "./factory";
|
||||
|
||||
/* typehints:start */
|
||||
import { BaseGameSpeed } from "../game/time/base_game_speed";
|
||||
import { Component } from "../game/component";
|
||||
import { BaseItem } from "../game/base_item";
|
||||
import { MetaBuilding } from "../game/meta_building";
|
||||
/* typehints:end */
|
||||
/**
|
||||
* @typedef {import("../game/time/base_game_speed").BaseGameSpeed} BaseGameSpeed
|
||||
* @typedef {import("../game/component").Component} Component
|
||||
* @typedef {import("../game/base_item").BaseItem} BaseItem
|
||||
* @typedef {import("../game/meta_building").MetaBuilding} MetaBuilding
|
||||
|
||||
|
||||
// These factories are here to remove circular dependencies
|
||||
|
||||
/** @type {SingletonFactoryTemplate<MetaBuilding>} */
|
||||
export let gMetaBuildingRegistry = new SingletonFactory();
|
||||
|
||||
/** @type {Object.<string, Array<typeof MetaBuilding>>} */
|
||||
/** @type {Object.<string, Array<Class<MetaBuilding>>>} */
|
||||
export let gBuildingsByCategory = null;
|
||||
|
||||
/** @type {FactoryTemplate<Component>} */
|
||||
@ -28,7 +28,7 @@ export let gItemRegistry = new Factory("item");
|
||||
// Helpers
|
||||
|
||||
/**
|
||||
* @param {Object.<string, Array<typeof MetaBuilding>>} buildings
|
||||
* @param {Object.<string, Array<Class<MetaBuilding>>>} buildings
|
||||
*/
|
||||
export function initBuildingsByCategory(buildings) {
|
||||
gBuildingsByCategory = buildings;
|
||||
|
@ -1,20 +1,19 @@
|
||||
/* typehints:start */
|
||||
import { Application } from "../application";
|
||||
/* typehints:end */
|
||||
|
||||
import { AtlasDefinition } from "./atlas_definitions";
|
||||
import { makeOffscreenBuffer } from "./buffer_utils";
|
||||
import { AtlasSprite, BaseSprite, RegularSprite, SpriteAtlasLink } from "./sprites";
|
||||
import { cachebust } from "./cachebust";
|
||||
import { createLogger } from "./logging";
|
||||
|
||||
/**
|
||||
* @typedef {import("../application").Application} Application
|
||||
* @typedef {import("./atlas_definitions").AtlasDefinition} AtlasDefinition;
|
||||
*/
|
||||
|
||||
const logger = createLogger("loader");
|
||||
|
||||
const missingSpriteIds = {};
|
||||
|
||||
class LoaderImpl {
|
||||
constructor() {
|
||||
/** @type {Application} */
|
||||
this.app = null;
|
||||
|
||||
/** @type {Map<string, BaseSprite>} */
|
||||
@ -23,6 +22,9 @@ class LoaderImpl {
|
||||
this.rawImages = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Application} app
|
||||
*/
|
||||
linkAppAfterBoot(app) {
|
||||
this.app = app;
|
||||
this.makeSpriteNotFoundCanvas();
|
||||
@ -58,7 +60,7 @@ class LoaderImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retursn a regular sprite from the cache
|
||||
* Returns a regular sprite from the cache
|
||||
* @param {string} key
|
||||
* @returns {RegularSprite}
|
||||
*/
|
||||
@ -155,44 +157,34 @@ class LoaderImpl {
|
||||
* @param {AtlasDefinition} atlas
|
||||
* @param {HTMLImageElement} loadedImage
|
||||
*/
|
||||
internalParseAtlas(atlas, loadedImage) {
|
||||
internalParseAtlas({ meta: { scale }, sourceData }, loadedImage) {
|
||||
this.rawImages.push(loadedImage);
|
||||
|
||||
for (const spriteKey in atlas.sourceData) {
|
||||
const spriteData = atlas.sourceData[spriteKey];
|
||||
for (const spriteName in sourceData) {
|
||||
const { frame, sourceSize, spriteSourceSize } = sourceData[spriteName];
|
||||
|
||||
let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteKey));
|
||||
let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteName));
|
||||
|
||||
if (!sprite) {
|
||||
sprite = new AtlasSprite({
|
||||
spriteName: spriteKey,
|
||||
});
|
||||
this.sprites.set(spriteKey, sprite);
|
||||
sprite = new AtlasSprite(spriteName);
|
||||
this.sprites.set(spriteName, sprite);
|
||||
}
|
||||
|
||||
const link = new SpriteAtlasLink({
|
||||
packedX: spriteData.frame.x,
|
||||
packedY: spriteData.frame.y,
|
||||
packedW: spriteData.frame.w,
|
||||
packedH: spriteData.frame.h,
|
||||
packOffsetX: spriteData.spriteSourceSize.x,
|
||||
packOffsetY: spriteData.spriteSourceSize.y,
|
||||
packedX: frame.x,
|
||||
packedY: frame.y,
|
||||
packedW: frame.w,
|
||||
packedH: frame.h,
|
||||
packOffsetX: spriteSourceSize.x,
|
||||
packOffsetY: spriteSourceSize.y,
|
||||
atlas: loadedImage,
|
||||
w: spriteData.sourceSize.w,
|
||||
h: spriteData.sourceSize.h,
|
||||
w: sourceSize.w,
|
||||
h: sourceSize.h,
|
||||
});
|
||||
sprite.linksByResolution[atlas.meta.scale] = link;
|
||||
sprite.linksByResolution[scale] = link;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the links for the sprites after the atlas has been loaded. Used so we
|
||||
* don't have to store duplicate sprites.
|
||||
*/
|
||||
createAtlasLinks() {
|
||||
// NOT USED
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the canvas which shows the question mark, shown when a sprite was not found
|
||||
*/
|
||||
@ -216,14 +208,9 @@ class LoaderImpl {
|
||||
// @ts-ignore
|
||||
canvas.src = "not-found";
|
||||
|
||||
const resolutions = ["0.1", "0.25", "0.5", "0.75", "1"];
|
||||
const sprite = new AtlasSprite({
|
||||
spriteName: "not-found",
|
||||
});
|
||||
|
||||
for (let i = 0; i < resolutions.length; ++i) {
|
||||
const res = resolutions[i];
|
||||
const link = new SpriteAtlasLink({
|
||||
const sprite = new AtlasSprite("not-found");
|
||||
["0.1", "0.25", "0.5", "0.75", "1"].forEach(resolution => {
|
||||
sprite.linksByResolution[resolution] = new SpriteAtlasLink({
|
||||
packedX: 0,
|
||||
packedY: 0,
|
||||
w: dims,
|
||||
@ -234,8 +221,8 @@ class LoaderImpl {
|
||||
packedH: dims,
|
||||
atlas: canvas,
|
||||
});
|
||||
sprite.linksByResolution[res] = link;
|
||||
}
|
||||
});
|
||||
|
||||
this.spriteNotFoundSprite = sprite;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { Application } from "../application";
|
||||
/* typehints:end */
|
||||
|
||||
import { sha1 } from "./sensitive_utils.encrypt";
|
||||
import { sha1, CRC_PREFIX, computeCrc } from "./sensitive_utils.encrypt";
|
||||
import { createLogger } from "./logging";
|
||||
import { FILE_NOT_FOUND } from "../platform/storage";
|
||||
import { accessNestedPropertyReverse } from "./utils";
|
||||
@ -84,7 +84,7 @@ export class ReadWriteProxy {
|
||||
*/
|
||||
static serializeObject(obj) {
|
||||
const jsonString = JSON.stringify(compressObject(obj));
|
||||
const checksum = sha1(jsonString + salt);
|
||||
const checksum = computeCrc(jsonString + salt);
|
||||
return compressionPrefix + compressX64(checksum + jsonString);
|
||||
}
|
||||
|
||||
@ -106,7 +106,11 @@ export class ReadWriteProxy {
|
||||
// Compare stored checksum with actual checksum
|
||||
const checksum = decompressed.substring(0, 40);
|
||||
const jsonString = decompressed.substr(40);
|
||||
const desiredChecksum = sha1(jsonString + salt);
|
||||
|
||||
const desiredChecksum = checksum.startsWith(CRC_PREFIX)
|
||||
? computeCrc(jsonString + salt)
|
||||
: sha1(jsonString + salt);
|
||||
|
||||
if (desiredChecksum !== checksum) {
|
||||
// Checksum mismatch
|
||||
throw new Error("bad-content / checksum-mismatch");
|
||||
@ -119,7 +123,7 @@ export class ReadWriteProxy {
|
||||
|
||||
/**
|
||||
* Writes the data asychronously, fails if verify() fails
|
||||
* @returns {Promise<string>}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
writeAsync() {
|
||||
const verifyResult = this.internalVerifyEntry(this.currentData);
|
||||
@ -128,35 +132,14 @@ export class ReadWriteProxy {
|
||||
logger.error("Tried to write invalid data to", this.filename, "reason:", verifyResult.reason);
|
||||
return Promise.reject(verifyResult.reason);
|
||||
}
|
||||
const jsonString = JSON.stringify(compressObject(this.currentData));
|
||||
|
||||
// if (!this.app.pageVisible || this.app.unloaded) {
|
||||
// logger.log("Saving file sync because in unload handler");
|
||||
// const checksum = sha1(jsonString + salt);
|
||||
// let compressed = compressionPrefix + compressX64(checksum + jsonString);
|
||||
// if (G_IS_DEV && IS_DEBUG) {
|
||||
// compressed = jsonString;
|
||||
// }
|
||||
|
||||
// if (!this.app.storage.writeFileSyncIfSupported(this.filename, compressed)) {
|
||||
// return Promise.reject("Failed to write " + this.filename + " sync!");
|
||||
// } else {
|
||||
// logger.log("📄 Wrote (sync!)", this.filename);
|
||||
// return Promise.resolve(compressed);
|
||||
// }
|
||||
// }
|
||||
|
||||
return asyncCompressor
|
||||
.compressFileAsync(jsonString)
|
||||
.compressObjectAsync(this.currentData)
|
||||
.then(compressed => {
|
||||
if (G_IS_DEV && IS_DEBUG) {
|
||||
compressed = jsonString;
|
||||
}
|
||||
return this.app.storage.writeFileAsync(this.filename, compressed);
|
||||
})
|
||||
.then(() => {
|
||||
logger.log("📄 Wrote", this.filename);
|
||||
return jsonString;
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error("Failed to write", this.filename, ":", err);
|
||||
@ -205,10 +188,16 @@ export class ReadWriteProxy {
|
||||
// Compare stored checksum with actual checksum
|
||||
const checksum = decompressed.substring(0, 40);
|
||||
const jsonString = decompressed.substr(40);
|
||||
const desiredChecksum = sha1(jsonString + salt);
|
||||
|
||||
const desiredChecksum = checksum.startsWith(CRC_PREFIX)
|
||||
? computeCrc(jsonString + salt)
|
||||
: sha1(jsonString + salt);
|
||||
|
||||
if (desiredChecksum !== checksum) {
|
||||
// Checksum mismatch
|
||||
return Promise.reject("bad-content / checksum-mismatch");
|
||||
return Promise.reject(
|
||||
"bad-content / checksum-mismatch: " + desiredChecksum + " vs " + checksum
|
||||
);
|
||||
}
|
||||
return jsonString;
|
||||
} else {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { globalConfig } from "./config";
|
||||
import { clamp, epsilonCompare, round2Digits } from "./utils";
|
||||
import { epsilonCompare, round2Digits } from "./utils";
|
||||
import { Vector } from "./vector";
|
||||
|
||||
export class Rectangle {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createHash } from "rusha";
|
||||
|
||||
import crc32 from "crc/crc32";
|
||||
import { decompressX64 } from "./lzstring";
|
||||
|
||||
export function sha1(str) {
|
||||
@ -10,3 +10,14 @@ export function sha1(str) {
|
||||
export function getNameOfProvider() {
|
||||
return window[decompressX64("DYewxghgLgliB2Q")][decompressX64("BYewzgLgdghgtgUyA")];
|
||||
}
|
||||
|
||||
// Distinguish legacy crc prefixes
|
||||
export const CRC_PREFIX = "crc32".padEnd(32, "-");
|
||||
|
||||
/**
|
||||
* Computes the crc for a given string
|
||||
* @param {string} str
|
||||
*/
|
||||
export function computeCrc(str) {
|
||||
return CRC_PREFIX + crc32(str).toString(16).padStart(8, "0");
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DrawParameters } from "./draw_parameters";
|
||||
import { Rectangle } from "./rectangle";
|
||||
import { epsilonCompare, round3Digits } from "./utils";
|
||||
import { round3Digits } from "./utils";
|
||||
|
||||
const floorSpriteCoordinates = false;
|
||||
|
||||
@ -63,10 +63,9 @@ export class SpriteAtlasLink {
|
||||
export class AtlasSprite extends BaseSprite {
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
* @param {string} param0.spriteName
|
||||
* @param {string} spriteName
|
||||
*/
|
||||
constructor({ spriteName = "sprite" }) {
|
||||
constructor(spriteName = "sprite") {
|
||||
super();
|
||||
/** @type {Object.<string, SpriteAtlasLink>} */
|
||||
this.linksByResolution = {};
|
||||
@ -197,8 +196,6 @@ export class AtlasSprite extends BaseSprite {
|
||||
destH = intersection.h;
|
||||
}
|
||||
|
||||
// assert(epsilonCompare(scaleW, scaleH), "Sprite should be square for cached rendering");
|
||||
|
||||
if (floorSpriteCoordinates) {
|
||||
parameters.context.drawImage(
|
||||
link.atlas,
|
||||
|
@ -1,46 +1,7 @@
|
||||
import { globalConfig, IS_DEBUG } from "./config";
|
||||
import { Vector } from "./vector";
|
||||
import { T } from "../translations";
|
||||
|
||||
// Constants
|
||||
export const TOP = new Vector(0, -1);
|
||||
export const RIGHT = new Vector(1, 0);
|
||||
export const BOTTOM = new Vector(0, 1);
|
||||
export const LEFT = new Vector(-1, 0);
|
||||
export const ALL_DIRECTIONS = [TOP, RIGHT, BOTTOM, LEFT];
|
||||
|
||||
const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"];
|
||||
|
||||
/**
|
||||
* Returns the build id
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getBuildId() {
|
||||
if (G_IS_DEV && IS_DEBUG) {
|
||||
return "local-dev";
|
||||
} else if (G_IS_DEV) {
|
||||
return "dev-" + getPlatformName() + "-" + G_BUILD_COMMIT_HASH;
|
||||
} else {
|
||||
return "prod-" + getPlatformName() + "-" + G_BUILD_COMMIT_HASH;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the environment id (dev, prod, etc)
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getEnvironmentId() {
|
||||
if (G_IS_DEV && IS_DEBUG) {
|
||||
return "local-dev";
|
||||
} else if (G_IS_DEV) {
|
||||
return "dev-" + getPlatformName();
|
||||
} else if (G_IS_RELEASE) {
|
||||
return "release-" + getPlatformName();
|
||||
} else {
|
||||
return "staging-" + getPlatformName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this platform is android
|
||||
* @returns {boolean}
|
||||
@ -66,7 +27,7 @@ export function isIos() {
|
||||
|
||||
/**
|
||||
* Returns a platform name
|
||||
* @returns {string}
|
||||
* @returns {"android" | "browser" | "ios" | "standalone" | "unknown"}
|
||||
*/
|
||||
export function getPlatformName() {
|
||||
if (G_IS_STANDALONE) {
|
||||
@ -96,60 +57,13 @@ export function getIPCRenderer() {
|
||||
return ipcRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a sensitive token by only displaying the first digits of it. Use for
|
||||
* stuff like savegame keys etc which should not appear in the log.
|
||||
* @param {string} key
|
||||
*/
|
||||
export function formatSensitive(key) {
|
||||
if (!key) {
|
||||
return "<null>";
|
||||
}
|
||||
key = key || "";
|
||||
return "[" + key.substr(0, 8) + "...]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new 2D array with the given fill method
|
||||
* @param {number} w Width
|
||||
* @param {number} h Height
|
||||
* @param {(function(number, number) : any) | number | boolean | string | null | undefined} filler Either Fill method, which should return the content for each cell, or static content
|
||||
* @param {string=} context Optional context for memory tracking
|
||||
* @returns {Array<Array<any>>}
|
||||
*/
|
||||
export function make2DArray(w, h, filler, context = null) {
|
||||
if (typeof filler === "function") {
|
||||
const tiles = new Array(w);
|
||||
for (let x = 0; x < w; ++x) {
|
||||
const row = new Array(h);
|
||||
for (let y = 0; y < h; ++y) {
|
||||
row[y] = filler(x, y);
|
||||
}
|
||||
tiles[x] = row;
|
||||
}
|
||||
return tiles;
|
||||
} else {
|
||||
const tiles = new Array(w);
|
||||
const row = new Array(h);
|
||||
for (let y = 0; y < h; ++y) {
|
||||
row[y] = filler;
|
||||
}
|
||||
|
||||
for (let x = 0; x < w; ++x) {
|
||||
tiles[x] = row.slice();
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a new 2D array with undefined contents
|
||||
* @param {number} w
|
||||
* @param {number} h
|
||||
* @param {string=} context
|
||||
* @returns {Array<Array<any>>}
|
||||
*/
|
||||
export function make2DUndefinedArray(w, h, context = null) {
|
||||
export function make2DUndefinedArray(w, h) {
|
||||
const result = new Array(w);
|
||||
for (let x = 0; x < w; ++x) {
|
||||
result[x] = new Array(h);
|
||||
@ -157,33 +71,6 @@ export function make2DUndefinedArray(w, h, context = null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a given 2D array with the given fill method
|
||||
* @param {Array<Array<any>>} array
|
||||
* @param {number} w Width
|
||||
* @param {number} h Height
|
||||
* @param {(function(number, number) : any) | number | boolean | string | null | undefined} filler Either Fill method, which should return the content for each cell, or static content
|
||||
*/
|
||||
export function clear2DArray(array, w, h, filler) {
|
||||
assert(array.length === w, "Array dims mismatch w");
|
||||
assert(array[0].length === h, "Array dims mismatch h");
|
||||
if (typeof filler === "function") {
|
||||
for (let x = 0; x < w; ++x) {
|
||||
const row = array[x];
|
||||
for (let y = 0; y < h; ++y) {
|
||||
row[y] = filler(x, y);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let x = 0; x < w; ++x) {
|
||||
const row = array[x];
|
||||
for (let y = 0; y < h; ++y) {
|
||||
row[y] = filler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new map (an empty object without any props)
|
||||
*/
|
||||
@ -215,7 +102,9 @@ export function accessNestedPropertyReverse(obj, keys) {
|
||||
|
||||
/**
|
||||
* Chooses a random entry of an array
|
||||
* @param {Array | string} arr
|
||||
* @template T
|
||||
* @param {T[]} arr
|
||||
* @returns {T}
|
||||
*/
|
||||
export function randomChoice(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
@ -304,23 +193,6 @@ export function arrayDeleteValue(array, value) {
|
||||
return arrayDelete(array, index);
|
||||
}
|
||||
|
||||
// Converts a direction into a 0 .. 7 index
|
||||
/**
|
||||
* Converts a direction into a index from 0 .. 7, used for miners, zombies etc which have 8 sprites
|
||||
* @param {Vector} offset direction
|
||||
* @param {boolean} inverse if inverse, the direction is reversed
|
||||
* @returns {number} in range [0, 7]
|
||||
*/
|
||||
export function angleToSpriteIndex(offset, inverse = false) {
|
||||
const twoPi = 2.0 * Math.PI;
|
||||
const factor = inverse ? -1 : 1;
|
||||
const offs = inverse ? 2.5 : 3.5;
|
||||
const angle = (factor * Math.atan2(offset.y, offset.x) + offs * Math.PI) % twoPi;
|
||||
|
||||
const index = Math.round((angle / twoPi) * 8) % 8;
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two floats for epsilon equality
|
||||
* @param {number} a
|
||||
@ -331,15 +203,6 @@ export function epsilonCompare(a, b, epsilon = 1e-5) {
|
||||
return Math.abs(a - b) < epsilon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare a float for epsilon equal to 0
|
||||
* @param {number} a
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function epsilonIsZero(a) {
|
||||
return epsilonCompare(a, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates two numbers
|
||||
* @param {number} a
|
||||
@ -399,17 +262,6 @@ export function findNiceIntegerValue(num) {
|
||||
return Math.ceil(findNiceValue(num));
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart rounding + fractional handling
|
||||
* @param {number} n
|
||||
*/
|
||||
function roundSmart(n) {
|
||||
if (n < 100) {
|
||||
return n.toFixed(1);
|
||||
}
|
||||
return Math.round(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a big number
|
||||
* @param {number} num
|
||||
@ -477,92 +329,12 @@ export function formatBigNumberFull(num, divider = T.global.thousandsDivider) {
|
||||
return out.substring(0, out.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delayes a promise so that it will resolve after a *minimum* amount of time only
|
||||
* @param {Promise<any>} promise The promise to delay
|
||||
* @param {number} minTimeMs The time to make it run at least
|
||||
* @returns {Promise<any>} The delayed promise
|
||||
*/
|
||||
export function artificialDelayedPromise(promise, minTimeMs = 500) {
|
||||
if (G_IS_DEV && globalConfig.debug.noArtificialDelays) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
const startTime = performance.now();
|
||||
return promise.then(
|
||||
result => {
|
||||
const timeTaken = performance.now() - startTime;
|
||||
const waitTime = Math.floor(minTimeMs - timeTaken);
|
||||
if (waitTime > 0) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve(result);
|
||||
}, waitTime);
|
||||
});
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
const timeTaken = performance.now() - startTime;
|
||||
const waitTime = Math.floor(minTimeMs - timeTaken);
|
||||
if (waitTime > 0) {
|
||||
// @ts-ignore
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(error);
|
||||
}, waitTime);
|
||||
});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a sine-based animation which pulsates from 0 .. 1 .. 0
|
||||
* @param {number} time Current time in seconds
|
||||
* @param {number} duration Duration of the full pulse in seconds
|
||||
* @param {number} seed Seed to offset the animation
|
||||
*/
|
||||
export function pulseAnimation(time, duration = 1.0, seed = 0.0) {
|
||||
return Math.sin((time * Math.PI * 2.0) / duration + seed * 5642.86729349) * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smallest angle between two angles
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
* @returns {number} 0 .. 2 PI
|
||||
*/
|
||||
export function smallestAngle(a, b) {
|
||||
return safeMod(a - b + Math.PI, 2.0 * Math.PI) - Math.PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modulo which works for negative numbers
|
||||
* @param {number} n
|
||||
* @param {number} m
|
||||
*/
|
||||
export function safeMod(n, m) {
|
||||
return ((n % m) + m) % m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an angle between 0 and 2 pi
|
||||
* @param {number} angle
|
||||
*/
|
||||
export function wrapAngle(angle) {
|
||||
return safeMod(angle, 2.0 * Math.PI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits two frames so the ui is updated
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function waitNextFrame() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise(function (resolve) {
|
||||
window.requestAnimationFrame(function () {
|
||||
window.requestAnimationFrame(function () {
|
||||
resolve();
|
||||
@ -617,27 +389,13 @@ export function clamp(v, minimum = 0, maximum = 1) {
|
||||
return Math.max(minimum, Math.min(maximum, v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures how long a function took
|
||||
* @param {string} name
|
||||
* @param {function():void} target
|
||||
*/
|
||||
export function measure(name, target) {
|
||||
const now = performance.now();
|
||||
for (let i = 0; i < 25; ++i) {
|
||||
target();
|
||||
}
|
||||
const dur = (performance.now() - now) / 25.0;
|
||||
console.warn("->", name, "took", dur.toFixed(2), "ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a new div element
|
||||
* @param {string=} id
|
||||
* @param {Array<string>=} classes
|
||||
* @param {string=} innerHTML
|
||||
*/
|
||||
export function makeDivElement(id = null, classes = [], innerHTML = "") {
|
||||
function makeDivElement(id = null, classes = [], innerHTML = "") {
|
||||
const div = document.createElement("div");
|
||||
if (id) {
|
||||
div.id = id;
|
||||
@ -662,20 +420,6 @@ export function makeDiv(parent, id = null, classes = [], innerHTML = "") {
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a new div and place before reference Node
|
||||
* @param {Element} parent
|
||||
* @param {Element} referenceNode
|
||||
* @param {string=} id
|
||||
* @param {Array<string>=} classes
|
||||
* @param {string=} innerHTML
|
||||
*/
|
||||
export function makeDivBefore(parent, referenceNode, id = null, classes = [], innerHTML = "") {
|
||||
const div = makeDivElement(id, classes, innerHTML);
|
||||
parent.insertBefore(div, referenceNode);
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a new button element
|
||||
* @param {Array<string>=} classes
|
||||
@ -703,19 +447,6 @@ export function makeButton(parent, classes = [], innerHTML = "") {
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a new button and place before reference Node
|
||||
* @param {Element} parent
|
||||
* @param {Element} referenceNode
|
||||
* @param {Array<string>=} classes
|
||||
* @param {string=} innerHTML
|
||||
*/
|
||||
export function makeButtonBefore(parent, referenceNode, classes = [], innerHTML = "") {
|
||||
const element = makeButtonElement(classes, innerHTML);
|
||||
parent.insertBefore(element, referenceNode);
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all children of the given element
|
||||
* @param {Element} elem
|
||||
@ -728,20 +459,10 @@ export function removeAllChildren(elem) {
|
||||
}
|
||||
}
|
||||
|
||||
export function smartFadeNumber(current, newOne, minFade = 0.01, maxFade = 0.9) {
|
||||
const tolerance = Math.min(current, newOne) * 0.5 + 10;
|
||||
let fade = minFade;
|
||||
if (Math.abs(current - newOne) < tolerance) {
|
||||
fade = maxFade;
|
||||
}
|
||||
|
||||
return current * fade + newOne * (1 - fade);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes lockstep simulation by converting times like 34.0000000003 to 34.00.
|
||||
* We use 3 digits of precision, this allows to store sufficient precision of 1 ms without
|
||||
* the risk to simulation errors due to resync issues
|
||||
* We use 3 digits of precision, this allows us to store precision of 1 ms without
|
||||
* the risking simulation errors due to resync issues
|
||||
* @param {number} value
|
||||
*/
|
||||
export function quantizeFloat(value) {
|
||||
@ -840,37 +561,6 @@ export function isSupportedBrowser() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a json schema object
|
||||
* @param {any} properties
|
||||
*/
|
||||
export function schemaObject(properties) {
|
||||
return {
|
||||
type: "object",
|
||||
required: Object.keys(properties).slice(),
|
||||
additionalProperties: false,
|
||||
properties,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} deg
|
||||
* @returns {Vector}
|
||||
*/
|
||||
export function fastRotateMultipleOf90(x, y, deg) {
|
||||
switch (deg) {
|
||||
case 0: {
|
||||
return new Vector(x, y);
|
||||
}
|
||||
case 90: {
|
||||
return new Vector(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an amount of seconds into something like "5s ago"
|
||||
* @param {number} secs Seconds
|
||||
@ -928,31 +618,6 @@ export function formatSeconds(secs) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a file download
|
||||
* @param {string} filename
|
||||
* @param {string} text
|
||||
*/
|
||||
export function generateFileDownload(filename, text) {
|
||||
var element = document.createElement("a");
|
||||
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
|
||||
element.setAttribute("download", filename);
|
||||
|
||||
element.style.display = "none";
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes the first letter
|
||||
* @param {string} str
|
||||
*/
|
||||
export function capitalizeFirstLetter(str) {
|
||||
return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a number like 2.5 to "2.5 items / s"
|
||||
* @param {number} speed
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { BasicSerializableObject } from "../savegame/serialization";
|
||||
import { enumLayer } from "./root";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumItemType = {
|
||||
@ -13,15 +12,6 @@ export const enumItemType = {
|
||||
genericEnergy: "genericEnergy",
|
||||
};
|
||||
|
||||
/** @enum {enumLayer} */
|
||||
export const enumItemTypeToLayer = {
|
||||
[enumItemType.shape]: enumLayer.regular,
|
||||
[enumItemType.color]: enumLayer.regular,
|
||||
[enumItemType.positiveEnergy]: enumLayer.wires,
|
||||
[enumItemType.negativeEnergy]: enumLayer.wires,
|
||||
[enumItemType.genericEnergy]: enumLayer.wires,
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for items on belts etc. Not an entity for performance reasons
|
||||
*/
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { globalConfig } from "../core/config";
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { gItemRegistry } from "../core/global_registries";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
import { epsilonCompare, round4Digits } from "../core/utils";
|
||||
import { Vector } from "../core/vector";
|
||||
import { enumDirection, enumDirectionToVector, Vector, enumInvertedDirections } from "../core/vector";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { BaseItem } from "./base_item";
|
||||
import { Entity } from "./entity";
|
||||
import { GameRoot, enumLayer } from "./root";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { gItemRegistry } from "../core/global_registries";
|
||||
|
||||
const logger = createLogger("belt_path");
|
||||
|
||||
@ -115,22 +115,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
canAcceptItem() {
|
||||
return this.spacingToFirstItem >= this.getItemSpacing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the spacing between items
|
||||
*/
|
||||
getItemSpacing() {
|
||||
return globalConfig.beltItemSpacingByLayer[this.layer];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layer of the this path
|
||||
* @returns {enumLayer}
|
||||
*/
|
||||
getLayer() {
|
||||
return this.entityPath[0].layer;
|
||||
return this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,15 +123,15 @@ export class BeltPath extends BasicSerializableObject {
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
tryAcceptItem(item) {
|
||||
if (this.spacingToFirstItem >= this.getItemSpacing()) {
|
||||
if (this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts) {
|
||||
// So, since we already need one tick to accept this item we will add this directly.
|
||||
const beltProgressPerTick =
|
||||
this.root.hubGoals.getBeltBaseSpeed(this.layer) *
|
||||
this.root.hubGoals.getBeltBaseSpeed() *
|
||||
this.root.dynamicTickrate.deltaSeconds *
|
||||
this.getItemSpacing();
|
||||
globalConfig.itemSpacingOnBelts;
|
||||
|
||||
// First, compute how much progress we can make *at max*
|
||||
const maxProgress = Math.max(0, this.spacingToFirstItem - this.getItemSpacing());
|
||||
const maxProgress = Math.max(0, this.spacingToFirstItem - globalConfig.itemSpacingOnBelts);
|
||||
const initialProgress = Math.min(maxProgress, beltProgressPerTick);
|
||||
|
||||
this.items.unshift([this.spacingToFirstItem - initialProgress, item]);
|
||||
@ -167,7 +152,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
* @returns {BaseItem|null}
|
||||
*/
|
||||
findItemAtTile(tile) {
|
||||
// TODO: This breaks color blind mode otherwise
|
||||
// @TODO: This breaks color blind mode otherwise
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -186,27 +171,81 @@ export class BeltPath extends BasicSerializableObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all ejectors on the path, so that only the last ejector
|
||||
* Recomputes cache variables once the path was changed
|
||||
*/
|
||||
onPathChanged() {
|
||||
this.ejectorComp = this.entityPath[this.entityPath.length - 1].components.ItemEjector;
|
||||
this.ejectorSlot = this.ejectorComp.slots[0];
|
||||
this.acceptorTarget = this.computeAcceptingEntityAndSlot();
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||
const ejectorComp = this.entityPath[i].components.ItemEjector;
|
||||
const isLast = i === this.entityPath.length - 1;
|
||||
ejectorComp.enabled = isLast;
|
||||
/**
|
||||
* Called by the belt system when the surroundings changed
|
||||
*/
|
||||
onSurroundingsChanged() {
|
||||
this.onPathChanged();
|
||||
}
|
||||
|
||||
// Clear all slots of non-end entities
|
||||
if (!isLast) {
|
||||
for (let k = 0; k < ejectorComp.slots.length; ++k) {
|
||||
ejectorComp.slots[k].item = null;
|
||||
ejectorComp.slots[k].progress = 0.0;
|
||||
/**
|
||||
* Finds the entity which accepts our items
|
||||
* @return {{ entity: Entity, slot: number, direction?: enumDirection }}
|
||||
*/
|
||||
computeAcceptingEntityAndSlot() {
|
||||
const lastEntity = this.entityPath[this.entityPath.length - 1];
|
||||
const lastStatic = lastEntity.components.StaticMapEntity;
|
||||
const lastBeltComp = lastEntity.components.Belt;
|
||||
|
||||
// Figure out where and into which direction we eject items
|
||||
const ejectSlotWsTile = lastStatic.localTileToWorld(new Vector(0, 0));
|
||||
const ejectSlotWsDirection = lastStatic.localDirectionToWorld(lastBeltComp.direction);
|
||||
const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection];
|
||||
const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector);
|
||||
|
||||
// Try to find the given acceptor component to take the item
|
||||
const targetEntity = this.root.map.getLayerContentXY(
|
||||
ejectSlotTargetWsTile.x,
|
||||
ejectSlotTargetWsTile.y,
|
||||
enumLayer.regular
|
||||
);
|
||||
|
||||
if (targetEntity) {
|
||||
const targetStaticComp = targetEntity.components.StaticMapEntity;
|
||||
const targetBeltComp = targetEntity.components.Belt;
|
||||
|
||||
// Check for belts (special case)
|
||||
if (targetBeltComp) {
|
||||
const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top);
|
||||
if (ejectSlotWsDirection === beltAcceptingDirection) {
|
||||
return {
|
||||
entity: targetEntity,
|
||||
direction: null,
|
||||
slot: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.layer = this.entityPath[0].layer;
|
||||
// Check for item acceptors
|
||||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||
if (!targetAcceptorComp) {
|
||||
// Entity doesn't accept items
|
||||
return;
|
||||
}
|
||||
|
||||
const ejectingDirection = targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection);
|
||||
const matchingSlot = targetAcceptorComp.findMatchingSlot(
|
||||
targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile),
|
||||
ejectingDirection
|
||||
);
|
||||
|
||||
if (!matchingSlot) {
|
||||
// No matching slot found
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
entity: targetEntity,
|
||||
slot: matchingSlot.index,
|
||||
direction: enumInvertedDirections[ejectingDirection],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Following code will be compiled out outside of dev versions
|
||||
@ -251,11 +290,6 @@ export class BeltPath extends BasicSerializableObject {
|
||||
return fail("Reference to destroyed entity " + entity.uid);
|
||||
}
|
||||
|
||||
const enabledState = i === this.entityPath.length - 1;
|
||||
if (entity.components.ItemEjector.enabled !== enabledState) {
|
||||
return fail("Item ejector enabled state is not synchronized (index =" + i + ")");
|
||||
}
|
||||
|
||||
const followUp = this.root.systemMgr.systems.belt.findFollowUpEntity(entity);
|
||||
if (!followUp) {
|
||||
return fail(
|
||||
@ -283,20 +317,6 @@ export class BeltPath extends BasicSerializableObject {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for right ejector component and slot
|
||||
if (this.ejectorComp !== this.entityPath[this.entityPath.length - 1].components.ItemEjector) {
|
||||
return fail("Stale ejectorComp handle");
|
||||
}
|
||||
if (this.ejectorSlot !== this.ejectorComp.slots[0]) {
|
||||
return fail("Stale ejector slot handle");
|
||||
}
|
||||
if (!this.ejectorComp) {
|
||||
return fail("Ejector comp not set");
|
||||
}
|
||||
if (!this.ejectorSlot) {
|
||||
return fail("Ejector slot not set");
|
||||
}
|
||||
|
||||
// Check spacing
|
||||
if (this.spacingToFirstItem > this.totalLength + 0.005) {
|
||||
return fail(
|
||||
@ -370,20 +390,12 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
const beltComp = entity.components.Belt;
|
||||
|
||||
// If the last belt has something on its ejector, put that into the path first
|
||||
const pendingItem = this.ejectorComp.takeSlotItem(0);
|
||||
if (pendingItem) {
|
||||
// Ok, so we have a pending item
|
||||
DEBUG && logger.log("Taking pending item and putting it back on the path");
|
||||
this.items.push([0, pendingItem]);
|
||||
}
|
||||
|
||||
// Append the entity
|
||||
this.entityPath.push(entity);
|
||||
this.onPathChanged();
|
||||
|
||||
// Extend the path length
|
||||
const additionalLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
||||
const additionalLength = beltComp.getEffectiveLengthTiles();
|
||||
this.totalLength += additionalLength;
|
||||
DEBUG && logger.log(" Extended total length by", additionalLength, "to", this.totalLength);
|
||||
|
||||
@ -426,7 +438,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
// All items on that belt are simply lost (for now)
|
||||
|
||||
const length = beltComp.getEffectiveLengthTiles(entity.layer);
|
||||
const length = beltComp.getEffectiveLengthTiles();
|
||||
|
||||
// Extend the length of this path
|
||||
this.totalLength += length;
|
||||
@ -478,7 +490,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
const beltComp = entity.components.Belt;
|
||||
beltComp.assignedPath = null;
|
||||
|
||||
const entityLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
||||
const entityLength = beltComp.getEffectiveLengthTiles();
|
||||
assert(this.entityPath.indexOf(entity) >= 0, "Entity not contained for split");
|
||||
assert(this.entityPath.indexOf(entity) !== 0, "Entity is first");
|
||||
assert(this.entityPath.indexOf(entity) !== this.entityPath.length - 1, "Entity is last");
|
||||
@ -496,7 +508,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
++firstPathEntityCount;
|
||||
firstPathEndEntity = otherEntity;
|
||||
firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles(otherEntity.layer);
|
||||
firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles();
|
||||
}
|
||||
|
||||
DEBUG &&
|
||||
@ -640,7 +652,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
// Ok, first remove the entity
|
||||
const beltComp = entity.components.Belt;
|
||||
const beltLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
||||
const beltLength = beltComp.getEffectiveLengthTiles();
|
||||
|
||||
DEBUG &&
|
||||
logger.log(
|
||||
@ -749,7 +761,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
// Ok, first remove the entity
|
||||
const beltComp = entity.components.Belt;
|
||||
const beltLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
||||
const beltLength = beltComp.getEffectiveLengthTiles();
|
||||
|
||||
DEBUG &&
|
||||
logger.log(
|
||||
@ -889,7 +901,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
beltComp.assignedPath = this;
|
||||
|
||||
// Update our length
|
||||
const additionalLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
||||
const additionalLength = beltComp.getEffectiveLengthTiles();
|
||||
this.totalLength += additionalLength;
|
||||
}
|
||||
|
||||
@ -947,7 +959,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
let length = 0;
|
||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||
const entity = this.entityPath[i];
|
||||
length += entity.components.Belt.getEffectiveLengthTiles(this.layer);
|
||||
length += entity.components.Belt.getEffectiveLengthTiles();
|
||||
}
|
||||
return length;
|
||||
}
|
||||
@ -962,15 +974,15 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
// Divide by item spacing on belts since we use throughput and not speed
|
||||
let beltSpeed =
|
||||
this.root.hubGoals.getBeltBaseSpeed(this.layer) *
|
||||
this.root.hubGoals.getBeltBaseSpeed() *
|
||||
this.root.dynamicTickrate.deltaSeconds *
|
||||
this.getItemSpacing();
|
||||
globalConfig.itemSpacingOnBelts;
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.instantBelts) {
|
||||
beltSpeed *= 100;
|
||||
}
|
||||
|
||||
let minimumDistance = this.ejectorSlot.item ? this.getItemSpacing() : 0;
|
||||
let minimumDistance = 0;
|
||||
|
||||
// Try to reduce spacing
|
||||
let remainingAmount = beltSpeed;
|
||||
@ -991,13 +1003,13 @@ export class BeltPath extends BasicSerializableObject {
|
||||
break;
|
||||
}
|
||||
|
||||
minimumDistance = this.getItemSpacing();
|
||||
minimumDistance = globalConfig.itemSpacingOnBelts;
|
||||
}
|
||||
|
||||
// Check if we have an item which is ready to be emitted
|
||||
const lastItem = this.items[this.items.length - 1];
|
||||
if (lastItem && lastItem[_nextDistance] === 0) {
|
||||
// Take over
|
||||
if (this.ejectorComp.tryEject(0, lastItem[_item])) {
|
||||
if (lastItem && lastItem[_nextDistance] === 0 && this.acceptorTarget) {
|
||||
if (this.tryHandOverItem(lastItem[_item])) {
|
||||
this.items.pop();
|
||||
}
|
||||
}
|
||||
@ -1007,6 +1019,47 @@ export class BeltPath extends BasicSerializableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to hand over the item to the end entity
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
tryHandOverItem(item) {
|
||||
if (!this.acceptorTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetAcceptorComp = this.acceptorTarget.entity.components.ItemAcceptor;
|
||||
|
||||
// Check if the acceptor has a filter for example
|
||||
if (targetAcceptorComp && !targetAcceptorComp.canAcceptItem(this.acceptorTarget.slot, item)) {
|
||||
// Well, this item is not accepted
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to pass over
|
||||
if (
|
||||
this.root.systemMgr.systems.itemEjector.tryPassOverItem(
|
||||
item,
|
||||
this.acceptorTarget.entity,
|
||||
this.acceptorTarget.slot
|
||||
)
|
||||
) {
|
||||
// Trigger animation on the acceptor comp
|
||||
const targetAcceptorComp = this.acceptorTarget.entity.components.ItemAcceptor;
|
||||
if (targetAcceptorComp) {
|
||||
targetAcceptorComp.onItemAccepted(
|
||||
this.acceptorTarget.slot,
|
||||
this.acceptorTarget.direction,
|
||||
item
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a world space position from the given progress
|
||||
* @param {number} progress
|
||||
@ -1020,14 +1073,14 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||
const beltComp = this.entityPath[i].components.Belt;
|
||||
const localLength = beltComp.getEffectiveLengthTiles(this.layer);
|
||||
const localLength = beltComp.getEffectiveLengthTiles();
|
||||
|
||||
if (currentLength + localLength >= progress || i === this.entityPath.length - 1) {
|
||||
// Min required here due to floating point issues
|
||||
const localProgress = Math.min(1.0, progress - currentLength);
|
||||
|
||||
assert(localProgress >= 0.0, "Invalid local progress: " + localProgress);
|
||||
const localSpace = beltComp.transformBeltToLocalSpace(localProgress, this.layer);
|
||||
const localSpace = beltComp.transformBeltToLocalSpace(localProgress);
|
||||
return this.entityPath[i].components.StaticMapEntity.localTileToWorld(localSpace);
|
||||
}
|
||||
currentLength += localLength;
|
||||
@ -1045,11 +1098,6 @@ export class BeltPath extends BasicSerializableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.entityPath[0].layer !== this.root.currentLayer) {
|
||||
// Don't draw
|
||||
return;
|
||||
}
|
||||
|
||||
parameters.context.fillStyle = "#d79a25";
|
||||
parameters.context.strokeStyle = "#d79a25";
|
||||
parameters.context.beginPath();
|
||||
@ -1131,10 +1179,10 @@ export class BeltPath extends BasicSerializableObject {
|
||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||
const entity = this.entityPath[i];
|
||||
const beltComp = entity.components.Belt;
|
||||
const beltLength = beltComp.getEffectiveLengthTiles(this.layer);
|
||||
const beltLength = beltComp.getEffectiveLengthTiles();
|
||||
|
||||
// Check if the current items are on the belt
|
||||
while (trackPos + beltLength >= currentItemPos) {
|
||||
while (trackPos + beltLength >= currentItemPos - 1e-51) {
|
||||
// Its on the belt, render it now
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
assert(
|
||||
@ -1142,7 +1190,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
"invalid track pos: " + currentItemPos + " vs " + trackPos + " (l =" + beltLength + ")"
|
||||
);
|
||||
|
||||
const localPos = beltComp.transformBeltToLocalSpace(currentItemPos - trackPos, this.layer);
|
||||
const localPos = beltComp.transformBeltToLocalSpace(currentItemPos - trackPos);
|
||||
const worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile();
|
||||
|
||||
const distanceAndItem = this.items[currentItemIndex];
|
||||
|
@ -92,13 +92,7 @@ export class Blueprint {
|
||||
parameters.context.globalAlpha = 1;
|
||||
}
|
||||
|
||||
staticComp.drawSpriteOnFullEntityBounds(
|
||||
parameters,
|
||||
staticComp.getBlueprintSprite(),
|
||||
0,
|
||||
true,
|
||||
newPos
|
||||
);
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, staticComp.getBlueprintSprite(), 0, newPos);
|
||||
}
|
||||
parameters.context.globalAlpha = 1;
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { MetaBuilding, defaultBuildingVariant } from "./meta_building";
|
||||
/* typehints:start */
|
||||
import { MetaBuilding } from "./meta_building";
|
||||
import { AtlasSprite } from "../core/sprites";
|
||||
import { Vector } from "../core/vector";
|
||||
/* typehints:end */
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
@ -7,6 +10,7 @@ import { AtlasSprite } from "../core/sprites";
|
||||
* metaInstance?: MetaBuilding,
|
||||
* variant?: string,
|
||||
* rotationVariant?: number,
|
||||
* tileSize?: Vector,
|
||||
* sprite?: AtlasSprite,
|
||||
* blueprintSprite?: AtlasSprite,
|
||||
* silhouetteColor?: string
|
||||
@ -22,18 +26,25 @@ export const gBuildingVariants = {
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} id
|
||||
* @param {*} meta
|
||||
* @param {*} variant
|
||||
* @param {*} rotationVariant
|
||||
* Registers a new variant
|
||||
* @param {number} id
|
||||
* @param {typeof MetaBuilding} meta
|
||||
* @param {string} variant
|
||||
* @param {number} rotationVariant
|
||||
*/
|
||||
export function registerBuildingVariant(id, meta, variant = defaultBuildingVariant, rotationVariant = 0) {
|
||||
export function registerBuildingVariant(
|
||||
id,
|
||||
meta,
|
||||
variant = "default" /* FIXME: Circular dependency, actually its defaultBuildingVariant */,
|
||||
rotationVariant = 0
|
||||
) {
|
||||
assert(!gBuildingVariants[id], "Duplicate id: " + id);
|
||||
gBuildingVariants[id] = {
|
||||
metaClass: meta,
|
||||
variant,
|
||||
rotationVariant,
|
||||
// @ts-ignore
|
||||
tileSize: new meta().getDimensions(variant),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,108 +0,0 @@
|
||||
import { formatItemsPerSecond } from "../../core/utils";
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { T } from "../../translations";
|
||||
import { enumItemType } from "../base_item";
|
||||
import { EnergyConsumerComponent } from "../components/energy_consumer";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { enumLayer, GameRoot } from "../root";
|
||||
|
||||
export class MetaAdvancedProcessorBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("advanced_processor");
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "#25d7b8";
|
||||
}
|
||||
|
||||
getDimensions(variant) {
|
||||
return new Vector(2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {string} variant
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.advancedProcessor);
|
||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
getIsUnlocked(root) {
|
||||
// TODO
|
||||
return true;
|
||||
// return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(
|
||||
new ItemProcessorComponent({
|
||||
inputsPerCharge: 1,
|
||||
processorType: enumItemProcessorTypes.advancedProcessor,
|
||||
})
|
||||
);
|
||||
entity.addComponent(
|
||||
new ItemEjectorComponent({
|
||||
slots: [
|
||||
{ pos: new Vector(1, 0), direction: enumDirection.right },
|
||||
{ pos: new Vector(1, 0), direction: enumDirection.top, layer: enumLayer.wires },
|
||||
],
|
||||
})
|
||||
);
|
||||
entity.addComponent(
|
||||
new EnergyConsumerComponent({
|
||||
bufferSize: 3,
|
||||
perCharge: 1,
|
||||
batteryPosition: new Vector(0.63, 0.7),
|
||||
acceptorSlotIndex: 1,
|
||||
ejectorSlotIndex: 1,
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new WiredPinsComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.top,
|
||||
type: enumPinSlotType.positiveEnergyAcceptor,
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 0),
|
||||
direction: enumDirection.top,
|
||||
type: enumPinSlotType.negativeEnergyEjector,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
entity.addComponent(
|
||||
new ItemAcceptorComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 1),
|
||||
directions: [enumDirection.left],
|
||||
},
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.top],
|
||||
filter: enumItemType.positiveEnergy,
|
||||
layer: enumLayer.wires,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -3,15 +3,35 @@ import { enumAngleToDirection, enumDirection, Vector } from "../../core/vector";
|
||||
import { SOUNDS } from "../../platform/sound";
|
||||
import { T } from "../../translations";
|
||||
import { BeltComponent } from "../components/belt";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { ReplaceableMapEntityComponent } from "../components/replaceable_map_entity";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { GameRoot, enumLayer } from "../root";
|
||||
import { GameRoot } from "../root";
|
||||
|
||||
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
|
||||
|
||||
const overlayMatrices = {
|
||||
[enumDirection.top]: {
|
||||
0: [0, 1, 0, 0, 1, 0, 0, 1, 0],
|
||||
90: [0, 0, 0, 1, 1, 1, 0, 0, 0],
|
||||
180: [0, 1, 0, 0, 1, 0, 0, 1, 0],
|
||||
270: [0, 0, 0, 1, 1, 1, 0, 0, 0],
|
||||
},
|
||||
|
||||
[enumDirection.left]: {
|
||||
0: [0, 0, 0, 1, 1, 0, 0, 1, 0],
|
||||
90: [0, 1, 0, 1, 1, 0, 0, 0, 0],
|
||||
180: [0, 1, 0, 0, 1, 1, 0, 0, 0],
|
||||
270: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
||||
},
|
||||
|
||||
[enumDirection.right]: {
|
||||
0: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
||||
90: [0, 0, 0, 1, 1, 0, 0, 1, 0],
|
||||
180: [0, 1, 0, 1, 1, 0, 0, 0, 0],
|
||||
270: [0, 1, 0, 0, 1, 1, 0, 0, 0],
|
||||
},
|
||||
};
|
||||
|
||||
export class MetaBeltBaseBuilding extends MetaBuilding {
|
||||
getHasDirectionLockAvailable() {
|
||||
return true;
|
||||
@ -23,7 +43,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
const beltSpeed = root.hubGoals.getBeltBaseSpeed(enumLayer.regular);
|
||||
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
|
||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
||||
}
|
||||
|
||||
@ -43,6 +63,21 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
||||
return null;
|
||||
}
|
||||
|
||||
getIsReplaceable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} rotation
|
||||
* @param {number} rotationVariant
|
||||
* @param {string} variant
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
|
||||
return overlayMatrices[entity.components.Belt.direction][rotation];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
@ -53,34 +88,6 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
||||
direction: enumDirection.top, // updated later
|
||||
})
|
||||
);
|
||||
// Make this entity replaceabel
|
||||
entity.addComponent(new ReplaceableMapEntityComponent());
|
||||
|
||||
entity.addComponent(
|
||||
new ItemAcceptorComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.bottom],
|
||||
layer: this.getLayer(),
|
||||
},
|
||||
],
|
||||
animated: false,
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new ItemEjectorComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.top, // updated later
|
||||
layer: this.getLayer(),
|
||||
},
|
||||
],
|
||||
instantEject: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,7 +97,6 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
||||
*/
|
||||
updateVariants(entity, rotationVariant) {
|
||||
entity.components.Belt.direction = arrayBeltVariantToRotation[rotationVariant];
|
||||
entity.components.ItemEjector.slots[0].direction = arrayBeltVariantToRotation[rotationVariant];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,7 +115,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
||||
const bottomDirection = enumAngleToDirection[(rotation + 180) % 360];
|
||||
const leftDirection = enumAngleToDirection[(rotation + 270) % 360];
|
||||
|
||||
const { ejectors, acceptors } = root.logic.getEjectorsAndAcceptorsAtTile(tile, layer);
|
||||
const { ejectors, acceptors } = root.logic.getEjectorsAndAcceptorsAtTile(tile);
|
||||
|
||||
let hasBottomEjector = false;
|
||||
let hasRightEjector = false;
|
||||
|
@ -1,109 +0,0 @@
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { enumItemType } from "../base_item";
|
||||
import { EnergyGeneratorComponent } from "../components/energy_generator";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { enumLayer, GameRoot } from "../root";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
|
||||
export class MetaEnergyGenerator extends MetaBuilding {
|
||||
constructor() {
|
||||
super("energy_generator");
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "#c425d7";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {string} variant
|
||||
* @returns {Array<[string, string]>}
|
||||
*/
|
||||
getAdditionalStatistics(root, variant) {
|
||||
// TODO
|
||||
return [];
|
||||
}
|
||||
|
||||
getDimensions(variant) {
|
||||
return new Vector(2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
getIsUnlocked(root) {
|
||||
return true;
|
||||
// return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(
|
||||
new ItemAcceptorComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 1),
|
||||
directions: [enumDirection.bottom],
|
||||
filter: enumItemType.shape,
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 1),
|
||||
directions: [enumDirection.bottom],
|
||||
filter: enumItemType.shape,
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 0),
|
||||
directions: [enumDirection.top],
|
||||
layer: enumLayer.wires,
|
||||
filter: enumItemType.negativeEnergy,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new ItemEjectorComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.top,
|
||||
layer: enumLayer.wires,
|
||||
},
|
||||
],
|
||||
instantEject: true,
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new EnergyGeneratorComponent({
|
||||
// Set by the energy generator system later
|
||||
requiredKey: null,
|
||||
wasteAcceptorSlotIndex: 2,
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new WiredPinsComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
type: enumPinSlotType.positiveEnergyEjector,
|
||||
direction: enumDirection.top,
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 0),
|
||||
type: enumPinSlotType.negativeEnergyAcceptor,
|
||||
direction: enumDirection.top,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ import { enumItemType } from "../base_item";
|
||||
import { HubComponent } from "../components/hub";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||
import { UnremovableComponent } from "../components/unremovable";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins";
|
||||
|
||||
export class MetaHubBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
@ -33,6 +33,10 @@ export class MetaHubBuilding extends MetaBuilding {
|
||||
return null;
|
||||
}
|
||||
|
||||
getIsRemovable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
@ -46,7 +50,18 @@ export class MetaHubBuilding extends MetaBuilding {
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(new UnremovableComponent());
|
||||
entity.addComponent(
|
||||
new WiredPinsComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
type: enumPinSlotType.logicalEjector,
|
||||
direction: enumDirection.top,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new ItemAcceptorComponent({
|
||||
slots: [
|
||||
|
@ -6,11 +6,13 @@ import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
||||
import { GameRoot } from "../root";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
import { T } from "../../translations";
|
||||
import { round1Digit, round2Digits, formatItemsPerSecond } from "../../core/utils";
|
||||
import { formatItemsPerSecond } from "../../core/utils";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumMinerVariants = { chainable: "chainable" };
|
||||
|
||||
const overlayMatrix = [1, 1, 1, 1, 0, 1, 1, 1, 1];
|
||||
|
||||
export class MetaMinerBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("miner");
|
||||
@ -41,6 +43,16 @@ export class MetaMinerBuilding extends MetaBuilding {
|
||||
return super.getAvailableVariants(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} rotation
|
||||
* @param {number} rotationVariant
|
||||
* @param {string} variant
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
|
||||
return overlayMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
||||
import { GameRoot, enumLayer } from "../root";
|
||||
import { GameRoot } from "../root";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
import { T } from "../../translations";
|
||||
import { formatItemsPerSecond } from "../../core/utils";
|
||||
import { BeltUnderlaysComponent } from "../components/belt_underlays";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumSplitterVariants = { compact: "compact", compactInverse: "compact-inverse" };
|
||||
@ -88,6 +88,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
|
||||
slots: [], // set later
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(new BeltUnderlaysComponent({ underlays: [] }));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,9 +117,9 @@ export class MetaSplitterBuilding extends MetaBuilding {
|
||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||
]);
|
||||
|
||||
entity.components.ItemAcceptor.beltUnderlays = [
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.regular },
|
||||
{ pos: new Vector(1, 0), direction: enumDirection.top, layer: enumLayer.regular },
|
||||
entity.components.BeltUnderlays.underlays = [
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||
];
|
||||
|
||||
break;
|
||||
@ -143,8 +145,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||
]);
|
||||
|
||||
entity.components.ItemAcceptor.beltUnderlays = [
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.regular },
|
||||
entity.components.BeltUnderlays.underlays = [
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||
];
|
||||
|
||||
break;
|
||||
|
@ -25,13 +25,31 @@ export const enumUndergroundBeltVariantToTier = {
|
||||
[enumUndergroundBeltVariants.tier2]: 1,
|
||||
};
|
||||
|
||||
const overlayMatrices = [
|
||||
// Sender
|
||||
{
|
||||
0: [1, 1, 1, 0, 1, 0, 0, 1, 0],
|
||||
90: [0, 0, 0, 1, 1, 1, 0, 0, 0],
|
||||
180: [0, 1, 0, 0, 1, 0, 0, 1, 0],
|
||||
270: [0, 0, 0, 1, 1, 1, 0, 0, 0],
|
||||
},
|
||||
|
||||
// Receiver
|
||||
{
|
||||
0: [0, 1, 0, 0, 1, 0, 1, 1, 1],
|
||||
90: [0, 1, 0, 1, 1, 0, 0, 0, 0],
|
||||
180: [0, 1, 0, 0, 1, 1, 0, 0, 0],
|
||||
270: [0, 0, 0, 0, 1, 1, 0, 1, 0],
|
||||
},
|
||||
];
|
||||
|
||||
export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("underground_belt");
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "#555";
|
||||
return "#222";
|
||||
}
|
||||
|
||||
getFlipOrientationAfterPlacement() {
|
||||
@ -42,6 +60,16 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} rotation
|
||||
* @param {number} rotationVariant
|
||||
* @param {string} variant
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) {
|
||||
return overlayMatrices[rotationVariant][rotation];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {string} variant
|
||||
|
@ -1,52 +0,0 @@
|
||||
import { Loader } from "../../core/loader";
|
||||
import { enumDirection } from "../../core/vector";
|
||||
import { enumLayer } from "../root";
|
||||
import { arrayBeltVariantToRotation, MetaBeltBaseBuilding } from "./belt_base";
|
||||
|
||||
export class MetaWireBaseBuilding extends MetaBeltBaseBuilding {
|
||||
constructor() {
|
||||
super("wire");
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "#c425d7";
|
||||
}
|
||||
|
||||
getLayer() {
|
||||
return enumLayer.wires;
|
||||
}
|
||||
|
||||
getPreviewSprite(rotationVariant) {
|
||||
switch (arrayBeltVariantToRotation[rotationVariant]) {
|
||||
case enumDirection.top: {
|
||||
return Loader.getSprite("sprites/buildings/wire_top.png");
|
||||
}
|
||||
case enumDirection.left: {
|
||||
return Loader.getSprite("sprites/buildings/wire_left.png");
|
||||
}
|
||||
case enumDirection.right: {
|
||||
return Loader.getSprite("sprites/buildings/wire_right.png");
|
||||
}
|
||||
default: {
|
||||
assertAlways(false, "Invalid belt rotation variant");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBlueprintSprite(rotationVariant) {
|
||||
switch (arrayBeltVariantToRotation[rotationVariant]) {
|
||||
case enumDirection.top: {
|
||||
return Loader.getSprite("sprites/blueprints/wire_top.png");
|
||||
}
|
||||
case enumDirection.left: {
|
||||
return Loader.getSprite("sprites/blueprints/wire_left.png");
|
||||
}
|
||||
case enumDirection.right: {
|
||||
return Loader.getSprite("sprites/blueprints/wire_right.png");
|
||||
}
|
||||
default: {
|
||||
assertAlways(false, "Invalid belt rotation variant");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { enumItemType } from "../base_item";
|
||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||
import { Entity } from "../entity";
|
||||
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
||||
import { enumLayer, GameRoot } from "../root";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumWireCrossingVariants = {
|
||||
// Default = splitter
|
||||
merger: "merger",
|
||||
};
|
||||
|
||||
export class MetaWireCrossingsBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("wire_crossings");
|
||||
}
|
||||
|
||||
getDimensions(variant) {
|
||||
return new Vector(1, 1);
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "#c425d7";
|
||||
}
|
||||
|
||||
getLayer() {
|
||||
return enumLayer.wires;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
getAvailableVariants(root) {
|
||||
return [defaultBuildingVariant, enumWireCrossingVariants.merger];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
getIsUnlocked(root) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(
|
||||
new ItemAcceptorComponent({
|
||||
slots: [], // set later
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new ItemProcessorComponent({
|
||||
inputsPerCharge: 1,
|
||||
processorType: enumItemProcessorTypes.splitterWires,
|
||||
})
|
||||
);
|
||||
|
||||
entity.addComponent(
|
||||
new ItemEjectorComponent({
|
||||
slots: [], // set later
|
||||
instantEject: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
* @param {number} rotationVariant
|
||||
* @param {string} variant
|
||||
*/
|
||||
updateVariants(entity, rotationVariant, variant) {
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant: {
|
||||
entity.components.ItemAcceptor.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.bottom],
|
||||
layer: enumLayer.wires,
|
||||
},
|
||||
]);
|
||||
|
||||
entity.components.ItemEjector.setSlots([
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.wires },
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.right, layer: enumLayer.wires },
|
||||
]);
|
||||
|
||||
break;
|
||||
}
|
||||
case enumWireCrossingVariants.merger: {
|
||||
entity.components.ItemAcceptor.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.top],
|
||||
layer: enumLayer.wires,
|
||||
},
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.right],
|
||||
layer: enumLayer.wires,
|
||||
},
|
||||
]);
|
||||
|
||||
entity.components.ItemEjector.setSlots([
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.bottom, layer: enumLayer.wires },
|
||||
]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertAlways(false, "Unknown painter variant: " + variant);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,8 +10,6 @@ export const enumColors = {
|
||||
|
||||
white: "white",
|
||||
uncolored: "uncolored",
|
||||
|
||||
black: "black",
|
||||
};
|
||||
|
||||
/** @enum {string} */
|
||||
@ -26,8 +24,6 @@ export const enumColorToShortcode = {
|
||||
|
||||
[enumColors.white]: "w",
|
||||
[enumColors.uncolored]: "u",
|
||||
|
||||
[enumColors.black]: "0",
|
||||
};
|
||||
|
||||
/** @enum {enumColors} */
|
||||
@ -54,27 +50,9 @@ export const enumColorsToHexCode = {
|
||||
// blue + green + red
|
||||
[enumColors.white]: "#ffffff",
|
||||
|
||||
[enumColors.black]: "#31383a",
|
||||
|
||||
[enumColors.uncolored]: "#aaaaaa",
|
||||
};
|
||||
|
||||
/** @enum {enumColors} */
|
||||
export const enumInvertedColors = {
|
||||
[enumColors.red]: enumColors.cyan,
|
||||
[enumColors.green]: enumColors.purple,
|
||||
[enumColors.blue]: enumColors.yellow,
|
||||
|
||||
[enumColors.yellow]: enumColors.blue,
|
||||
[enumColors.purple]: enumColors.green,
|
||||
[enumColors.cyan]: enumColors.red,
|
||||
|
||||
[enumColors.white]: enumColors.black,
|
||||
[enumColors.black]: enumColors.white,
|
||||
|
||||
[enumColors.uncolored]: enumColors.uncolored,
|
||||
};
|
||||
|
||||
const c = enumColors;
|
||||
/** @enum {Object.<string, string>} */
|
||||
export const enumColorMixingResults = {
|
||||
@ -88,7 +66,6 @@ export const enumColorMixingResults = {
|
||||
[c.cyan]: c.white,
|
||||
|
||||
[c.white]: c.white,
|
||||
[c.black]: c.red,
|
||||
},
|
||||
|
||||
// 0, 255, 0
|
||||
@ -100,7 +77,6 @@ export const enumColorMixingResults = {
|
||||
[c.cyan]: c.cyan,
|
||||
|
||||
[c.white]: c.white,
|
||||
[c.black]: c.green,
|
||||
},
|
||||
|
||||
// 0, 255, 0
|
||||
@ -110,20 +86,17 @@ export const enumColorMixingResults = {
|
||||
[c.cyan]: c.cyan,
|
||||
|
||||
[c.white]: c.white,
|
||||
[c.black]: c.blue,
|
||||
},
|
||||
|
||||
// 255, 255, 0
|
||||
[c.yellow]: {
|
||||
[c.purple]: c.white,
|
||||
[c.cyan]: c.white,
|
||||
[c.black]: c.yellow,
|
||||
},
|
||||
|
||||
// 255, 0, 255
|
||||
[c.purple]: {
|
||||
[c.cyan]: c.white,
|
||||
[c.black]: c.purple,
|
||||
},
|
||||
|
||||
// 0, 255, 255
|
||||
@ -140,24 +113,12 @@ export const enumColorMixingResults = {
|
||||
[c.uncolored]: {
|
||||
// auto
|
||||
},
|
||||
|
||||
[c.black]: {
|
||||
// auto
|
||||
[c.white]: c.uncolored,
|
||||
[c.cyan]: c.cyan,
|
||||
[c.uncolored]: c.uncolored,
|
||||
},
|
||||
};
|
||||
|
||||
// Create same color lookups
|
||||
for (const color in enumColors) {
|
||||
enumColorMixingResults[color][color] = color;
|
||||
|
||||
// Anything with white is white again, except for black which creates gray
|
||||
if (color !== enumColors.black) {
|
||||
enumColorMixingResults[color][c.white] = c.white;
|
||||
}
|
||||
|
||||
enumColorMixingResults[color][c.white] = c.white;
|
||||
// Anything with uncolored is the same color
|
||||
enumColorMixingResults[color][c.uncolored] = color;
|
||||
}
|
||||
|
@ -44,3 +44,9 @@ export class Component extends BasicSerializableObject {
|
||||
}
|
||||
/* dev:end */
|
||||
}
|
||||
|
||||
/**
|
||||
* TypeScript does not support Abstract Static methods (https://github.com/microsoft/TypeScript/issues/34516)
|
||||
* One workaround is to declare the type of the component and reference that for static methods
|
||||
* @typedef {typeof Component} StaticComponent
|
||||
*/
|
||||
|
@ -5,14 +5,11 @@ import { ItemEjectorComponent } from "./components/item_ejector";
|
||||
import { ItemAcceptorComponent } from "./components/item_acceptor";
|
||||
import { MinerComponent } from "./components/miner";
|
||||
import { ItemProcessorComponent } from "./components/item_processor";
|
||||
import { ReplaceableMapEntityComponent } from "./components/replaceable_map_entity";
|
||||
import { UndergroundBeltComponent } from "./components/underground_belt";
|
||||
import { UnremovableComponent } from "./components/unremovable";
|
||||
import { HubComponent } from "./components/hub";
|
||||
import { StorageComponent } from "./components/storage";
|
||||
import { EnergyGeneratorComponent } from "./components/energy_generator";
|
||||
import { WiredPinsComponent } from "./components/wired_pins";
|
||||
import { EnergyConsumerComponent } from "./components/energy_consumer";
|
||||
import { BeltUnderlaysComponent } from "./components/belt_underlays";
|
||||
|
||||
export function initComponentRegistry() {
|
||||
gComponentRegistry.register(StaticMapEntityComponent);
|
||||
@ -21,14 +18,11 @@ export function initComponentRegistry() {
|
||||
gComponentRegistry.register(ItemAcceptorComponent);
|
||||
gComponentRegistry.register(MinerComponent);
|
||||
gComponentRegistry.register(ItemProcessorComponent);
|
||||
gComponentRegistry.register(ReplaceableMapEntityComponent);
|
||||
gComponentRegistry.register(UndergroundBeltComponent);
|
||||
gComponentRegistry.register(UnremovableComponent);
|
||||
gComponentRegistry.register(HubComponent);
|
||||
gComponentRegistry.register(StorageComponent);
|
||||
gComponentRegistry.register(EnergyGeneratorComponent);
|
||||
gComponentRegistry.register(WiredPinsComponent);
|
||||
gComponentRegistry.register(EnergyConsumerComponent);
|
||||
gComponentRegistry.register(BeltUnderlaysComponent);
|
||||
|
||||
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
||||
|
||||
|
@ -2,23 +2,44 @@ import { enumDirection, Vector } from "../../core/vector";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { BeltPath } from "../belt_path";
|
||||
import { Component } from "../component";
|
||||
import { Entity } from "../entity";
|
||||
import { enumLayer } from "../root";
|
||||
|
||||
export const curvedBeltLength = /* Math.PI / 4 */ 0.78;
|
||||
|
||||
/** @type {import("./item_acceptor").ItemAcceptorSlot} */
|
||||
export const FAKE_BELT_ACCEPTOR_SLOT = {
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.bottom],
|
||||
};
|
||||
|
||||
/** @type {Object<enumDirection, import("./item_ejector").ItemEjectorSlot>} */
|
||||
export const FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION = {
|
||||
[enumDirection.top]: {
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.top,
|
||||
item: null,
|
||||
progress: 0,
|
||||
},
|
||||
|
||||
[enumDirection.right]: {
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.right,
|
||||
item: null,
|
||||
progress: 0,
|
||||
},
|
||||
|
||||
[enumDirection.left]: {
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.left,
|
||||
item: null,
|
||||
progress: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export class BeltComponent extends Component {
|
||||
static getId() {
|
||||
return "Belt";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
// The followUpCache field is not serialized.
|
||||
return {
|
||||
direction: types.string,
|
||||
};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new BeltComponent({ direction: this.direction });
|
||||
}
|
||||
@ -33,9 +54,6 @@ export class BeltComponent extends Component {
|
||||
|
||||
this.direction = direction;
|
||||
|
||||
/** @type {Entity} */
|
||||
this.followUpCache = null;
|
||||
|
||||
/**
|
||||
* The path this belt is contained in, not serialized
|
||||
* @type {BeltPath}
|
||||
@ -45,77 +63,59 @@ export class BeltComponent extends Component {
|
||||
|
||||
/**
|
||||
* Returns the effective length of this belt in tile space
|
||||
* @param {enumLayer} layer
|
||||
* @returns {number}
|
||||
*/
|
||||
getEffectiveLengthTiles(layer) {
|
||||
assert(layer, "no layer given");
|
||||
if (layer === enumLayer.wires) {
|
||||
return 1.0;
|
||||
}
|
||||
getEffectiveLengthTiles() {
|
||||
return this.direction === enumDirection.top ? 1.0 : curvedBeltLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fake acceptor slot used for matching
|
||||
* @returns {import("./item_acceptor").ItemAcceptorSlot}
|
||||
*/
|
||||
getFakeAcceptorSlot() {
|
||||
return FAKE_BELT_ACCEPTOR_SLOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fake acceptor slot used for matching
|
||||
* @returns {import("./item_ejector").ItemEjectorSlot}
|
||||
*/
|
||||
getFakeEjectorSlot() {
|
||||
assert(
|
||||
FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION[this.direction],
|
||||
"Invalid belt direction: ",
|
||||
this.direction
|
||||
);
|
||||
return FAKE_BELT_EJECTOR_SLOT_BY_DIRECTION[this.direction];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from belt space (0 = start of belt ... 1 = end of belt) to the local
|
||||
* belt coordinates (-0.5|-0.5 to 0.5|0.5)
|
||||
* @param {number} progress
|
||||
* @param {enumLayer} layer
|
||||
* @returns {Vector}
|
||||
*/
|
||||
transformBeltToLocalSpace(progress, layer) {
|
||||
transformBeltToLocalSpace(progress) {
|
||||
assert(progress >= 0.0, "Invalid progress ( < 0): " + progress);
|
||||
switch (this.direction) {
|
||||
case enumDirection.top:
|
||||
assert(progress <= 1.02, "Invalid progress: " + progress);
|
||||
return new Vector(0, 0.5 - progress);
|
||||
|
||||
switch (layer) {
|
||||
case enumLayer.regular: {
|
||||
switch (this.direction) {
|
||||
case enumDirection.top:
|
||||
assert(progress <= 1.02, "Invalid progress: " + progress);
|
||||
return new Vector(0, 0.5 - progress);
|
||||
|
||||
case enumDirection.right: {
|
||||
assert(progress <= curvedBeltLength + 0.02, "Invalid progress 2: " + progress);
|
||||
const arcProgress = (progress / curvedBeltLength) * 0.5 * Math.PI;
|
||||
return new Vector(
|
||||
0.5 - 0.5 * Math.cos(arcProgress),
|
||||
0.5 - 0.5 * Math.sin(arcProgress)
|
||||
);
|
||||
}
|
||||
case enumDirection.left: {
|
||||
assert(progress <= curvedBeltLength + 0.02, "Invalid progress 3: " + progress);
|
||||
const arcProgress = (progress / curvedBeltLength) * 0.5 * Math.PI;
|
||||
return new Vector(
|
||||
-0.5 + 0.5 * Math.cos(arcProgress),
|
||||
0.5 - 0.5 * Math.sin(arcProgress)
|
||||
);
|
||||
}
|
||||
default:
|
||||
assertAlways(false, "Invalid belt direction: " + this.direction);
|
||||
return new Vector(0, 0);
|
||||
}
|
||||
case enumDirection.right: {
|
||||
assert(progress <= curvedBeltLength + 0.02, "Invalid progress 2: " + progress);
|
||||
const arcProgress = (progress / curvedBeltLength) * 0.5 * Math.PI;
|
||||
return new Vector(0.5 - 0.5 * Math.cos(arcProgress), 0.5 - 0.5 * Math.sin(arcProgress));
|
||||
}
|
||||
case enumLayer.wires: {
|
||||
const pow = 0.5;
|
||||
switch (this.direction) {
|
||||
case enumDirection.top:
|
||||
assert(progress <= 1.02, "Invalid progress: " + progress);
|
||||
return new Vector(0, 0.5 - progress);
|
||||
|
||||
case enumDirection.right: {
|
||||
assert(progress <= 1.02, "Invalid progress 2: " + progress);
|
||||
return progress > 0.5 ? new Vector(progress - 0.5, 0) : new Vector(0, 0.5 - progress);
|
||||
}
|
||||
case enumDirection.left: {
|
||||
assert(progress <= 1.02, "Invalid progress 3: " + progress);
|
||||
return progress > 0.5
|
||||
? new Vector(-progress + 0.5, 0)
|
||||
: new Vector(0, 0.5 - progress);
|
||||
}
|
||||
default:
|
||||
assertAlways(false, "Invalid belt direction: " + this.direction);
|
||||
return new Vector(0, 0);
|
||||
}
|
||||
case enumDirection.left: {
|
||||
assert(progress <= curvedBeltLength + 0.02, "Invalid progress 3: " + progress);
|
||||
const arcProgress = (progress / curvedBeltLength) * 0.5 * Math.PI;
|
||||
return new Vector(-0.5 + 0.5 * Math.cos(arcProgress), 0.5 - 0.5 * Math.sin(arcProgress));
|
||||
}
|
||||
default:
|
||||
assertAlways(false, "Invalid belt direction: " + this.direction);
|
||||
return new Vector(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
src/js/game/components/belt_underlays.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { Component } from "../component";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
|
||||
export class BeltUnderlaysComponent extends Component {
|
||||
static getId() {
|
||||
return "BeltUnderlays";
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
const beltUnderlaysCopy = [];
|
||||
for (let i = 0; i < this.underlays.length; ++i) {
|
||||
const underlay = this.underlays[i];
|
||||
beltUnderlaysCopy.push({
|
||||
pos: underlay.pos.copy(),
|
||||
direction: underlay.direction,
|
||||
});
|
||||
}
|
||||
|
||||
return new BeltUnderlaysComponent({
|
||||
underlays: beltUnderlaysCopy,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} param0
|
||||
* @param {Array<{pos: Vector, direction: enumDirection}>=} param0.underlays Where to render belt underlays
|
||||
*/
|
||||
constructor({ underlays }) {
|
||||
super();
|
||||
this.underlays = underlays;
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
import { Component } from "../component";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { Vector } from "../../core/vector";
|
||||
import { BaseItem, enumItemTypeToLayer, enumItemType } from "../base_item";
|
||||
|
||||
export class EnergyConsumerComponent extends Component {
|
||||
static getId() {
|
||||
return "EnergyConsumer";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
bufferSize: types.float,
|
||||
perCharge: types.float,
|
||||
batteryPosition: types.vector,
|
||||
energyType: types.enum(enumItemType),
|
||||
wasteType: types.enum(enumItemType),
|
||||
acceptorSlotIndex: types.uint,
|
||||
ejectorSlotIndex: types.uint,
|
||||
|
||||
stored: types.float,
|
||||
piledOutput: types.float,
|
||||
};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new EnergyConsumerComponent({
|
||||
bufferSize: this.bufferSize,
|
||||
perCharge: this.perCharge,
|
||||
batteryPosition: this.batteryPosition.copy(),
|
||||
acceptorSlotIndex: this.acceptorSlotIndex,
|
||||
ejectorSlotIndex: this.ejectorSlotIndex,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
* @param {number} param0.bufferSize How much energy this consumer can store
|
||||
* @param {number} param0.perCharge How much energy this consumer needs per charge
|
||||
* @param {Vector} param0.batteryPosition world space render offset of the battery icon
|
||||
* @param {number} param0.acceptorSlotIndex Which slot to accept energy on
|
||||
* @param {number} param0.ejectorSlotIndex Which slot to eject energy off
|
||||
*
|
||||
*/
|
||||
constructor({
|
||||
bufferSize = 3,
|
||||
perCharge = 1,
|
||||
batteryPosition = new Vector(),
|
||||
acceptorSlotIndex = 0,
|
||||
ejectorSlotIndex = 0,
|
||||
}) {
|
||||
super();
|
||||
this.bufferSize = bufferSize;
|
||||
this.perCharge = perCharge;
|
||||
this.batteryPosition = batteryPosition;
|
||||
this.energyType = enumItemType.positiveEnergy;
|
||||
this.wasteType = enumItemType.negativeEnergy;
|
||||
this.acceptorSlotIndex = acceptorSlotIndex;
|
||||
this.ejectorSlotIndex = ejectorSlotIndex;
|
||||
|
||||
/**
|
||||
* How much energy we have stored right now
|
||||
*/
|
||||
this.stored = 0;
|
||||
|
||||
/**
|
||||
* How much waste we have piled up so far
|
||||
*/
|
||||
this.piledOutput = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to accept a given item
|
||||
* @param {BaseItem} item
|
||||
* @param {number} slotIndex
|
||||
*/
|
||||
tryAcceptItem(item, slotIndex) {
|
||||
if (slotIndex !== this.acceptorSlotIndex) {
|
||||
// Wrong slot
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.getItemType() !== this.energyType) {
|
||||
// Not the right type
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.stored >= this.bufferSize) {
|
||||
// We are full
|
||||
return false;
|
||||
}
|
||||
|
||||
// All good, consume
|
||||
this.stored = Math.min(this.stored + 1, this.bufferSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to start the next charge
|
||||
*/
|
||||
tryStartNextCharge() {
|
||||
if (this.hasTooMuchWastePiled()) {
|
||||
// Too much waste remaining
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.stored < this.perCharge) {
|
||||
// Not enough energy stored
|
||||
return false;
|
||||
}
|
||||
|
||||
this.stored -= this.perCharge;
|
||||
this.piledOutput += this.perCharge;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if there is too much waste piled
|
||||
*/
|
||||
hasTooMuchWastePiled() {
|
||||
return this.piledOutput >= 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces the waste by the given amount
|
||||
* @param {number} amount
|
||||
*/
|
||||
reduceWaste(amount) {
|
||||
this.piledOutput = Math.max(0, this.piledOutput - amount);
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { BaseItem, enumItemType } from "../base_item";
|
||||
import { Component } from "../component";
|
||||
import { ShapeItem } from "../items/shape_item";
|
||||
|
||||
const maxQueueSize = 4;
|
||||
|
||||
export class EnergyGeneratorComponent extends Component {
|
||||
static getId() {
|
||||
return "EnergyGenerator";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
requiredKey: types.nullable(types.string),
|
||||
itemsInQueue: types.uint,
|
||||
wasteAcceptorSlotIndex: types.uint,
|
||||
};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new EnergyGeneratorComponent({
|
||||
requiredKey: null,
|
||||
wasteAcceptorSlotIndex: this.wasteAcceptorSlotIndex,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
* @param {string=} param0.requiredKey Which shape this generator needs, can be null if not computed yet
|
||||
* @param {number} param0.wasteAcceptorSlotIndex Which slot accepts the waste
|
||||
*/
|
||||
constructor({ requiredKey, wasteAcceptorSlotIndex = 0 }) {
|
||||
super();
|
||||
this.requiredKey = requiredKey;
|
||||
|
||||
/**
|
||||
* Stores how many items are ready to be converted to energy
|
||||
* @type {number}
|
||||
*/
|
||||
this.itemsInQueue = 0;
|
||||
|
||||
/**
|
||||
* Stores which slot accepts the waste
|
||||
* @type {number}
|
||||
*/
|
||||
this.wasteAcceptorSlotIndex = wasteAcceptorSlotIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {BaseItem} item
|
||||
* @param {number} slot
|
||||
*/
|
||||
tryTakeItem(item, slot) {
|
||||
if (slot === this.wasteAcceptorSlotIndex) {
|
||||
// this is the acceptor slot on the wires layer
|
||||
// just destroy it
|
||||
return true;
|
||||
} else {
|
||||
if (item.getItemType() !== enumItemType.shape) {
|
||||
// This shouldn't happen since we have a filter - still, it doesn't hurt
|
||||
// to check either
|
||||
assertAlways(
|
||||
false,
|
||||
"Energy generator took wrong item: " +
|
||||
item.getItemType() +
|
||||
" on slot " +
|
||||
slot +
|
||||
" (waste slot = " +
|
||||
this.wasteAcceptorSlotIndex +
|
||||
")"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/** @type {ShapeItem} */ (item).definition.getHash() !== this.requiredKey) {
|
||||
// Not our shape
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.itemsInQueue >= maxQueueSize) {
|
||||
// Queue is full
|
||||
return false;
|
||||
}
|
||||
|
||||
// Take item and put it into the queue
|
||||
++this.itemsInQueue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +1,7 @@
|
||||
import { Component } from "../component";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
import { types } from "../../savegame/serialization";
|
||||
|
||||
export class HubComponent extends Component {
|
||||
static getId() {
|
||||
return "Hub";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
definitionsToAnalyze: types.array(types.knownType(ShapeDefinition)),
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
/**
|
||||
* Shape definitions in queue to be analyzed and counted towards the goal
|
||||
* @type {Array<ShapeDefinition>}
|
||||
*/
|
||||
this.definitionsToAnalyze = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ShapeDefinition} definition
|
||||
*/
|
||||
queueShapeDefinition(definition) {
|
||||
this.definitionsToAnalyze.push(definition);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,10 @@ import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { BaseItem, enumItemType } from "../base_item";
|
||||
import { Component } from "../component";
|
||||
import { enumLayer } from "../root";
|
||||
|
||||
/** @typedef {{
|
||||
* pos: Vector,
|
||||
* directions: enumDirection[],
|
||||
* layer: enumLayer,
|
||||
* filter?: enumItemType
|
||||
* }} ItemAcceptorSlot */
|
||||
|
||||
@ -22,7 +20,6 @@ import { enumLayer } from "../root";
|
||||
/** @typedef {{
|
||||
* pos: Vector,
|
||||
* directions: enumDirection[],
|
||||
* layer?: enumLayer,
|
||||
* filter?: enumItemType
|
||||
* }} ItemAcceptorSlotConfig */
|
||||
|
||||
@ -31,31 +28,6 @@ export class ItemAcceptorComponent extends Component {
|
||||
return "ItemAcceptor";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
slots: types.array(
|
||||
types.structured({
|
||||
pos: types.vector,
|
||||
directions: types.array(types.enum(enumDirection)),
|
||||
filter: types.nullable(types.enum(enumItemType)),
|
||||
|
||||
// TODO: MIGRATE
|
||||
layer: types.enum(enumLayer),
|
||||
})
|
||||
),
|
||||
animated: types.bool,
|
||||
beltUnderlays: types.array(
|
||||
types.structured({
|
||||
pos: types.vector,
|
||||
direction: types.enum(enumDirection),
|
||||
|
||||
// TODO: MIGRATE
|
||||
layer: types.enum(enumLayer),
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
const slotsCopy = [];
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
@ -64,24 +36,11 @@ export class ItemAcceptorComponent extends Component {
|
||||
pos: slot.pos.copy(),
|
||||
directions: slot.directions.slice(),
|
||||
filter: slot.filter,
|
||||
layer: slot.layer,
|
||||
});
|
||||
}
|
||||
|
||||
const beltUnderlaysCopy = [];
|
||||
for (let i = 0; i < this.beltUnderlays.length; ++i) {
|
||||
const underlay = this.beltUnderlays[i];
|
||||
beltUnderlaysCopy.push({
|
||||
pos: underlay.pos.copy(),
|
||||
direction: underlay.direction,
|
||||
layer: underlay.layer,
|
||||
});
|
||||
}
|
||||
|
||||
return new ItemAcceptorComponent({
|
||||
slots: slotsCopy,
|
||||
beltUnderlays: beltUnderlaysCopy,
|
||||
animated: this.animated,
|
||||
});
|
||||
}
|
||||
|
||||
@ -89,23 +48,16 @@ export class ItemAcceptorComponent extends Component {
|
||||
*
|
||||
* @param {object} param0
|
||||
* @param {Array<ItemAcceptorSlotConfig>} param0.slots The slots from which we accept items
|
||||
* @param {boolean=} param0.animated Whether to animate item consumption
|
||||
* @param {Array<{pos: Vector, direction: enumDirection, layer: enumLayer}>=} param0.beltUnderlays Where to render belt underlays
|
||||
*/
|
||||
constructor({ slots = [], beltUnderlays = [], animated = true }) {
|
||||
constructor({ slots = [] }) {
|
||||
super();
|
||||
|
||||
this.animated = animated;
|
||||
|
||||
/**
|
||||
* Fixes belt animations
|
||||
* @type {Array<{ item: BaseItem, slotIndex: number, animProgress: number, direction: enumDirection }>}
|
||||
*/
|
||||
this.itemConsumptionAnimations = [];
|
||||
|
||||
/* Which belt underlays to render */
|
||||
this.beltUnderlays = beltUnderlays;
|
||||
|
||||
this.setSlots(slots);
|
||||
}
|
||||
|
||||
@ -121,7 +73,6 @@ export class ItemAcceptorComponent extends Component {
|
||||
this.slots.push({
|
||||
pos: slot.pos,
|
||||
directions: slot.directions,
|
||||
layer: slot.layer || enumLayer.regular,
|
||||
|
||||
// Which type of item to accept (shape | color | all) @see enumItemType
|
||||
filter: slot.filter,
|
||||
@ -164,24 +115,21 @@ export class ItemAcceptorComponent extends Component {
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
onItemAccepted(slotIndex, direction, item) {
|
||||
if (this.animated) {
|
||||
this.itemConsumptionAnimations.push({
|
||||
item,
|
||||
slotIndex,
|
||||
direction,
|
||||
animProgress: 0.0,
|
||||
});
|
||||
}
|
||||
this.itemConsumptionAnimations.push({
|
||||
item,
|
||||
slotIndex,
|
||||
direction,
|
||||
animProgress: 0.0,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find a slot which accepts the current item
|
||||
* @param {Vector} targetLocalTile
|
||||
* @param {enumDirection} fromLocalDirection
|
||||
* @param {enumLayer} layer
|
||||
* @returns {ItemAcceptorLocatedSlot|null}
|
||||
*/
|
||||
findMatchingSlot(targetLocalTile, fromLocalDirection, layer) {
|
||||
findMatchingSlot(targetLocalTile, fromLocalDirection) {
|
||||
// We need to invert our direction since the acceptor specifies *from* which direction
|
||||
// it accepts items, but the ejector specifies *into* which direction it ejects items.
|
||||
// E.g.: Ejector ejects into "right" direction but acceptor accepts from "left" direction.
|
||||
@ -196,11 +144,6 @@ export class ItemAcceptorComponent extends Component {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the layer matches
|
||||
if (slot.layer !== layer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the acceptor slot accepts items from our direction
|
||||
for (let i = 0; i < slot.directions.length; ++i) {
|
||||
// const localDirection = targetStaticComp.localDirectionToWorld(slot.directions[l]);
|
||||
|
@ -5,15 +5,16 @@ import { types } from "../../savegame/serialization";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
import { Entity } from "../entity";
|
||||
import { enumLayer } from "../root";
|
||||
import { BeltPath } from "../belt_path";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* pos: Vector,
|
||||
* direction: enumDirection,
|
||||
* item: BaseItem,
|
||||
* layer: enumLayer,
|
||||
* progress: number?,
|
||||
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
||||
* cachedBeltPath?: BeltPath,
|
||||
* cachedTargetEntity?: Entity
|
||||
* }} ItemEjectorSlot
|
||||
*/
|
||||
@ -24,19 +25,12 @@ export class ItemEjectorComponent extends Component {
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
// The cachedDestSlot, cachedTargetEntity, and cachedConnectedSlots fields
|
||||
// are not serialized.
|
||||
// The cachedDestSlot, cachedTargetEntity fields are not serialized.
|
||||
return {
|
||||
instantEject: types.bool,
|
||||
slots: types.array(
|
||||
types.structured({
|
||||
pos: types.vector,
|
||||
direction: types.enum(enumDirection),
|
||||
item: types.nullable(types.obj(gItemRegistry)),
|
||||
progress: types.float,
|
||||
|
||||
// TODO: Migrate
|
||||
layer: types.enum(enumLayer),
|
||||
})
|
||||
),
|
||||
};
|
||||
@ -49,33 +43,24 @@ export class ItemEjectorComponent extends Component {
|
||||
slotsCopy.push({
|
||||
pos: slot.pos.copy(),
|
||||
direction: slot.direction,
|
||||
layer: slot.layer,
|
||||
});
|
||||
}
|
||||
|
||||
return new ItemEjectorComponent({
|
||||
slots: slotsCopy,
|
||||
instantEject: this.instantEject,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
* @param {Array<{pos: Vector, direction: enumDirection, layer?: enumLayer}>=} param0.slots The slots to eject on
|
||||
* @param {boolean=} param0.instantEject If the ejection is instant
|
||||
* @param {Array<{pos: Vector, direction: enumDirection }>=} param0.slots The slots to eject on
|
||||
*/
|
||||
constructor({ slots = [], instantEject = false }) {
|
||||
constructor({ slots = [] }) {
|
||||
super();
|
||||
|
||||
// How long items take to eject
|
||||
this.instantEject = instantEject;
|
||||
|
||||
this.setSlots(slots);
|
||||
|
||||
/** @type {ItemEjectorSlot[]} */
|
||||
this.cachedConnectedSlots = null;
|
||||
|
||||
/**
|
||||
* Whether this ejector slot is enabled
|
||||
*/
|
||||
@ -83,7 +68,7 @@ export class ItemEjectorComponent extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<{pos: Vector, direction: enumDirection, layer?: enumLayer}>} slots The slots to eject on
|
||||
* @param {Array<{pos: Vector, direction: enumDirection }>} slots The slots to eject on
|
||||
*/
|
||||
setSlots(slots) {
|
||||
/** @type {Array<ItemEjectorSlot>} */
|
||||
@ -95,7 +80,6 @@ export class ItemEjectorComponent extends Component {
|
||||
direction: slot.direction,
|
||||
item: null,
|
||||
progress: 0,
|
||||
layer: slot.layer || enumLayer.regular,
|
||||
cachedDestSlot: null,
|
||||
cachedTargetEntity: null,
|
||||
});
|
||||
@ -104,11 +88,10 @@ export class ItemEjectorComponent extends Component {
|
||||
|
||||
/**
|
||||
* Returns where this slot ejects to
|
||||
* @param {number} index
|
||||
* @param {ItemEjectorSlot} slot
|
||||
* @returns {Vector}
|
||||
*/
|
||||
getSlotTargetLocalTile(index) {
|
||||
const slot = this.slots[index];
|
||||
getSlotTargetLocalTile(slot) {
|
||||
const directionVector = enumDirectionToVector[slot.direction];
|
||||
return slot.pos.add(directionVector);
|
||||
}
|
||||
@ -116,11 +99,10 @@ export class ItemEjectorComponent extends Component {
|
||||
/**
|
||||
* Returns whether any slot ejects to the given local tile
|
||||
* @param {Vector} tile
|
||||
* @param {enumLayer} layer
|
||||
*/
|
||||
anySlotEjectsToLocalTile(tile, layer) {
|
||||
anySlotEjectsToLocalTile(tile) {
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
if (this.getSlotTargetLocalTile(i).equals(tile) && this.slots[i].layer === layer) {
|
||||
if (this.getSlotTargetLocalTile(this.slots[i]).equals(tile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -139,12 +121,11 @@ export class ItemEjectorComponent extends Component {
|
||||
|
||||
/**
|
||||
* Returns the first free slot on this ejector or null if there is none
|
||||
* @param {enumLayer} layer
|
||||
* @returns {number?}
|
||||
*/
|
||||
getFirstFreeSlot(layer) {
|
||||
getFirstFreeSlot() {
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
if (this.canEjectOnSlot(i) && this.slots[i].layer === layer) {
|
||||
if (this.canEjectOnSlot(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -162,7 +143,7 @@ export class ItemEjectorComponent extends Component {
|
||||
return false;
|
||||
}
|
||||
this.slots[slotIndex].item = item;
|
||||
this.slots[slotIndex].progress = this.instantEject ? 1 : 0;
|
||||
this.slots[slotIndex].progress = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ export const enumItemProcessorTypes = {
|
||||
painter: "painter",
|
||||
painterDouble: "painterDouble",
|
||||
painterQuad: "painterQuad",
|
||||
advancedProcessor: "advancedProcessor",
|
||||
hub: "hub",
|
||||
};
|
||||
|
||||
@ -30,9 +29,6 @@ export class ItemProcessorComponent extends Component {
|
||||
static getSchema() {
|
||||
return {
|
||||
nextOutputSlot: types.uint,
|
||||
type: types.enum(enumItemProcessorTypes),
|
||||
inputsPerCharge: types.uint,
|
||||
|
||||
inputSlots: types.array(
|
||||
types.structured({
|
||||
item: types.obj(gItemRegistry),
|
||||
|
@ -15,7 +15,6 @@ export class MinerComponent extends Component {
|
||||
// cachedMinedItem is not serialized.
|
||||
return {
|
||||
lastMiningTime: types.ufloat,
|
||||
chainable: types.bool,
|
||||
itemChainBuffer: types.array(types.obj(gItemRegistry)),
|
||||
};
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
import { Component } from "../component";
|
||||
|
||||
/**
|
||||
* Marks an entity as replaceable, so that when other buildings are placed above him it
|
||||
* simply gets deleted
|
||||
*/
|
||||
export class ReplaceableMapEntityComponent extends Component {
|
||||
static getId() {
|
||||
return "ReplaceableMapEntity";
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new ReplaceableMapEntityComponent();
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import { enumDirection, Vector } from "../../core/vector";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { Component } from "../component";
|
||||
import { getBuildingDataFromCode } from "../building_codes";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
|
||||
export class StaticMapEntityComponent extends Component {
|
||||
static getId() {
|
||||
@ -15,7 +16,6 @@ export class StaticMapEntityComponent extends Component {
|
||||
static getSchema() {
|
||||
return {
|
||||
origin: types.tileVector,
|
||||
tileSize: types.tileVector,
|
||||
rotation: types.float,
|
||||
originalRotation: types.float,
|
||||
|
||||
@ -24,6 +24,14 @@ export class StaticMapEntityComponent extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective tile size
|
||||
* @returns {Vector}
|
||||
*/
|
||||
getTileSize() {
|
||||
return getBuildingDataFromCode(this.code).tileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sprite
|
||||
* @returns {AtlasSprite}
|
||||
@ -48,10 +56,17 @@ export class StaticMapEntityComponent extends Component {
|
||||
return getBuildingDataFromCode(this.code).silhouetteColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the meta building
|
||||
* @returns {MetaBuilding}
|
||||
*/
|
||||
getMetaBuilding() {
|
||||
return getBuildingDataFromCode(this.code).metaInstance;
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new StaticMapEntityComponent({
|
||||
origin: this.origin.copy(),
|
||||
tileSize: this.tileSize.copy(),
|
||||
rotation: this.rotation,
|
||||
originalRotation: this.originalRotation,
|
||||
code: this.code,
|
||||
@ -81,7 +96,6 @@ export class StaticMapEntityComponent extends Component {
|
||||
);
|
||||
|
||||
this.origin = origin;
|
||||
this.tileSize = tileSize;
|
||||
this.rotation = rotation;
|
||||
this.code = code;
|
||||
this.originalRotation = originalRotation;
|
||||
@ -92,30 +106,16 @@ export class StaticMapEntityComponent extends Component {
|
||||
* @returns {Rectangle}
|
||||
*/
|
||||
getTileSpaceBounds() {
|
||||
const size = this.getTileSize();
|
||||
switch (this.rotation) {
|
||||
case 0:
|
||||
return new Rectangle(this.origin.x, this.origin.y, this.tileSize.x, this.tileSize.y);
|
||||
return new Rectangle(this.origin.x, this.origin.y, size.x, size.y);
|
||||
case 90:
|
||||
return new Rectangle(
|
||||
this.origin.x - this.tileSize.y + 1,
|
||||
this.origin.y,
|
||||
this.tileSize.y,
|
||||
this.tileSize.x
|
||||
);
|
||||
return new Rectangle(this.origin.x - size.y + 1, this.origin.y, size.y, size.x);
|
||||
case 180:
|
||||
return new Rectangle(
|
||||
this.origin.x - this.tileSize.x + 1,
|
||||
this.origin.y - this.tileSize.y + 1,
|
||||
this.tileSize.x,
|
||||
this.tileSize.y
|
||||
);
|
||||
return new Rectangle(this.origin.x - size.x + 1, this.origin.y - size.y + 1, size.x, size.y);
|
||||
case 270:
|
||||
return new Rectangle(
|
||||
this.origin.x,
|
||||
this.origin.y - this.tileSize.x + 1,
|
||||
this.tileSize.y,
|
||||
this.tileSize.x
|
||||
);
|
||||
return new Rectangle(this.origin.x, this.origin.y - size.x + 1, size.y, size.x);
|
||||
default:
|
||||
assert(false, "Invalid rotation");
|
||||
}
|
||||
@ -186,34 +186,35 @@ export class StaticMapEntityComponent extends Component {
|
||||
let y = 0;
|
||||
let w = 0;
|
||||
let h = 0;
|
||||
const size = this.getTileSize();
|
||||
|
||||
switch (this.rotation) {
|
||||
case 0: {
|
||||
x = this.origin.x;
|
||||
y = this.origin.y;
|
||||
w = this.tileSize.x;
|
||||
h = this.tileSize.y;
|
||||
w = size.x;
|
||||
h = size.y;
|
||||
break;
|
||||
}
|
||||
case 90: {
|
||||
x = this.origin.x - this.tileSize.y + 1;
|
||||
x = this.origin.x - size.y + 1;
|
||||
y = this.origin.y;
|
||||
w = this.tileSize.y;
|
||||
h = this.tileSize.x;
|
||||
w = size.y;
|
||||
h = size.x;
|
||||
break;
|
||||
}
|
||||
case 180: {
|
||||
x = this.origin.x - this.tileSize.x + 1;
|
||||
y = this.origin.y - this.tileSize.y + 1;
|
||||
w = this.tileSize.x;
|
||||
h = this.tileSize.y;
|
||||
x = this.origin.x - size.x + 1;
|
||||
y = this.origin.y - size.y + 1;
|
||||
w = size.x;
|
||||
h = size.y;
|
||||
break;
|
||||
}
|
||||
case 270: {
|
||||
x = this.origin.x;
|
||||
y = this.origin.y - this.tileSize.x + 1;
|
||||
w = this.tileSize.y;
|
||||
h = this.tileSize.x;
|
||||
y = this.origin.y - size.x + 1;
|
||||
w = size.y;
|
||||
h = size.x;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -233,19 +234,13 @@ export class StaticMapEntityComponent extends Component {
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {AtlasSprite} sprite
|
||||
* @param {number=} extrudePixels How many pixels to extrude the sprite
|
||||
* @param {boolean=} clipping Whether to clip
|
||||
* @param {Vector=} overridePosition Whether to drwa the entity at a different location
|
||||
*/
|
||||
drawSpriteOnFullEntityBounds(
|
||||
parameters,
|
||||
sprite,
|
||||
extrudePixels = 0,
|
||||
clipping = true,
|
||||
overridePosition = null
|
||||
) {
|
||||
drawSpriteOnFullEntityBounds(parameters, sprite, extrudePixels = 0, overridePosition = null) {
|
||||
if (!this.shouldBeDrawn(parameters) && !overridePosition) {
|
||||
return;
|
||||
}
|
||||
const size = this.getTileSize();
|
||||
let worldX = this.origin.x * globalConfig.tileSize;
|
||||
let worldY = this.origin.y * globalConfig.tileSize;
|
||||
|
||||
@ -258,10 +253,10 @@ export class StaticMapEntityComponent extends Component {
|
||||
// Early out, is faster
|
||||
sprite.drawCached(
|
||||
parameters,
|
||||
worldX - extrudePixels * this.tileSize.x,
|
||||
worldY - extrudePixels * this.tileSize.y,
|
||||
globalConfig.tileSize * this.tileSize.x + 2 * extrudePixels * this.tileSize.x,
|
||||
globalConfig.tileSize * this.tileSize.y + 2 * extrudePixels * this.tileSize.y,
|
||||
worldX - extrudePixels * size.x,
|
||||
worldY - extrudePixels * size.y,
|
||||
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
|
||||
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
@ -273,10 +268,10 @@ export class StaticMapEntityComponent extends Component {
|
||||
|
||||
sprite.drawCached(
|
||||
parameters,
|
||||
-globalConfig.halfTileSize - extrudePixels * this.tileSize.x,
|
||||
-globalConfig.halfTileSize - extrudePixels * this.tileSize.y,
|
||||
globalConfig.tileSize * this.tileSize.x + 2 * extrudePixels * this.tileSize.x,
|
||||
globalConfig.tileSize * this.tileSize.y + 2 * extrudePixels * this.tileSize.y,
|
||||
-globalConfig.halfTileSize - extrudePixels * size.x,
|
||||
-globalConfig.halfTileSize - extrudePixels * size.y,
|
||||
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
|
||||
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y,
|
||||
false
|
||||
);
|
||||
|
||||
|
@ -12,10 +12,8 @@ export class StorageComponent extends Component {
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
maximumStorage: types.uint,
|
||||
storedCount: types.uint,
|
||||
storedItem: types.nullable(types.obj(gItemRegistry)),
|
||||
overlayOpacity: types.ufloat,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -26,9 +26,7 @@ export class UndergroundBeltComponent extends Component {
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
mode: types.enum(enumUndergroundBeltMode),
|
||||
pendingItems: types.array(types.pair(types.obj(gItemRegistry), types.float)),
|
||||
tier: types.uint,
|
||||
};
|
||||
}
|
||||
|
||||
@ -103,8 +101,7 @@ export class UndergroundBeltComponent extends Component {
|
||||
}
|
||||
|
||||
// Notice: We assume that for all items the travel distance is the same
|
||||
const maxItemsInTunnel =
|
||||
(2 + travelDistance) / globalConfig.beltItemSpacingByLayer[enumLayer.regular];
|
||||
const maxItemsInTunnel = (2 + travelDistance) / globalConfig.itemSpacingOnBelts;
|
||||
if (this.pendingItems.length >= maxItemsInTunnel) {
|
||||
// Simulate a real belt which gets full at some point
|
||||
return false;
|
||||
@ -114,8 +111,7 @@ export class UndergroundBeltComponent extends Component {
|
||||
// This corresponds to the item ejector - it needs 0.5 additional tiles to eject the item.
|
||||
// So instead of adding 1 we add 0.5 only.
|
||||
// Additionally it takes 1 tile for the acceptor which we just add on top.
|
||||
const travelDuration =
|
||||
(travelDistance + 1.5) / beltSpeed / globalConfig.beltItemSpacingByLayer[enumLayer.regular];
|
||||
const travelDuration = (travelDistance + 1.5) / beltSpeed / globalConfig.itemSpacingOnBelts;
|
||||
|
||||
this.pendingItems.push([item, travelDuration]);
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
import { Component } from "../component";
|
||||
|
||||
export class UnremovableComponent extends Component {
|
||||
static getId() {
|
||||
return "Unremovable";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
return new UnremovableComponent();
|
||||
}
|
||||
}
|
@ -8,6 +8,8 @@ export const enumPinSlotType = {
|
||||
negativeEnergyEjector: "negativeEnergyEjector",
|
||||
positiveEnergyAcceptor: "positiveEnergyAcceptor",
|
||||
negativeEnergyAcceptor: "negativeEnergyAcceptor",
|
||||
logicalEjector: "logicalEjector",
|
||||
logicalAcceptor: "logicalAcceptor",
|
||||
};
|
||||
|
||||
/** @typedef {{
|
||||
@ -27,17 +29,6 @@ export class WiredPinsComponent extends Component {
|
||||
return "WiredPins";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
slots: types.array(
|
||||
types.structured({
|
||||
pos: types.vector,
|
||||
type: types.enum(enumPinSlotType),
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
|
@ -349,8 +349,9 @@ export class GameCore {
|
||||
// Gather context and save all state
|
||||
const context = root.context;
|
||||
context.save();
|
||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||
context.clearRect(0, 0, window.innerWidth * 3, window.innerHeight * 3);
|
||||
if (G_IS_DEV) {
|
||||
context.fillStyle = "#a10000";
|
||||
context.fillRect(0, 0, window.innerWidth * 3, window.innerHeight * 3);
|
||||
}
|
||||
|
||||
// Compute optimal zoom level and atlas scale
|
||||
@ -394,49 +395,36 @@ export class GameCore {
|
||||
// -----
|
||||
|
||||
// BG / Map Resources / Belt Backgrounds
|
||||
root.map.drawBackground(params);
|
||||
|
||||
if (!this.root.camera.getIsMapOverlayActive()) {
|
||||
root.map.drawBackground(params);
|
||||
|
||||
// Underlays for splitters / balancers
|
||||
systems.itemAcceptor.drawUnderlays(params, enumLayer.regular);
|
||||
systems.beltUnderlays.drawUnderlays(params);
|
||||
|
||||
// Belt items
|
||||
systems.belt.drawLayerBeltItems(params, enumLayer.regular);
|
||||
systems.belt.drawBeltItems(params);
|
||||
|
||||
// Items being ejected / accepted currently (animations)
|
||||
systems.itemEjector.drawLayer(params, enumLayer.regular);
|
||||
systems.itemAcceptor.drawLayer(params, enumLayer.regular);
|
||||
}
|
||||
systems.itemEjector.draw(params);
|
||||
systems.itemAcceptor.draw(params);
|
||||
|
||||
// Miner & Static map entities
|
||||
root.map.drawForeground(params);
|
||||
// Miner & Static map entities
|
||||
root.map.drawForeground(params);
|
||||
|
||||
if (!this.root.camera.getIsMapOverlayActive()) {
|
||||
// HUB Overlay
|
||||
systems.hub.draw(params);
|
||||
|
||||
// Energy generator overlay
|
||||
systems.energyGenerator.draw(params);
|
||||
|
||||
// Storage items
|
||||
systems.storage.draw(params);
|
||||
|
||||
// Energy consumer (Battery icons)
|
||||
systems.energyConsumer.draw(params);
|
||||
// Green wires overlay
|
||||
root.hud.parts.wiresOverlay.draw(params);
|
||||
} else {
|
||||
root.map.drawOverlay(params);
|
||||
}
|
||||
|
||||
// Green wires overlay (not within the if because it can fade)
|
||||
root.hud.parts.wiresOverlay.draw(params);
|
||||
|
||||
if (this.root.currentLayer === enumLayer.wires && !this.root.camera.getIsMapOverlayActive()) {
|
||||
// Belt sprites & Static map entities
|
||||
root.map.drawWiresLayer(params);
|
||||
|
||||
// Belt items as well as accepted / ejected items
|
||||
systems.belt.drawLayerBeltItems(params, enumLayer.wires);
|
||||
systems.itemEjector.drawLayer(params, enumLayer.wires);
|
||||
systems.itemAcceptor.drawLayer(params, enumLayer.wires);
|
||||
|
||||
// Static map entities
|
||||
root.map.drawWiresForegroundLayer(params);
|
||||
|
||||
// pins
|
||||
|