Merge branch 'master' of https://github.com/tobspr/shapez.io
@ -11,6 +11,7 @@ module.exports = {
|
|||||||
);
|
);
|
||||||
return commitHash.replace(/^\s+|\s+$/g, "");
|
return commitHash.replace(/^\s+|\s+$/g, "");
|
||||||
},
|
},
|
||||||
|
|
||||||
getAllResourceImages() {
|
getAllResourceImages() {
|
||||||
return glob
|
return glob
|
||||||
.sync("res/**/*.@(png|svg|jpg)", { cwd: ".." })
|
.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() {
|
getVersion() {
|
||||||
return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString());
|
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 = {
|
module.exports = {
|
||||||
|
@ -77,7 +77,6 @@ function gulptasksCSS($, gulp, buildFolder, browserSync) {
|
|||||||
.pipe($.plumber())
|
.pipe($.plumber())
|
||||||
.pipe($.sass.sync({ outputStyle: "compressed" }).on("error", $.sass.logError))
|
.pipe($.sass.sync({ outputStyle: "compressed" }).on("error", $.sass.logError))
|
||||||
.pipe($.postcss(postcssPlugins(true, { cachebust: true })))
|
.pipe($.postcss(postcssPlugins(true, { cachebust: true })))
|
||||||
// .pipe($.cssbeautify())
|
|
||||||
.pipe(gulp.dest(buildFolder))
|
.pipe(gulp.dest(buildFolder))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -90,7 +89,6 @@ function gulptasksCSS($, gulp, buildFolder, browserSync) {
|
|||||||
.pipe($.plumber())
|
.pipe($.plumber())
|
||||||
.pipe($.sass.sync({ outputStyle: "compressed" }).on("error", $.sass.logError))
|
.pipe($.sass.sync({ outputStyle: "compressed" }).on("error", $.sass.logError))
|
||||||
.pipe($.postcss(postcssPlugins(true, { cachebust: false })))
|
.pipe($.postcss(postcssPlugins(true, { cachebust: false })))
|
||||||
// .pipe($.cssbeautify())
|
|
||||||
.pipe(gulp.dest(buildFolder))
|
.pipe(gulp.dest(buildFolder))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
67
gulp/html.js
@ -9,7 +9,7 @@ function computeIntegrityHash(fullPath, algorithm = "sha256") {
|
|||||||
return algorithm + "-" + hash;
|
return algorithm + "-" + hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
function gulptasksHTML($, gulp, buildFolder) {
|
||||||
const commitHash = buildUtils.getRevision();
|
const commitHash = buildUtils.getRevision();
|
||||||
async function buildHtml(
|
async function buildHtml(
|
||||||
apiUrl,
|
apiUrl,
|
||||||
@ -27,9 +27,8 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
|||||||
return gulp
|
return gulp
|
||||||
.src("../src/html/" + (standalone ? "index.standalone.html" : "index.html"))
|
.src("../src/html/" + (standalone ? "index.standalone.html" : "index.html"))
|
||||||
.pipe(
|
.pipe(
|
||||||
$.dom(function () {
|
$.dom(/** @this {Document} **/ function () {
|
||||||
// @ts-ignore
|
const document = this;
|
||||||
const document = /** @type {Document} */ (this);
|
|
||||||
|
|
||||||
// Preconnect to api
|
// Preconnect to api
|
||||||
const prefetchLink = document.createElement("link");
|
const prefetchLink = document.createElement("link");
|
||||||
@ -38,21 +37,6 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
|||||||
prefetchLink.setAttribute("crossorigin", "anonymous");
|
prefetchLink.setAttribute("crossorigin", "anonymous");
|
||||||
document.head.appendChild(prefetchLink);
|
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
|
// Append css
|
||||||
const css = document.createElement("link");
|
const css = document.createElement("link");
|
||||||
css.rel = "stylesheet";
|
css.rel = "stylesheet";
|
||||||
@ -68,17 +52,6 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
|||||||
}
|
}
|
||||||
document.head.appendChild(css);
|
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) {
|
if (app) {
|
||||||
// Append cordova link
|
// Append cordova link
|
||||||
const cdv = document.createElement("script");
|
const cdv = document.createElement("script");
|
||||||
@ -114,18 +87,9 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
|||||||
|
|
||||||
// Do not need to preload in app or standalone
|
// Do not need to preload in app or standalone
|
||||||
if (!hasLocalFiles) {
|
if (!hasLocalFiles) {
|
||||||
// Preload images
|
|
||||||
const images = buildUtils.getAllResourceImages();
|
|
||||||
|
|
||||||
// Preload essentials
|
// Preload essentials
|
||||||
const preloads = ["fonts/GameFont.woff2"];
|
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 => {
|
preloads.forEach(src => {
|
||||||
const preloadLink = document.createElement("link");
|
const preloadLink = document.createElement("link");
|
||||||
preloadLink.rel = "preload";
|
preloadLink.rel = "preload";
|
||||||
@ -138,23 +102,6 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
|||||||
}
|
}
|
||||||
document.head.appendChild(preloadLink);
|
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+")`;
|
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;
|
font-display: swap;
|
||||||
src: url('${cachebust("res/fonts/GameFont.woff2")}') format('woff2');
|
src: url('${cachebust("res/fonts/GameFont.woff2")}') format('woff2');
|
||||||
}
|
}
|
||||||
|
|
||||||
#ll_fp {
|
#ll_fp {
|
||||||
font-family: GameFont;
|
font-family: GameFont;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@ -177,7 +124,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
|||||||
left: 0;
|
left: 0;
|
||||||
opacity: 0.05;
|
opacity: 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ll_p {
|
#ll_p {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -190,7 +137,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
|||||||
center;
|
center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ll_p > div {
|
#ll_p > div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -201,7 +148,7 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) {
|
|||||||
font-family: 'GameFont', sans-serif;
|
font-family: 'GameFont', sans-serif;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ll_p > span {
|
#ll_p > span {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
@ -6,12 +6,6 @@ function requireUncached(module) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function gulptasksJS($, gulp, buildFolder, browserSync) {
|
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
|
//// DEV
|
||||||
|
|
||||||
@ -71,6 +65,7 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
|
|||||||
gulp.task("js.staging", gulp.parallel("js.staging.transpiled", "js.staging.latest"));
|
gulp.task("js.staging", gulp.parallel("js.staging.transpiled", "js.staging.latest"));
|
||||||
|
|
||||||
//// PROD
|
//// PROD
|
||||||
|
|
||||||
gulp.task("js.prod.transpiled", () => {
|
gulp.task("js.prod.transpiled", () => {
|
||||||
return gulp
|
return gulp
|
||||||
.src("../src/js/main.js")
|
.src("../src/js/main.js")
|
||||||
@ -167,8 +162,6 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
|
|||||||
)
|
)
|
||||||
.pipe(gulp.dest(buildFolder));
|
.pipe(gulp.dest(buildFolder));
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Tasks for te app
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "builder",
|
"description": "builder",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "main.js",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"gulp": "gulp"
|
"gulp": "gulp"
|
||||||
},
|
},
|
||||||
@ -41,7 +40,6 @@
|
|||||||
"lz-string": "^1.4.4",
|
"lz-string": "^1.4.4",
|
||||||
"markdown-loader": "^5.1.0",
|
"markdown-loader": "^5.1.0",
|
||||||
"node-sri": "^1.1.1",
|
"node-sri": "^1.1.1",
|
||||||
"obfuscator-loader": "^1.1.2",
|
|
||||||
"phonegap-plugin-mobile-accessibility": "^1.0.5",
|
"phonegap-plugin-mobile-accessibility": "^1.0.5",
|
||||||
"promise-polyfill": "^8.1.0",
|
"promise-polyfill": "^8.1.0",
|
||||||
"query-string": "^6.8.1",
|
"query-string": "^6.8.1",
|
||||||
@ -54,7 +52,6 @@
|
|||||||
"uglify-template-string-loader": "^1.1.0",
|
"uglify-template-string-loader": "^1.1.0",
|
||||||
"unused-files-webpack-plugin": "^3.4.0",
|
"unused-files-webpack-plugin": "^3.4.0",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^4.43.0",
|
||||||
"webpack-bundle-analyzer": "^3.0.3",
|
|
||||||
"webpack-cli": "^3.1.0",
|
"webpack-cli": "^3.1.0",
|
||||||
"webpack-deep-scope-plugin": "^1.6.0",
|
"webpack-deep-scope-plugin": "^1.6.0",
|
||||||
"webpack-plugin-replace": "^1.1.1",
|
"webpack-plugin-replace": "^1.1.1",
|
||||||
@ -76,8 +73,6 @@
|
|||||||
"gulp-cache": "^1.1.3",
|
"gulp-cache": "^1.1.3",
|
||||||
"gulp-cached": "^1.1.1",
|
"gulp-cached": "^1.1.1",
|
||||||
"gulp-clean": "^0.4.0",
|
"gulp-clean": "^0.4.0",
|
||||||
"gulp-cssbeautify": "^2.0.1",
|
|
||||||
"gulp-csslint": "^1.0.1",
|
|
||||||
"gulp-dom": "^1.0.0",
|
"gulp-dom": "^1.0.0",
|
||||||
"gulp-flatten": "^0.4.0",
|
"gulp-flatten": "^0.4.0",
|
||||||
"gulp-fluent-ffmpeg": "^2.0.0",
|
"gulp-fluent-ffmpeg": "^2.0.0",
|
||||||
@ -85,8 +80,6 @@
|
|||||||
"gulp-htmlmin": "^5.0.1",
|
"gulp-htmlmin": "^5.0.1",
|
||||||
"gulp-if": "^3.0.0",
|
"gulp-if": "^3.0.0",
|
||||||
"gulp-imagemin": "^7.1.0",
|
"gulp-imagemin": "^7.1.0",
|
||||||
"gulp-javascript-obfuscator": "^1.1.5",
|
|
||||||
"gulp-jsbeautifier": "^3.0.0",
|
|
||||||
"gulp-load-plugins": "^2.0.3",
|
"gulp-load-plugins": "^2.0.3",
|
||||||
"gulp-phonegap-build": "^0.1.5",
|
"gulp-phonegap-build": "^0.1.5",
|
||||||
"gulp-plumber": "^1.2.1",
|
"gulp-plumber": "^1.2.1",
|
||||||
@ -104,13 +97,11 @@
|
|||||||
"imagemin-pngquant": "^9.0.0",
|
"imagemin-pngquant": "^9.0.0",
|
||||||
"jimp": "^0.6.1",
|
"jimp": "^0.6.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"onesky-fetch": "^0.0.7",
|
|
||||||
"postcss-assets": "^5.0.0",
|
"postcss-assets": "^5.0.0",
|
||||||
"postcss-preset-env": "^6.5.0",
|
"postcss-preset-env": "^6.5.0",
|
||||||
"postcss-round-subpixels": "^1.2.0",
|
"postcss-round-subpixels": "^1.2.0",
|
||||||
"postcss-unprefix": "^2.1.3",
|
"postcss-unprefix": "^2.1.3",
|
||||||
"sass-unused": "^0.3.0",
|
"sass-unused": "^0.3.0",
|
||||||
"speed-measure-webpack-plugin": "^1.3.1",
|
|
||||||
"strip-json-comments": "^3.0.1",
|
"strip-json-comments": "^3.0.1",
|
||||||
"trim": "^0.0.1",
|
"trim": "^0.0.1",
|
||||||
"webpack-stream": "^5.2.1",
|
"webpack-stream": "^5.2.1",
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
const packager = require("electron-packager");
|
const packager = require("electron-packager");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const buildutils = require("./buildutils");
|
const { getVersion } = require("./buildutils");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const fse = require("fs-extra");
|
const fse = require("fs-extra");
|
||||||
const execSync = require("child_process").execSync;
|
const execSync = require("child_process").execSync;
|
||||||
|
|
||||||
function gulptasksStandalone($, gulp, buildFolder) {
|
function gulptasksStandalone($, gulp) {
|
||||||
const electronBaseDir = path.join(__dirname, "..", "electron");
|
const electronBaseDir = path.join(__dirname, "..", "electron");
|
||||||
|
|
||||||
const tempDestDir = path.join(__dirname, "..", "tmp_standalone_files");
|
const tempDestDir = path.join(__dirname, "..", "tmp_standalone_files");
|
||||||
@ -47,49 +47,7 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("standalone.prepare.minifyCode", () => {
|
gulp.task("standalone.prepare.minifyCode", () => {
|
||||||
return (
|
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
|
||||||
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))
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("standalone.prepare.copyGamefiles", () => {
|
gulp.task("standalone.prepare.copyGamefiles", () => {
|
||||||
@ -122,15 +80,14 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
|||||||
* @param {'win32'|'linux'|'darwin'} platform
|
* @param {'win32'|'linux'|'darwin'} platform
|
||||||
* @param {'x64'|'ia32'} arch
|
* @param {'x64'|'ia32'} arch
|
||||||
* @param {function():void} cb
|
* @param {function():void} cb
|
||||||
* @param {boolean=} isRelease
|
|
||||||
*/
|
*/
|
||||||
function packageStandalone(platform, arch, cb, isRelease = false) {
|
function packageStandalone(platform, arch, cb) {
|
||||||
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
|
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
|
||||||
|
|
||||||
packager({
|
packager({
|
||||||
dir: tempDestBuildDir,
|
dir: tempDestBuildDir,
|
||||||
appCopyright: "Tobias Springer",
|
appCopyright: "Tobias Springer",
|
||||||
appVersion: buildutils.getVersion(),
|
appVersion: getVersion(),
|
||||||
buildVersion: "1.0.0",
|
buildVersion: "1.0.0",
|
||||||
arch,
|
arch,
|
||||||
platform,
|
platform,
|
||||||
@ -164,19 +121,6 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
|||||||
'#!/usr/bin/env bash\n./shapezio --no-sandbox "$@"\n'
|
'#!/usr/bin/env bash\n./shapezio --no-sandbox "$@"\n'
|
||||||
);
|
);
|
||||||
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
|
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") {
|
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.win64", cb => packageStandalone("win32", "x64", cb));
|
||||||
gulp.task("standalone.package.prod.win32", cb => packageStandalone("win32", "ia32", cb, true));
|
gulp.task("standalone.package.prod.win32", cb => packageStandalone("win32", "ia32", cb));
|
||||||
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb, true));
|
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb));
|
||||||
gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb, true));
|
gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb));
|
||||||
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb, true));
|
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb));
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"standalone.package.prod",
|
"standalone.package.prod",
|
||||||
@ -240,8 +184,6 @@ function gulptasksStandalone($, gulp, buildFolder) {
|
|||||||
"standalone.package.prod.win64",
|
"standalone.package.prod.win64",
|
||||||
"standalone.package.prod.linux64",
|
"standalone.package.prod.linux64",
|
||||||
"standalone.package.prod.darwin64"
|
"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 translationsSourceDir = path.join(__dirname, "..", "translations");
|
||||||
const translationsJsonDir = path.join(__dirname, "..", "src", "js", "built-temp");
|
const translationsJsonDir = path.join(__dirname, "..", "src", "js", "built-temp");
|
||||||
|
|
||||||
function gulptasksTranslations($, gulp, buildFolder) {
|
function gulptasksTranslations($, gulp) {
|
||||||
gulp.task("translations.convertToJson", () => {
|
gulp.task("translations.convertToJson", () => {
|
||||||
return gulp
|
return gulp
|
||||||
.src(path.join(translationsSourceDir, "*.yaml"))
|
.src(path.join(translationsSourceDir, "*.yaml"))
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const utils = require("./buildutils");
|
const { getRevision, getVersion, getAllResourceImages } = require("./buildutils");
|
||||||
const lzString = require("lz-string");
|
const lzString = require("lz-string");
|
||||||
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
|
||||||
const CircularDependencyPlugin = require("circular-dependency-plugin");
|
const CircularDependencyPlugin = require("circular-dependency-plugin");
|
||||||
|
|
||||||
module.exports = ({ watch = false, standalone = false }) => {
|
module.exports = ({ watch = false, standalone = false }) => {
|
||||||
@ -41,9 +40,9 @@ module.exports = ({ watch = false, standalone = false }) => {
|
|||||||
G_IS_BROWSER: "true",
|
G_IS_BROWSER: "true",
|
||||||
G_IS_STANDALONE: standalone ? "true" : "false",
|
G_IS_STANDALONE: standalone ? "true" : "false",
|
||||||
G_BUILD_TIME: "" + new Date().getTime(),
|
G_BUILD_TIME: "" + new Date().getTime(),
|
||||||
G_BUILD_COMMIT_HASH: JSON.stringify(utils.getRevision()),
|
G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()),
|
||||||
G_BUILD_VERSION: JSON.stringify(utils.getVersion()),
|
G_BUILD_VERSION: JSON.stringify(getVersion()),
|
||||||
G_ALL_UI_IMAGES: JSON.stringify(utils.getAllResourceImages()),
|
G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new CircularDependencyPlugin({
|
new CircularDependencyPlugin({
|
||||||
@ -60,7 +59,6 @@ module.exports = ({ watch = false, standalone = false }) => {
|
|||||||
// set the current working directory for displaying module paths
|
// set the current working directory for displaying module paths
|
||||||
cwd: path.join(__dirname, "..", "src", "js"),
|
cwd: path.join(__dirname, "..", "src", "js"),
|
||||||
}),
|
}),
|
||||||
// new BundleAnalyzerPlugin()
|
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
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$/,
|
test: /\.ya?ml$/,
|
||||||
type: "json", // Required by Webpack v4
|
type: "json", // Required by Webpack v4
|
||||||
|
@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const utils = require("./buildutils");
|
const { getRevision, getVersion, getAllResourceImages } = require("./buildutils");
|
||||||
const lzString = require("lz-string");
|
const lzString = require("lz-string");
|
||||||
|
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
const StringReplacePlugin = require("string-replace-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 UnusedFilesPlugin = require("unused-files-webpack-plugin").UnusedFilesWebpackPlugin;
|
||||||
// const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
|
|
||||||
|
|
||||||
module.exports = ({
|
module.exports = ({
|
||||||
enableAssert = false,
|
enableAssert = false,
|
||||||
@ -34,9 +32,9 @@ module.exports = ({
|
|||||||
G_APP_ENVIRONMENT: JSON.stringify(environment),
|
G_APP_ENVIRONMENT: JSON.stringify(environment),
|
||||||
G_HAVE_ASSERT: enableAssert ? "true" : "false",
|
G_HAVE_ASSERT: enableAssert ? "true" : "false",
|
||||||
G_BUILD_TIME: "" + new Date().getTime(),
|
G_BUILD_TIME: "" + new Date().getTime(),
|
||||||
G_BUILD_COMMIT_HASH: JSON.stringify(utils.getRevision()),
|
G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()),
|
||||||
G_BUILD_VERSION: JSON.stringify(utils.getVersion()),
|
G_BUILD_VERSION: JSON.stringify(getVersion()),
|
||||||
G_ALL_UI_IMAGES: JSON.stringify(utils.getAllResourceImages()),
|
G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()),
|
||||||
};
|
};
|
||||||
|
|
||||||
const minifyNames = environment === "prod";
|
const minifyNames = environment === "prod";
|
||||||
@ -143,9 +141,9 @@ module.exports = ({
|
|||||||
ecma: es6 ? 6 : 5,
|
ecma: es6 ? 6 : 5,
|
||||||
preamble:
|
preamble:
|
||||||
"/* shapez.io Codebase - Copyright 2020 Tobias Springer - " +
|
"/* 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"),
|
cwd: path.join(__dirname, "..", "src", "js"),
|
||||||
patterns: ["../src/js/**/*.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: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
799
gulp/yarn.lock
@ -9,7 +9,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cd gulp && yarn gulp main.serveDev",
|
"dev": "cd gulp && yarn gulp main.serveDev",
|
||||||
"tslint": "cd src/js && tsc",
|
"tslint": "cd src/js && tsc",
|
||||||
"lint": "npx eslint src/js",
|
"lint": "eslint src/js",
|
||||||
"prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.*",
|
"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",
|
"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",
|
"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",
|
"clipboard-copy": "^3.1.0",
|
||||||
"colors": "^1.3.3",
|
"colors": "^1.3.3",
|
||||||
"core-js": "3",
|
"core-js": "3",
|
||||||
|
"crc": "^3.8.0",
|
||||||
"cssnano-preset-advanced": "^4.0.7",
|
"cssnano-preset-advanced": "^4.0.7",
|
||||||
"email-validator": "^2.0.4",
|
"email-validator": "^2.0.4",
|
||||||
"eslint": "7.1.0",
|
"eslint": "7.1.0",
|
||||||
@ -46,7 +47,6 @@
|
|||||||
"lz-string": "^1.4.4",
|
"lz-string": "^1.4.4",
|
||||||
"markdown-loader": "^4.0.0",
|
"markdown-loader": "^4.0.0",
|
||||||
"match-all": "^1.2.5",
|
"match-all": "^1.2.5",
|
||||||
"obfuscator-loader": "^1.1.2",
|
|
||||||
"phonegap-plugin-mobile-accessibility": "^1.0.5",
|
"phonegap-plugin-mobile-accessibility": "^1.0.5",
|
||||||
"promise-polyfill": "^8.1.0",
|
"promise-polyfill": "^8.1.0",
|
||||||
"query-string": "^6.8.1",
|
"query-string": "^6.8.1",
|
||||||
@ -86,14 +86,12 @@
|
|||||||
"imagemin-pngquant": "^8.0.0",
|
"imagemin-pngquant": "^8.0.0",
|
||||||
"jimp": "^0.6.1",
|
"jimp": "^0.6.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"onesky-fetch": "^0.0.7",
|
|
||||||
"postcss-assets": "^5.0.0",
|
"postcss-assets": "^5.0.0",
|
||||||
"postcss-preset-env": "^6.5.0",
|
"postcss-preset-env": "^6.5.0",
|
||||||
"postcss-round-subpixels": "^1.2.0",
|
"postcss-round-subpixels": "^1.2.0",
|
||||||
"postcss-unprefix": "^2.1.3",
|
"postcss-unprefix": "^2.1.3",
|
||||||
"prettier": "^2.0.4",
|
"prettier": "^2.0.4",
|
||||||
"sass-unused": "^0.3.0",
|
"sass-unused": "^0.3.0",
|
||||||
"speed-measure-webpack-plugin": "^1.3.1",
|
|
||||||
"strip-json-comments": "^3.0.1",
|
"strip-json-comments": "^3.0.1",
|
||||||
"trim": "^0.0.1",
|
"trim": "^0.0.1",
|
||||||
"yarn": "^1.22.4"
|
"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/rotater.png</key>
|
||||||
<key type="filename">sprites/buildings/trash.png</key>
|
<key type="filename">sprites/buildings/trash.png</key>
|
||||||
<key type="filename">sprites/misc/wires_overlay_tile.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">
|
<struct type="IndividualSpriteSettings">
|
||||||
<key>pivotPoint</key>
|
<key>pivotPoint</key>
|
||||||
<point_f>0.5,0.5</point_f>
|
<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_left.png</key>
|
||||||
<key type="filename">sprites/blueprints/belt_right.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/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_left.png</key>
|
||||||
<key type="filename">sprites/buildings/belt_right.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/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">
|
<struct type="IndividualSpriteSettings">
|
||||||
<key>pivotPoint</key>
|
<key>pivotPoint</key>
|
||||||
<point_f>0.5,0.5</point_f>
|
<point_f>0.5,0.5</point_f>
|
||||||
@ -385,28 +371,6 @@
|
|||||||
<key>scale9FromFile</key>
|
<key>scale9FromFile</key>
|
||||||
<false/>
|
<false/>
|
||||||
</struct>
|
</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/cutter-quad.png</key>
|
||||||
<key type="filename">sprites/blueprints/painter-quad.png</key>
|
<key type="filename">sprites/blueprints/painter-quad.png</key>
|
||||||
<key type="filename">sprites/buildings/cutter-quad.png</key>
|
<key type="filename">sprites/buildings/cutter-quad.png</key>
|
||||||
@ -481,6 +445,23 @@
|
|||||||
<key>scale9FromFile</key>
|
<key>scale9FromFile</key>
|
||||||
<false/>
|
<false/>
|
||||||
</struct>
|
</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/cutter.png</key>
|
||||||
<key type="filename">sprites/buildings/mixer.png</key>
|
<key type="filename">sprites/buildings/mixer.png</key>
|
||||||
<key type="filename">sprites/buildings/painter.png</key>
|
<key type="filename">sprites/buildings/painter.png</key>
|
||||||
@ -568,21 +549,6 @@
|
|||||||
<key>scale9FromFile</key>
|
<key>scale9FromFile</key>
|
||||||
<false/>
|
<false/>
|
||||||
</struct>
|
</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>
|
<key type="filename">sprites/misc/storage_overlay.png</key>
|
||||||
<struct type="IndividualSpriteSettings">
|
<struct type="IndividualSpriteSettings">
|
||||||
<key>pivotPoint</key>
|
<key>pivotPoint</key>
|
||||||
@ -598,27 +564,6 @@
|
|||||||
<key>scale9FromFile</key>
|
<key>scale9FromFile</key>
|
||||||
<false/>
|
<false/>
|
||||||
</struct>
|
</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>
|
</map>
|
||||||
<key>fileList</key>
|
<key>fileList</key>
|
||||||
<array>
|
<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 {
|
canvas {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
image-rendering: auto;
|
image-rendering: pixelated;
|
||||||
// &.smoothed {
|
// &.smoothed {
|
||||||
// }
|
// }
|
||||||
// &.unsmoothed {
|
// &.unsmoothed {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt,
|
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt;
|
||||||
energy_generator, wire, advanced_processor, wire_crossings;
|
|
||||||
|
|
||||||
@each $building in $buildings {
|
@each $building in $buildings {
|
||||||
[data-icon="building_icons/#{$building}.png"] {
|
[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 { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
||||||
import { AnalyticsInterface } from "./platform/analytics";
|
import { AnalyticsInterface } from "./platform/analytics";
|
||||||
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
||||||
import { NoGameAnalytics } from "./platform/browser/no_game_analytics";
|
|
||||||
import { SoundImplBrowser } from "./platform/browser/sound";
|
import { SoundImplBrowser } from "./platform/browser/sound";
|
||||||
import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper";
|
import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper";
|
||||||
import { PlatformWrapperImplElectron } from "./platform/electron/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 { PlatformWrapperInterface } from "./platform/wrapper";
|
||||||
import { ApplicationSettings } from "./profile/application_settings";
|
import { ApplicationSettings } from "./profile/application_settings";
|
||||||
import { SavegameManager } from "./savegame/savegame_manager";
|
import { SavegameManager } from "./savegame/savegame_manager";
|
||||||
@ -34,6 +30,12 @@ import { PreloadState } from "./states/preload";
|
|||||||
import { SettingsState } from "./states/settings";
|
import { SettingsState } from "./states/settings";
|
||||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
||||||
|
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
|
||||||
|
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
|
||||||
|
*/
|
||||||
|
|
||||||
const logger = createLogger("application");
|
const logger = createLogger("application");
|
||||||
|
|
||||||
// Set the name of the hidden property and the change event for visibility
|
// Set the name of the hidden property and the change event for visibility
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Signal } from "./signal";
|
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";
|
import { createLogger } from "./logging";
|
||||||
const logger = createLogger("animation_frame");
|
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 { createLogger } from "./logging";
|
||||||
|
import { round2Digits } from "./utils";
|
||||||
|
|
||||||
const logger = createLogger("async_compression");
|
const logger = createLogger("async_compression");
|
||||||
|
|
||||||
@ -50,7 +53,15 @@ class AsynCompression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const duration = performance.now() - jobData.startTime;
|
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;
|
const resolver = jobData.resolver;
|
||||||
delete this.currentJobs[jobId];
|
delete this.currentJobs[jobId];
|
||||||
resolver(result);
|
resolver(result);
|
||||||
@ -70,12 +81,13 @@ class AsynCompression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compresses file
|
* Compresses any object
|
||||||
* @param {string} text
|
* @param {any} obj
|
||||||
*/
|
*/
|
||||||
compressFileAsync(text) {
|
compressObjectAsync(obj) {
|
||||||
return this.internalQueueJob("compressFile", {
|
logger.log("Compressing object async (optimized)");
|
||||||
text,
|
return this.internalQueueJob("compressObject", {
|
||||||
|
obj,
|
||||||
compressionPrefix,
|
compressionPrefix,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -98,6 +110,8 @@ class AsynCompression {
|
|||||||
resolver: resolve,
|
resolver: resolve,
|
||||||
startTime: performance.now(),
|
startTime: performance.now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
logger.log("Posting job", job, "/", jobId);
|
||||||
this.worker.postMessage({ jobId, job, data });
|
this.worker.postMessage({ jobId, job, data });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,38 @@
|
|||||||
/**
|
/**
|
||||||
|
* @typedef {{ w: number, h: number }} Size
|
||||||
|
* @typedef {{ x: number, y: number }} Position
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* frame: { x: number, y: number, w: number, h: number },
|
* frame: Position & Size,
|
||||||
* rotated: false,
|
* rotated: boolean,
|
||||||
* spriteSourceSize: { x: number, y: number, w: number, h: number },
|
* spriteSourceSize: Position & Size,
|
||||||
* sourceSize: { w: number, h: number},
|
* sourceSize: Size,
|
||||||
* trimmed: true
|
* trimmed: boolean
|
||||||
* }} SpriteDefinition
|
* }} 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 {
|
export class AtlasDefinition {
|
||||||
constructor(sourceData) {
|
/**
|
||||||
this.sourceFileName = sourceData.meta.image;
|
* @param {SourceData} sourceData
|
||||||
this.meta = sourceData.meta;
|
*/
|
||||||
|
constructor({ frames, meta }) {
|
||||||
/** @type {Object.<string, SpriteDefinition>} */
|
this.meta = meta;
|
||||||
this.sourceData = sourceData.frames;
|
this.sourceData = frames;
|
||||||
|
this.sourceFileName = meta.image;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFullSourcePath() {
|
getFullSourcePath() {
|
||||||
@ -22,6 +40,7 @@ export class AtlasDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {AtlasDefinition[]} **/
|
||||||
export const atlasFiles = require
|
export const atlasFiles = require
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
.context("../../../res_built/atlas/", false, /.*\.json/i)
|
.context("../../../res_built/atlas/", false, /.*\.json/i)
|
||||||
|
@ -115,7 +115,6 @@ export class BackgroundResourcesLoader {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
logger.log("⏰ Finish load: bare game");
|
logger.log("⏰ Finish load: bare game");
|
||||||
Loader.createAtlasLinks();
|
|
||||||
this.bareGameReady = true;
|
this.bareGameReady = true;
|
||||||
initBuildingCodesAfterResourcesLoaded();
|
initBuildingCodesAfterResourcesLoaded();
|
||||||
this.signalBareGameLoaded.dispatch();
|
this.signalBareGameLoaded.dispatch();
|
||||||
|
@ -95,15 +95,18 @@ export class BufferMaintainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @param {object} param0
|
||||||
* @param {string} key
|
* @param {string} param0.key
|
||||||
* @param {string} subKey
|
* @param {string} param0.subKey
|
||||||
* @param {function(HTMLCanvasElement, CanvasRenderingContext2D, number, number, number, object?) : void} redrawMethod
|
* @param {number} param0.w
|
||||||
* @param {object=} additionalParams
|
* @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}
|
* @returns {HTMLCanvasElement}
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
getForKey(key, subKey, w, h, dpi, redrawMethod, additionalParams) {
|
getForKey({ key, subKey, w, h, dpi, redrawMethod, additionalParams }) {
|
||||||
// First, create parent key
|
// First, create parent key
|
||||||
let parent = this.cache.get(key);
|
let parent = this.cache.get(key);
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
|
@ -34,6 +34,7 @@ export const globalConfig = {
|
|||||||
assetsDpi: 192 / 32,
|
assetsDpi: 192 / 32,
|
||||||
assetsSharpness: 1.2,
|
assetsSharpness: 1.2,
|
||||||
shapesSharpness: 1.4,
|
shapesSharpness: 1.4,
|
||||||
|
mapChunkSharpness: 1.0,
|
||||||
|
|
||||||
// Production analytics
|
// Production analytics
|
||||||
statisticsGraphDpi: 2.5,
|
statisticsGraphDpi: 2.5,
|
||||||
@ -45,18 +46,14 @@ export const globalConfig = {
|
|||||||
|
|
||||||
// Map
|
// Map
|
||||||
mapChunkSize: 16,
|
mapChunkSize: 16,
|
||||||
mapChunkPrerenderMinZoom: -1,
|
mapChunkOverviewMinZoom: 0.9,
|
||||||
mapChunkOverviewMinZoom: 0.7,
|
|
||||||
|
|
||||||
// Belt speeds
|
// Belt speeds
|
||||||
// NOTICE: Update webpack.production.config too!
|
// NOTICE: Update webpack.production.config too!
|
||||||
beltSpeedItemsPerSecond: 2,
|
beltSpeedItemsPerSecond: 2,
|
||||||
minerSpeedItemsPerSecond: 0, // COMPUTED
|
minerSpeedItemsPerSecond: 0, // COMPUTED
|
||||||
|
|
||||||
beltItemSpacingByLayer: {
|
itemSpacingOnBelts: 0.63,
|
||||||
regular: 0.63,
|
|
||||||
wires: 0.4,
|
|
||||||
},
|
|
||||||
|
|
||||||
wiresSpeedItemsPerSecond: 6,
|
wiresSpeedItemsPerSecond: 6,
|
||||||
|
|
||||||
@ -116,14 +113,12 @@ globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5
|
|||||||
// Dynamic calculations
|
// Dynamic calculations
|
||||||
if (globalConfig.debug.disableMapOverview) {
|
if (globalConfig.debug.disableMapOverview) {
|
||||||
globalConfig.mapChunkOverviewMinZoom = 0;
|
globalConfig.mapChunkOverviewMinZoom = 0;
|
||||||
globalConfig.mapChunkPrerenderMinZoom = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stuff for making the trailer
|
// Stuff for making the trailer
|
||||||
if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
|
if (G_IS_DEV && globalConfig.debug.renderForTrailer) {
|
||||||
globalConfig.debug.framePausesBetweenTicks = 32;
|
globalConfig.debug.framePausesBetweenTicks = 32;
|
||||||
// globalConfig.mapChunkOverviewMinZoom = 0.0;
|
// globalConfig.mapChunkOverviewMinZoom = 0.0;
|
||||||
// globalConfig.mapChunkPrerenderMinZoom = globalConfig.mapChunkOverviewMinZoom;
|
|
||||||
// globalConfig.debug.instantBelts = true;
|
// globalConfig.debug.instantBelts = true;
|
||||||
// globalConfig.debug.instantProcessors = true;
|
// globalConfig.debug.instantProcessors = true;
|
||||||
// globalConfig.debug.instantMiners = true;
|
// globalConfig.debug.instantMiners = true;
|
||||||
|
@ -15,8 +15,8 @@ export function getDeviceDPI() {
|
|||||||
* @returns {number} Smoothed dpi
|
* @returns {number} Smoothed dpi
|
||||||
*/
|
*/
|
||||||
export function smoothenDpi(dpi) {
|
export function smoothenDpi(dpi) {
|
||||||
if (dpi < 0.05) {
|
if (dpi < 0.02) {
|
||||||
return 0.05;
|
return 0.02;
|
||||||
} else if (dpi < 0.1) {
|
} else if (dpi < 0.1) {
|
||||||
return round2Digits(dpi);
|
return round2Digits(dpi);
|
||||||
} else if (dpi < 1) {
|
} else if (dpi < 1) {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Rectangle } from "./rectangle";
|
|
||||||
import { globalConfig } from "./config";
|
import { globalConfig } from "./config";
|
||||||
|
|
||||||
/* typehints:start */
|
/**
|
||||||
import { GameRoot } from "../game/root";
|
* @typedef {import("../game/root").GameRoot} GameRoot
|
||||||
/* typehints:end */
|
* @typedef {import("./rectangle").Rectangle} Rectangle
|
||||||
|
*/
|
||||||
|
|
||||||
export class DrawParameters {
|
export class DrawParameters {
|
||||||
constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {
|
constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
/* typehints:start */
|
/**
|
||||||
import { AtlasSprite } from "./sprites";
|
* @typedef {import("./sprites").AtlasSprite} AtlasSprite
|
||||||
import { DrawParameters } from "./draw_parameters";
|
* @typedef {import("./draw_parameters").DrawParameters} DrawParameters
|
||||||
/* typehints:end */
|
*/
|
||||||
|
|
||||||
import { Vector } from "./vector";
|
|
||||||
import { Rectangle } from "./rectangle";
|
|
||||||
import { createLogger } from "./logging";
|
|
||||||
|
|
||||||
const logger = createLogger("draw_utils");
|
|
||||||
|
|
||||||
export function initDrawUtils() {
|
export function initDrawUtils() {
|
||||||
CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) {
|
CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) {
|
||||||
|
this.beginPath();
|
||||||
|
|
||||||
if (r < 0.05) {
|
if (r < 0.05) {
|
||||||
this.beginPath();
|
|
||||||
this.rect(x, y, w, h);
|
this.rect(x, y, w, h);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -20,25 +15,26 @@ export function initDrawUtils() {
|
|||||||
if (w < 2 * r) {
|
if (w < 2 * r) {
|
||||||
r = w / 2;
|
r = w / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h < 2 * r) {
|
if (h < 2 * r) {
|
||||||
r = h / 2;
|
r = h / 2;
|
||||||
}
|
}
|
||||||
this.beginPath();
|
|
||||||
this.moveTo(x + r, y);
|
this.moveTo(x + r, y);
|
||||||
this.arcTo(x + w, y, x + w, y + h, r);
|
this.arcTo(x + w, y, x + w, y + h, r);
|
||||||
this.arcTo(x + w, y + h, x, 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 + h, x, y, r);
|
||||||
this.arcTo(x, y, x + w, y, r);
|
this.arcTo(x, y, x + w, y, r);
|
||||||
// this.closePath();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) {
|
CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) {
|
||||||
|
this.beginPath();
|
||||||
|
|
||||||
if (r < 0.05) {
|
if (r < 0.05) {
|
||||||
this.beginPath();
|
|
||||||
this.rect(x, y, 1, 1);
|
this.rect(x, y, 1, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.beginPath();
|
|
||||||
this.arc(x, y, r, 0, 2.0 * Math.PI);
|
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.rotate(-angle);
|
||||||
parameters.context.translate(-x, -y);
|
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 { SingletonFactory } from "./singleton_factory";
|
||||||
import { Factory } from "./factory";
|
import { Factory } from "./factory";
|
||||||
|
|
||||||
/* typehints:start */
|
/**
|
||||||
import { BaseGameSpeed } from "../game/time/base_game_speed";
|
* @typedef {import("../game/time/base_game_speed").BaseGameSpeed} BaseGameSpeed
|
||||||
import { Component } from "../game/component";
|
* @typedef {import("../game/component").Component} Component
|
||||||
import { BaseItem } from "../game/base_item";
|
* @typedef {import("../game/base_item").BaseItem} BaseItem
|
||||||
import { MetaBuilding } from "../game/meta_building";
|
* @typedef {import("../game/meta_building").MetaBuilding} MetaBuilding
|
||||||
/* typehints:end */
|
|
||||||
|
|
||||||
// These factories are here to remove circular dependencies
|
// These factories are here to remove circular dependencies
|
||||||
|
|
||||||
/** @type {SingletonFactoryTemplate<MetaBuilding>} */
|
/** @type {SingletonFactoryTemplate<MetaBuilding>} */
|
||||||
export let gMetaBuildingRegistry = new SingletonFactory();
|
export let gMetaBuildingRegistry = new SingletonFactory();
|
||||||
|
|
||||||
/** @type {Object.<string, Array<typeof MetaBuilding>>} */
|
/** @type {Object.<string, Array<Class<MetaBuilding>>>} */
|
||||||
export let gBuildingsByCategory = null;
|
export let gBuildingsByCategory = null;
|
||||||
|
|
||||||
/** @type {FactoryTemplate<Component>} */
|
/** @type {FactoryTemplate<Component>} */
|
||||||
@ -28,7 +28,7 @@ export let gItemRegistry = new Factory("item");
|
|||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object.<string, Array<typeof MetaBuilding>>} buildings
|
* @param {Object.<string, Array<Class<MetaBuilding>>>} buildings
|
||||||
*/
|
*/
|
||||||
export function initBuildingsByCategory(buildings) {
|
export function initBuildingsByCategory(buildings) {
|
||||||
gBuildingsByCategory = 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 { makeOffscreenBuffer } from "./buffer_utils";
|
||||||
import { AtlasSprite, BaseSprite, RegularSprite, SpriteAtlasLink } from "./sprites";
|
import { AtlasSprite, BaseSprite, RegularSprite, SpriteAtlasLink } from "./sprites";
|
||||||
import { cachebust } from "./cachebust";
|
import { cachebust } from "./cachebust";
|
||||||
import { createLogger } from "./logging";
|
import { createLogger } from "./logging";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import("../application").Application} Application
|
||||||
|
* @typedef {import("./atlas_definitions").AtlasDefinition} AtlasDefinition;
|
||||||
|
*/
|
||||||
|
|
||||||
const logger = createLogger("loader");
|
const logger = createLogger("loader");
|
||||||
|
|
||||||
const missingSpriteIds = {};
|
const missingSpriteIds = {};
|
||||||
|
|
||||||
class LoaderImpl {
|
class LoaderImpl {
|
||||||
constructor() {
|
constructor() {
|
||||||
/** @type {Application} */
|
|
||||||
this.app = null;
|
this.app = null;
|
||||||
|
|
||||||
/** @type {Map<string, BaseSprite>} */
|
/** @type {Map<string, BaseSprite>} */
|
||||||
@ -23,6 +22,9 @@ class LoaderImpl {
|
|||||||
this.rawImages = [];
|
this.rawImages = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Application} app
|
||||||
|
*/
|
||||||
linkAppAfterBoot(app) {
|
linkAppAfterBoot(app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.makeSpriteNotFoundCanvas();
|
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
|
* @param {string} key
|
||||||
* @returns {RegularSprite}
|
* @returns {RegularSprite}
|
||||||
*/
|
*/
|
||||||
@ -155,44 +157,34 @@ class LoaderImpl {
|
|||||||
* @param {AtlasDefinition} atlas
|
* @param {AtlasDefinition} atlas
|
||||||
* @param {HTMLImageElement} loadedImage
|
* @param {HTMLImageElement} loadedImage
|
||||||
*/
|
*/
|
||||||
internalParseAtlas(atlas, loadedImage) {
|
internalParseAtlas({ meta: { scale }, sourceData }, loadedImage) {
|
||||||
this.rawImages.push(loadedImage);
|
this.rawImages.push(loadedImage);
|
||||||
|
|
||||||
for (const spriteKey in atlas.sourceData) {
|
for (const spriteName in sourceData) {
|
||||||
const spriteData = atlas.sourceData[spriteKey];
|
const { frame, sourceSize, spriteSourceSize } = sourceData[spriteName];
|
||||||
|
|
||||||
let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteKey));
|
let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteName));
|
||||||
|
|
||||||
if (!sprite) {
|
if (!sprite) {
|
||||||
sprite = new AtlasSprite({
|
sprite = new AtlasSprite(spriteName);
|
||||||
spriteName: spriteKey,
|
this.sprites.set(spriteName, sprite);
|
||||||
});
|
|
||||||
this.sprites.set(spriteKey, sprite);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const link = new SpriteAtlasLink({
|
const link = new SpriteAtlasLink({
|
||||||
packedX: spriteData.frame.x,
|
packedX: frame.x,
|
||||||
packedY: spriteData.frame.y,
|
packedY: frame.y,
|
||||||
packedW: spriteData.frame.w,
|
packedW: frame.w,
|
||||||
packedH: spriteData.frame.h,
|
packedH: frame.h,
|
||||||
packOffsetX: spriteData.spriteSourceSize.x,
|
packOffsetX: spriteSourceSize.x,
|
||||||
packOffsetY: spriteData.spriteSourceSize.y,
|
packOffsetY: spriteSourceSize.y,
|
||||||
atlas: loadedImage,
|
atlas: loadedImage,
|
||||||
w: spriteData.sourceSize.w,
|
w: sourceSize.w,
|
||||||
h: spriteData.sourceSize.h,
|
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
|
* Makes the canvas which shows the question mark, shown when a sprite was not found
|
||||||
*/
|
*/
|
||||||
@ -216,14 +208,9 @@ class LoaderImpl {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
canvas.src = "not-found";
|
canvas.src = "not-found";
|
||||||
|
|
||||||
const resolutions = ["0.1", "0.25", "0.5", "0.75", "1"];
|
const sprite = new AtlasSprite("not-found");
|
||||||
const sprite = new AtlasSprite({
|
["0.1", "0.25", "0.5", "0.75", "1"].forEach(resolution => {
|
||||||
spriteName: "not-found",
|
sprite.linksByResolution[resolution] = new SpriteAtlasLink({
|
||||||
});
|
|
||||||
|
|
||||||
for (let i = 0; i < resolutions.length; ++i) {
|
|
||||||
const res = resolutions[i];
|
|
||||||
const link = new SpriteAtlasLink({
|
|
||||||
packedX: 0,
|
packedX: 0,
|
||||||
packedY: 0,
|
packedY: 0,
|
||||||
w: dims,
|
w: dims,
|
||||||
@ -234,8 +221,8 @@ class LoaderImpl {
|
|||||||
packedH: dims,
|
packedH: dims,
|
||||||
atlas: canvas,
|
atlas: canvas,
|
||||||
});
|
});
|
||||||
sprite.linksByResolution[res] = link;
|
});
|
||||||
}
|
|
||||||
this.spriteNotFoundSprite = sprite;
|
this.spriteNotFoundSprite = sprite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { sha1 } from "./sensitive_utils.encrypt";
|
import { sha1, CRC_PREFIX, computeCrc } from "./sensitive_utils.encrypt";
|
||||||
import { createLogger } from "./logging";
|
import { createLogger } from "./logging";
|
||||||
import { FILE_NOT_FOUND } from "../platform/storage";
|
import { FILE_NOT_FOUND } from "../platform/storage";
|
||||||
import { accessNestedPropertyReverse } from "./utils";
|
import { accessNestedPropertyReverse } from "./utils";
|
||||||
@ -84,7 +84,7 @@ export class ReadWriteProxy {
|
|||||||
*/
|
*/
|
||||||
static serializeObject(obj) {
|
static serializeObject(obj) {
|
||||||
const jsonString = JSON.stringify(compressObject(obj));
|
const jsonString = JSON.stringify(compressObject(obj));
|
||||||
const checksum = sha1(jsonString + salt);
|
const checksum = computeCrc(jsonString + salt);
|
||||||
return compressionPrefix + compressX64(checksum + jsonString);
|
return compressionPrefix + compressX64(checksum + jsonString);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +106,11 @@ export class ReadWriteProxy {
|
|||||||
// Compare stored checksum with actual checksum
|
// Compare stored checksum with actual checksum
|
||||||
const checksum = decompressed.substring(0, 40);
|
const checksum = decompressed.substring(0, 40);
|
||||||
const jsonString = decompressed.substr(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) {
|
if (desiredChecksum !== checksum) {
|
||||||
// Checksum mismatch
|
// Checksum mismatch
|
||||||
throw new Error("bad-content / checksum-mismatch");
|
throw new Error("bad-content / checksum-mismatch");
|
||||||
@ -119,7 +123,7 @@ export class ReadWriteProxy {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the data asychronously, fails if verify() fails
|
* Writes the data asychronously, fails if verify() fails
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
writeAsync() {
|
writeAsync() {
|
||||||
const verifyResult = this.internalVerifyEntry(this.currentData);
|
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);
|
logger.error("Tried to write invalid data to", this.filename, "reason:", verifyResult.reason);
|
||||||
return Promise.reject(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
|
return asyncCompressor
|
||||||
.compressFileAsync(jsonString)
|
.compressObjectAsync(this.currentData)
|
||||||
.then(compressed => {
|
.then(compressed => {
|
||||||
if (G_IS_DEV && IS_DEBUG) {
|
|
||||||
compressed = jsonString;
|
|
||||||
}
|
|
||||||
return this.app.storage.writeFileAsync(this.filename, compressed);
|
return this.app.storage.writeFileAsync(this.filename, compressed);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
logger.log("📄 Wrote", this.filename);
|
logger.log("📄 Wrote", this.filename);
|
||||||
return jsonString;
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error("Failed to write", this.filename, ":", err);
|
logger.error("Failed to write", this.filename, ":", err);
|
||||||
@ -205,10 +188,16 @@ export class ReadWriteProxy {
|
|||||||
// Compare stored checksum with actual checksum
|
// Compare stored checksum with actual checksum
|
||||||
const checksum = decompressed.substring(0, 40);
|
const checksum = decompressed.substring(0, 40);
|
||||||
const jsonString = decompressed.substr(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) {
|
if (desiredChecksum !== checksum) {
|
||||||
// Checksum mismatch
|
// Checksum mismatch
|
||||||
return Promise.reject("bad-content / checksum-mismatch");
|
return Promise.reject(
|
||||||
|
"bad-content / checksum-mismatch: " + desiredChecksum + " vs " + checksum
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return jsonString;
|
return jsonString;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { globalConfig } from "./config";
|
import { globalConfig } from "./config";
|
||||||
import { clamp, epsilonCompare, round2Digits } from "./utils";
|
import { epsilonCompare, round2Digits } from "./utils";
|
||||||
import { Vector } from "./vector";
|
import { Vector } from "./vector";
|
||||||
|
|
||||||
export class Rectangle {
|
export class Rectangle {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createHash } from "rusha";
|
import { createHash } from "rusha";
|
||||||
|
import crc32 from "crc/crc32";
|
||||||
import { decompressX64 } from "./lzstring";
|
import { decompressX64 } from "./lzstring";
|
||||||
|
|
||||||
export function sha1(str) {
|
export function sha1(str) {
|
||||||
@ -10,3 +10,14 @@ export function sha1(str) {
|
|||||||
export function getNameOfProvider() {
|
export function getNameOfProvider() {
|
||||||
return window[decompressX64("DYewxghgLgliB2Q")][decompressX64("BYewzgLgdghgtgUyA")];
|
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 { DrawParameters } from "./draw_parameters";
|
||||||
import { Rectangle } from "./rectangle";
|
import { Rectangle } from "./rectangle";
|
||||||
import { epsilonCompare, round3Digits } from "./utils";
|
import { round3Digits } from "./utils";
|
||||||
|
|
||||||
const floorSpriteCoordinates = false;
|
const floorSpriteCoordinates = false;
|
||||||
|
|
||||||
@ -63,10 +63,9 @@ export class SpriteAtlasLink {
|
|||||||
export class AtlasSprite extends BaseSprite {
|
export class AtlasSprite extends BaseSprite {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} param0
|
* @param {string} spriteName
|
||||||
* @param {string} param0.spriteName
|
|
||||||
*/
|
*/
|
||||||
constructor({ spriteName = "sprite" }) {
|
constructor(spriteName = "sprite") {
|
||||||
super();
|
super();
|
||||||
/** @type {Object.<string, SpriteAtlasLink>} */
|
/** @type {Object.<string, SpriteAtlasLink>} */
|
||||||
this.linksByResolution = {};
|
this.linksByResolution = {};
|
||||||
@ -197,8 +196,6 @@ export class AtlasSprite extends BaseSprite {
|
|||||||
destH = intersection.h;
|
destH = intersection.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// assert(epsilonCompare(scaleW, scaleH), "Sprite should be square for cached rendering");
|
|
||||||
|
|
||||||
if (floorSpriteCoordinates) {
|
if (floorSpriteCoordinates) {
|
||||||
parameters.context.drawImage(
|
parameters.context.drawImage(
|
||||||
link.atlas,
|
link.atlas,
|
||||||
|
@ -1,46 +1,7 @@
|
|||||||
import { globalConfig, IS_DEBUG } from "./config";
|
|
||||||
import { Vector } from "./vector";
|
|
||||||
import { T } from "../translations";
|
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"];
|
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 if this platform is android
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
@ -66,7 +27,7 @@ export function isIos() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a platform name
|
* Returns a platform name
|
||||||
* @returns {string}
|
* @returns {"android" | "browser" | "ios" | "standalone" | "unknown"}
|
||||||
*/
|
*/
|
||||||
export function getPlatformName() {
|
export function getPlatformName() {
|
||||||
if (G_IS_STANDALONE) {
|
if (G_IS_STANDALONE) {
|
||||||
@ -96,60 +57,13 @@ export function getIPCRenderer() {
|
|||||||
return ipcRenderer;
|
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
|
* Makes a new 2D array with undefined contents
|
||||||
* @param {number} w
|
* @param {number} w
|
||||||
* @param {number} h
|
* @param {number} h
|
||||||
* @param {string=} context
|
|
||||||
* @returns {Array<Array<any>>}
|
* @returns {Array<Array<any>>}
|
||||||
*/
|
*/
|
||||||
export function make2DUndefinedArray(w, h, context = null) {
|
export function make2DUndefinedArray(w, h) {
|
||||||
const result = new Array(w);
|
const result = new Array(w);
|
||||||
for (let x = 0; x < w; ++x) {
|
for (let x = 0; x < w; ++x) {
|
||||||
result[x] = new Array(h);
|
result[x] = new Array(h);
|
||||||
@ -157,33 +71,6 @@ export function make2DUndefinedArray(w, h, context = null) {
|
|||||||
return result;
|
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)
|
* 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
|
* Chooses a random entry of an array
|
||||||
* @param {Array | string} arr
|
* @template T
|
||||||
|
* @param {T[]} arr
|
||||||
|
* @returns {T}
|
||||||
*/
|
*/
|
||||||
export function randomChoice(arr) {
|
export function randomChoice(arr) {
|
||||||
return arr[Math.floor(Math.random() * arr.length)];
|
return arr[Math.floor(Math.random() * arr.length)];
|
||||||
@ -304,23 +193,6 @@ export function arrayDeleteValue(array, value) {
|
|||||||
return arrayDelete(array, index);
|
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
|
* Compare two floats for epsilon equality
|
||||||
* @param {number} a
|
* @param {number} a
|
||||||
@ -331,15 +203,6 @@ export function epsilonCompare(a, b, epsilon = 1e-5) {
|
|||||||
return Math.abs(a - b) < epsilon;
|
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
|
* Interpolates two numbers
|
||||||
* @param {number} a
|
* @param {number} a
|
||||||
@ -399,17 +262,6 @@ export function findNiceIntegerValue(num) {
|
|||||||
return Math.ceil(findNiceValue(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
|
* Formats a big number
|
||||||
* @param {number} num
|
* @param {number} num
|
||||||
@ -477,92 +329,12 @@ export function formatBigNumberFull(num, divider = T.global.thousandsDivider) {
|
|||||||
return out.substring(0, out.length - 1);
|
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
|
* Waits two frames so the ui is updated
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
export function waitNextFrame() {
|
export function waitNextFrame() {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve) {
|
||||||
window.requestAnimationFrame(function () {
|
window.requestAnimationFrame(function () {
|
||||||
window.requestAnimationFrame(function () {
|
window.requestAnimationFrame(function () {
|
||||||
resolve();
|
resolve();
|
||||||
@ -617,27 +389,13 @@ export function clamp(v, minimum = 0, maximum = 1) {
|
|||||||
return Math.max(minimum, Math.min(maximum, v));
|
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
|
* Helper method to create a new div element
|
||||||
* @param {string=} id
|
* @param {string=} id
|
||||||
* @param {Array<string>=} classes
|
* @param {Array<string>=} classes
|
||||||
* @param {string=} innerHTML
|
* @param {string=} innerHTML
|
||||||
*/
|
*/
|
||||||
export function makeDivElement(id = null, classes = [], innerHTML = "") {
|
function makeDivElement(id = null, classes = [], innerHTML = "") {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
if (id) {
|
if (id) {
|
||||||
div.id = id;
|
div.id = id;
|
||||||
@ -662,20 +420,6 @@ export function makeDiv(parent, id = null, classes = [], innerHTML = "") {
|
|||||||
return div;
|
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
|
* Helper method to create a new button element
|
||||||
* @param {Array<string>=} classes
|
* @param {Array<string>=} classes
|
||||||
@ -703,19 +447,6 @@ export function makeButton(parent, classes = [], innerHTML = "") {
|
|||||||
return element;
|
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
|
* Removes all children of the given element
|
||||||
* @param {Element} elem
|
* @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.
|
* 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
|
* We use 3 digits of precision, this allows us to store precision of 1 ms without
|
||||||
* the risk to simulation errors due to resync issues
|
* the risking simulation errors due to resync issues
|
||||||
* @param {number} value
|
* @param {number} value
|
||||||
*/
|
*/
|
||||||
export function quantizeFloat(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"
|
* Formats an amount of seconds into something like "5s ago"
|
||||||
* @param {number} secs Seconds
|
* @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"
|
* Formats a number like 2.5 to "2.5 items / s"
|
||||||
* @param {number} speed
|
* @param {number} speed
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { BasicSerializableObject } from "../savegame/serialization";
|
import { BasicSerializableObject } from "../savegame/serialization";
|
||||||
import { enumLayer } from "./root";
|
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumItemType = {
|
export const enumItemType = {
|
||||||
@ -13,15 +12,6 @@ export const enumItemType = {
|
|||||||
genericEnergy: "genericEnergy",
|
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
|
* Class for items on belts etc. Not an entity for performance reasons
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
|
import { gItemRegistry } from "../core/global_registries";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
|
import { Rectangle } from "../core/rectangle";
|
||||||
import { epsilonCompare, round4Digits } from "../core/utils";
|
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 { BaseItem } from "./base_item";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { GameRoot, enumLayer } from "./root";
|
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");
|
const logger = createLogger("belt_path");
|
||||||
|
|
||||||
@ -115,22 +115,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
canAcceptItem() {
|
canAcceptItem() {
|
||||||
return this.spacingToFirstItem >= this.getItemSpacing();
|
return this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,15 +123,15 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
* @param {BaseItem} item
|
* @param {BaseItem} item
|
||||||
*/
|
*/
|
||||||
tryAcceptItem(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.
|
// So, since we already need one tick to accept this item we will add this directly.
|
||||||
const beltProgressPerTick =
|
const beltProgressPerTick =
|
||||||
this.root.hubGoals.getBeltBaseSpeed(this.layer) *
|
this.root.hubGoals.getBeltBaseSpeed() *
|
||||||
this.root.dynamicTickrate.deltaSeconds *
|
this.root.dynamicTickrate.deltaSeconds *
|
||||||
this.getItemSpacing();
|
globalConfig.itemSpacingOnBelts;
|
||||||
|
|
||||||
// First, compute how much progress we can make *at max*
|
// 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);
|
const initialProgress = Math.min(maxProgress, beltProgressPerTick);
|
||||||
|
|
||||||
this.items.unshift([this.spacingToFirstItem - initialProgress, item]);
|
this.items.unshift([this.spacingToFirstItem - initialProgress, item]);
|
||||||
@ -167,7 +152,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
* @returns {BaseItem|null}
|
* @returns {BaseItem|null}
|
||||||
*/
|
*/
|
||||||
findItemAtTile(tile) {
|
findItemAtTile(tile) {
|
||||||
// TODO: This breaks color blind mode otherwise
|
// @TODO: This breaks color blind mode otherwise
|
||||||
return null;
|
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() {
|
onPathChanged() {
|
||||||
this.ejectorComp = this.entityPath[this.entityPath.length - 1].components.ItemEjector;
|
this.acceptorTarget = this.computeAcceptingEntityAndSlot();
|
||||||
this.ejectorSlot = this.ejectorComp.slots[0];
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
/**
|
||||||
const ejectorComp = this.entityPath[i].components.ItemEjector;
|
* Called by the belt system when the surroundings changed
|
||||||
const isLast = i === this.entityPath.length - 1;
|
*/
|
||||||
ejectorComp.enabled = isLast;
|
onSurroundingsChanged() {
|
||||||
|
this.onPathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
// Clear all slots of non-end entities
|
/**
|
||||||
if (!isLast) {
|
* Finds the entity which accepts our items
|
||||||
for (let k = 0; k < ejectorComp.slots.length; ++k) {
|
* @return {{ entity: Entity, slot: number, direction?: enumDirection }}
|
||||||
ejectorComp.slots[k].item = null;
|
*/
|
||||||
ejectorComp.slots[k].progress = 0.0;
|
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
|
// 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);
|
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);
|
const followUp = this.root.systemMgr.systems.belt.findFollowUpEntity(entity);
|
||||||
if (!followUp) {
|
if (!followUp) {
|
||||||
return fail(
|
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
|
// Check spacing
|
||||||
if (this.spacingToFirstItem > this.totalLength + 0.005) {
|
if (this.spacingToFirstItem > this.totalLength + 0.005) {
|
||||||
return fail(
|
return fail(
|
||||||
@ -370,20 +390,12 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
|
|
||||||
const beltComp = entity.components.Belt;
|
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
|
// Append the entity
|
||||||
this.entityPath.push(entity);
|
this.entityPath.push(entity);
|
||||||
this.onPathChanged();
|
this.onPathChanged();
|
||||||
|
|
||||||
// Extend the path length
|
// Extend the path length
|
||||||
const additionalLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
const additionalLength = beltComp.getEffectiveLengthTiles();
|
||||||
this.totalLength += additionalLength;
|
this.totalLength += additionalLength;
|
||||||
DEBUG && logger.log(" Extended total length by", additionalLength, "to", this.totalLength);
|
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)
|
// 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
|
// Extend the length of this path
|
||||||
this.totalLength += length;
|
this.totalLength += length;
|
||||||
@ -478,7 +490,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
const beltComp = entity.components.Belt;
|
const beltComp = entity.components.Belt;
|
||||||
beltComp.assignedPath = null;
|
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 not contained for split");
|
||||||
assert(this.entityPath.indexOf(entity) !== 0, "Entity is first");
|
assert(this.entityPath.indexOf(entity) !== 0, "Entity is first");
|
||||||
assert(this.entityPath.indexOf(entity) !== this.entityPath.length - 1, "Entity is last");
|
assert(this.entityPath.indexOf(entity) !== this.entityPath.length - 1, "Entity is last");
|
||||||
@ -496,7 +508,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
|
|
||||||
++firstPathEntityCount;
|
++firstPathEntityCount;
|
||||||
firstPathEndEntity = otherEntity;
|
firstPathEndEntity = otherEntity;
|
||||||
firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles(otherEntity.layer);
|
firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG &&
|
DEBUG &&
|
||||||
@ -640,7 +652,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
|
|
||||||
// Ok, first remove the entity
|
// Ok, first remove the entity
|
||||||
const beltComp = entity.components.Belt;
|
const beltComp = entity.components.Belt;
|
||||||
const beltLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
const beltLength = beltComp.getEffectiveLengthTiles();
|
||||||
|
|
||||||
DEBUG &&
|
DEBUG &&
|
||||||
logger.log(
|
logger.log(
|
||||||
@ -749,7 +761,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
|
|
||||||
// Ok, first remove the entity
|
// Ok, first remove the entity
|
||||||
const beltComp = entity.components.Belt;
|
const beltComp = entity.components.Belt;
|
||||||
const beltLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
const beltLength = beltComp.getEffectiveLengthTiles();
|
||||||
|
|
||||||
DEBUG &&
|
DEBUG &&
|
||||||
logger.log(
|
logger.log(
|
||||||
@ -889,7 +901,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
beltComp.assignedPath = this;
|
beltComp.assignedPath = this;
|
||||||
|
|
||||||
// Update our length
|
// Update our length
|
||||||
const additionalLength = beltComp.getEffectiveLengthTiles(entity.layer);
|
const additionalLength = beltComp.getEffectiveLengthTiles();
|
||||||
this.totalLength += additionalLength;
|
this.totalLength += additionalLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -947,7 +959,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
let length = 0;
|
let length = 0;
|
||||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||||
const entity = this.entityPath[i];
|
const entity = this.entityPath[i];
|
||||||
length += entity.components.Belt.getEffectiveLengthTiles(this.layer);
|
length += entity.components.Belt.getEffectiveLengthTiles();
|
||||||
}
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
@ -962,15 +974,15 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
|
|
||||||
// Divide by item spacing on belts since we use throughput and not speed
|
// Divide by item spacing on belts since we use throughput and not speed
|
||||||
let beltSpeed =
|
let beltSpeed =
|
||||||
this.root.hubGoals.getBeltBaseSpeed(this.layer) *
|
this.root.hubGoals.getBeltBaseSpeed() *
|
||||||
this.root.dynamicTickrate.deltaSeconds *
|
this.root.dynamicTickrate.deltaSeconds *
|
||||||
this.getItemSpacing();
|
globalConfig.itemSpacingOnBelts;
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.instantBelts) {
|
if (G_IS_DEV && globalConfig.debug.instantBelts) {
|
||||||
beltSpeed *= 100;
|
beltSpeed *= 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
let minimumDistance = this.ejectorSlot.item ? this.getItemSpacing() : 0;
|
let minimumDistance = 0;
|
||||||
|
|
||||||
// Try to reduce spacing
|
// Try to reduce spacing
|
||||||
let remainingAmount = beltSpeed;
|
let remainingAmount = beltSpeed;
|
||||||
@ -991,13 +1003,13 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
break;
|
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];
|
const lastItem = this.items[this.items.length - 1];
|
||||||
if (lastItem && lastItem[_nextDistance] === 0) {
|
if (lastItem && lastItem[_nextDistance] === 0 && this.acceptorTarget) {
|
||||||
// Take over
|
if (this.tryHandOverItem(lastItem[_item])) {
|
||||||
if (this.ejectorComp.tryEject(0, lastItem[_item])) {
|
|
||||||
this.items.pop();
|
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
|
* Computes a world space position from the given progress
|
||||||
* @param {number} progress
|
* @param {number} progress
|
||||||
@ -1020,14 +1073,14 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
|
|
||||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||||
const beltComp = this.entityPath[i].components.Belt;
|
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) {
|
if (currentLength + localLength >= progress || i === this.entityPath.length - 1) {
|
||||||
// Min required here due to floating point issues
|
// Min required here due to floating point issues
|
||||||
const localProgress = Math.min(1.0, progress - currentLength);
|
const localProgress = Math.min(1.0, progress - currentLength);
|
||||||
|
|
||||||
assert(localProgress >= 0.0, "Invalid local progress: " + localProgress);
|
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);
|
return this.entityPath[i].components.StaticMapEntity.localTileToWorld(localSpace);
|
||||||
}
|
}
|
||||||
currentLength += localLength;
|
currentLength += localLength;
|
||||||
@ -1045,11 +1098,6 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.entityPath[0].layer !== this.root.currentLayer) {
|
|
||||||
// Don't draw
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters.context.fillStyle = "#d79a25";
|
parameters.context.fillStyle = "#d79a25";
|
||||||
parameters.context.strokeStyle = "#d79a25";
|
parameters.context.strokeStyle = "#d79a25";
|
||||||
parameters.context.beginPath();
|
parameters.context.beginPath();
|
||||||
@ -1131,10 +1179,10 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
for (let i = 0; i < this.entityPath.length; ++i) {
|
for (let i = 0; i < this.entityPath.length; ++i) {
|
||||||
const entity = this.entityPath[i];
|
const entity = this.entityPath[i];
|
||||||
const beltComp = entity.components.Belt;
|
const beltComp = entity.components.Belt;
|
||||||
const beltLength = beltComp.getEffectiveLengthTiles(this.layer);
|
const beltLength = beltComp.getEffectiveLengthTiles();
|
||||||
|
|
||||||
// Check if the current items are on the belt
|
// 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
|
// Its on the belt, render it now
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
assert(
|
assert(
|
||||||
@ -1142,7 +1190,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||||||
"invalid track pos: " + currentItemPos + " vs " + trackPos + " (l =" + beltLength + ")"
|
"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 worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile();
|
||||||
|
|
||||||
const distanceAndItem = this.items[currentItemIndex];
|
const distanceAndItem = this.items[currentItemIndex];
|
||||||
|
@ -92,13 +92,7 @@ export class Blueprint {
|
|||||||
parameters.context.globalAlpha = 1;
|
parameters.context.globalAlpha = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
staticComp.drawSpriteOnFullEntityBounds(
|
staticComp.drawSpriteOnFullEntityBounds(parameters, staticComp.getBlueprintSprite(), 0, newPos);
|
||||||
parameters,
|
|
||||||
staticComp.getBlueprintSprite(),
|
|
||||||
0,
|
|
||||||
true,
|
|
||||||
newPos
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
parameters.context.globalAlpha = 1;
|
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 { AtlasSprite } from "../core/sprites";
|
||||||
|
import { Vector } from "../core/vector";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
@ -7,6 +10,7 @@ import { AtlasSprite } from "../core/sprites";
|
|||||||
* metaInstance?: MetaBuilding,
|
* metaInstance?: MetaBuilding,
|
||||||
* variant?: string,
|
* variant?: string,
|
||||||
* rotationVariant?: number,
|
* rotationVariant?: number,
|
||||||
|
* tileSize?: Vector,
|
||||||
* sprite?: AtlasSprite,
|
* sprite?: AtlasSprite,
|
||||||
* blueprintSprite?: AtlasSprite,
|
* blueprintSprite?: AtlasSprite,
|
||||||
* silhouetteColor?: string
|
* silhouetteColor?: string
|
||||||
@ -22,18 +26,25 @@ export const gBuildingVariants = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Registers a new variant
|
||||||
* @param {*} id
|
* @param {number} id
|
||||||
* @param {*} meta
|
* @param {typeof MetaBuilding} meta
|
||||||
* @param {*} variant
|
* @param {string} variant
|
||||||
* @param {*} rotationVariant
|
* @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);
|
assert(!gBuildingVariants[id], "Duplicate id: " + id);
|
||||||
gBuildingVariants[id] = {
|
gBuildingVariants[id] = {
|
||||||
metaClass: meta,
|
metaClass: meta,
|
||||||
variant,
|
variant,
|
||||||
rotationVariant,
|
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 { SOUNDS } from "../../platform/sound";
|
||||||
import { T } from "../../translations";
|
import { T } from "../../translations";
|
||||||
import { BeltComponent } from "../components/belt";
|
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 { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot, enumLayer } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
|
||||||
export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right];
|
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 {
|
export class MetaBeltBaseBuilding extends MetaBuilding {
|
||||||
getHasDirectionLockAvailable() {
|
getHasDirectionLockAvailable() {
|
||||||
return true;
|
return true;
|
||||||
@ -23,7 +43,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
|||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
const beltSpeed = root.hubGoals.getBeltBaseSpeed(enumLayer.regular);
|
const beltSpeed = root.hubGoals.getBeltBaseSpeed();
|
||||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +63,21 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
|||||||
return null;
|
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
|
* Creates the entity at the given location
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
@ -53,34 +88,6 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
|
|||||||
direction: enumDirection.top, // updated later
|
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) {
|
updateVariants(entity, rotationVariant) {
|
||||||
entity.components.Belt.direction = arrayBeltVariantToRotation[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 bottomDirection = enumAngleToDirection[(rotation + 180) % 360];
|
||||||
const leftDirection = enumAngleToDirection[(rotation + 270) % 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 hasBottomEjector = false;
|
||||||
let hasRightEjector = 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 { HubComponent } from "../components/hub";
|
||||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||||
import { UnremovableComponent } from "../components/unremovable";
|
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
|
import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins";
|
||||||
|
|
||||||
export class MetaHubBuilding extends MetaBuilding {
|
export class MetaHubBuilding extends MetaBuilding {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -33,6 +33,10 @@ export class MetaHubBuilding extends MetaBuilding {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIsRemovable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the entity at the given location
|
* Creates the entity at the given location
|
||||||
* @param {Entity} entity
|
* @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(
|
entity.addComponent(
|
||||||
new ItemAcceptorComponent({
|
new ItemAcceptorComponent({
|
||||||
slots: [
|
slots: [
|
||||||
|
@ -6,11 +6,13 @@ import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
|||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
import { T } from "../../translations";
|
import { T } from "../../translations";
|
||||||
import { round1Digit, round2Digits, formatItemsPerSecond } from "../../core/utils";
|
import { formatItemsPerSecond } from "../../core/utils";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumMinerVariants = { chainable: "chainable" };
|
export const enumMinerVariants = { chainable: "chainable" };
|
||||||
|
|
||||||
|
const overlayMatrix = [1, 1, 1, 1, 0, 1, 1, 1, 1];
|
||||||
|
|
||||||
export class MetaMinerBuilding extends MetaBuilding {
|
export class MetaMinerBuilding extends MetaBuilding {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("miner");
|
super("miner");
|
||||||
@ -41,6 +43,16 @@ export class MetaMinerBuilding extends MetaBuilding {
|
|||||||
return super.getAvailableVariants(root);
|
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
|
* Creates the entity at the given location
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { globalConfig } from "../../core/config";
|
|
||||||
import { enumDirection, Vector } from "../../core/vector";
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
||||||
import { GameRoot, enumLayer } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
import { T } from "../../translations";
|
import { T } from "../../translations";
|
||||||
import { formatItemsPerSecond } from "../../core/utils";
|
import { formatItemsPerSecond } from "../../core/utils";
|
||||||
|
import { BeltUnderlaysComponent } from "../components/belt_underlays";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumSplitterVariants = { compact: "compact", compactInverse: "compact-inverse" };
|
export const enumSplitterVariants = { compact: "compact", compactInverse: "compact-inverse" };
|
||||||
@ -88,6 +88,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
|
|||||||
slots: [], // set later
|
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 },
|
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
entity.components.ItemAcceptor.beltUnderlays = [
|
entity.components.BeltUnderlays.underlays = [
|
||||||
{ pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.regular },
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
{ pos: new Vector(1, 0), direction: enumDirection.top, layer: enumLayer.regular },
|
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||||
];
|
];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -143,8 +145,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
|
|||||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
entity.components.ItemAcceptor.beltUnderlays = [
|
entity.components.BeltUnderlays.underlays = [
|
||||||
{ pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.regular },
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
];
|
];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -25,13 +25,31 @@ export const enumUndergroundBeltVariantToTier = {
|
|||||||
[enumUndergroundBeltVariants.tier2]: 1,
|
[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 {
|
export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("underground_belt");
|
super("underground_belt");
|
||||||
}
|
}
|
||||||
|
|
||||||
getSilhouetteColor() {
|
getSilhouetteColor() {
|
||||||
return "#555";
|
return "#222";
|
||||||
}
|
}
|
||||||
|
|
||||||
getFlipOrientationAfterPlacement() {
|
getFlipOrientationAfterPlacement() {
|
||||||
@ -42,6 +60,16 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
|||||||
return true;
|
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 {GameRoot} root
|
||||||
* @param {string} variant
|
* @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",
|
white: "white",
|
||||||
uncolored: "uncolored",
|
uncolored: "uncolored",
|
||||||
|
|
||||||
black: "black",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
@ -26,8 +24,6 @@ export const enumColorToShortcode = {
|
|||||||
|
|
||||||
[enumColors.white]: "w",
|
[enumColors.white]: "w",
|
||||||
[enumColors.uncolored]: "u",
|
[enumColors.uncolored]: "u",
|
||||||
|
|
||||||
[enumColors.black]: "0",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @enum {enumColors} */
|
/** @enum {enumColors} */
|
||||||
@ -54,27 +50,9 @@ export const enumColorsToHexCode = {
|
|||||||
// blue + green + red
|
// blue + green + red
|
||||||
[enumColors.white]: "#ffffff",
|
[enumColors.white]: "#ffffff",
|
||||||
|
|
||||||
[enumColors.black]: "#31383a",
|
|
||||||
|
|
||||||
[enumColors.uncolored]: "#aaaaaa",
|
[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;
|
const c = enumColors;
|
||||||
/** @enum {Object.<string, string>} */
|
/** @enum {Object.<string, string>} */
|
||||||
export const enumColorMixingResults = {
|
export const enumColorMixingResults = {
|
||||||
@ -88,7 +66,6 @@ export const enumColorMixingResults = {
|
|||||||
[c.cyan]: c.white,
|
[c.cyan]: c.white,
|
||||||
|
|
||||||
[c.white]: c.white,
|
[c.white]: c.white,
|
||||||
[c.black]: c.red,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 0, 255, 0
|
// 0, 255, 0
|
||||||
@ -100,7 +77,6 @@ export const enumColorMixingResults = {
|
|||||||
[c.cyan]: c.cyan,
|
[c.cyan]: c.cyan,
|
||||||
|
|
||||||
[c.white]: c.white,
|
[c.white]: c.white,
|
||||||
[c.black]: c.green,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 0, 255, 0
|
// 0, 255, 0
|
||||||
@ -110,20 +86,17 @@ export const enumColorMixingResults = {
|
|||||||
[c.cyan]: c.cyan,
|
[c.cyan]: c.cyan,
|
||||||
|
|
||||||
[c.white]: c.white,
|
[c.white]: c.white,
|
||||||
[c.black]: c.blue,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 255, 255, 0
|
// 255, 255, 0
|
||||||
[c.yellow]: {
|
[c.yellow]: {
|
||||||
[c.purple]: c.white,
|
[c.purple]: c.white,
|
||||||
[c.cyan]: c.white,
|
[c.cyan]: c.white,
|
||||||
[c.black]: c.yellow,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 255, 0, 255
|
// 255, 0, 255
|
||||||
[c.purple]: {
|
[c.purple]: {
|
||||||
[c.cyan]: c.white,
|
[c.cyan]: c.white,
|
||||||
[c.black]: c.purple,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 0, 255, 255
|
// 0, 255, 255
|
||||||
@ -140,24 +113,12 @@ export const enumColorMixingResults = {
|
|||||||
[c.uncolored]: {
|
[c.uncolored]: {
|
||||||
// auto
|
// auto
|
||||||
},
|
},
|
||||||
|
|
||||||
[c.black]: {
|
|
||||||
// auto
|
|
||||||
[c.white]: c.uncolored,
|
|
||||||
[c.cyan]: c.cyan,
|
|
||||||
[c.uncolored]: c.uncolored,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create same color lookups
|
// Create same color lookups
|
||||||
for (const color in enumColors) {
|
for (const color in enumColors) {
|
||||||
enumColorMixingResults[color][color] = color;
|
enumColorMixingResults[color][color] = color;
|
||||||
|
enumColorMixingResults[color][c.white] = c.white;
|
||||||
// Anything with white is white again, except for black which creates gray
|
|
||||||
if (color !== enumColors.black) {
|
|
||||||
enumColorMixingResults[color][c.white] = c.white;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anything with uncolored is the same color
|
// Anything with uncolored is the same color
|
||||||
enumColorMixingResults[color][c.uncolored] = color;
|
enumColorMixingResults[color][c.uncolored] = color;
|
||||||
}
|
}
|
||||||
|
@ -44,3 +44,9 @@ export class Component extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
/* dev:end */
|
/* 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 { ItemAcceptorComponent } from "./components/item_acceptor";
|
||||||
import { MinerComponent } from "./components/miner";
|
import { MinerComponent } from "./components/miner";
|
||||||
import { ItemProcessorComponent } from "./components/item_processor";
|
import { ItemProcessorComponent } from "./components/item_processor";
|
||||||
import { ReplaceableMapEntityComponent } from "./components/replaceable_map_entity";
|
|
||||||
import { UndergroundBeltComponent } from "./components/underground_belt";
|
import { UndergroundBeltComponent } from "./components/underground_belt";
|
||||||
import { UnremovableComponent } from "./components/unremovable";
|
|
||||||
import { HubComponent } from "./components/hub";
|
import { HubComponent } from "./components/hub";
|
||||||
import { StorageComponent } from "./components/storage";
|
import { StorageComponent } from "./components/storage";
|
||||||
import { EnergyGeneratorComponent } from "./components/energy_generator";
|
|
||||||
import { WiredPinsComponent } from "./components/wired_pins";
|
import { WiredPinsComponent } from "./components/wired_pins";
|
||||||
import { EnergyConsumerComponent } from "./components/energy_consumer";
|
import { BeltUnderlaysComponent } from "./components/belt_underlays";
|
||||||
|
|
||||||
export function initComponentRegistry() {
|
export function initComponentRegistry() {
|
||||||
gComponentRegistry.register(StaticMapEntityComponent);
|
gComponentRegistry.register(StaticMapEntityComponent);
|
||||||
@ -21,14 +18,11 @@ export function initComponentRegistry() {
|
|||||||
gComponentRegistry.register(ItemAcceptorComponent);
|
gComponentRegistry.register(ItemAcceptorComponent);
|
||||||
gComponentRegistry.register(MinerComponent);
|
gComponentRegistry.register(MinerComponent);
|
||||||
gComponentRegistry.register(ItemProcessorComponent);
|
gComponentRegistry.register(ItemProcessorComponent);
|
||||||
gComponentRegistry.register(ReplaceableMapEntityComponent);
|
|
||||||
gComponentRegistry.register(UndergroundBeltComponent);
|
gComponentRegistry.register(UndergroundBeltComponent);
|
||||||
gComponentRegistry.register(UnremovableComponent);
|
|
||||||
gComponentRegistry.register(HubComponent);
|
gComponentRegistry.register(HubComponent);
|
||||||
gComponentRegistry.register(StorageComponent);
|
gComponentRegistry.register(StorageComponent);
|
||||||
gComponentRegistry.register(EnergyGeneratorComponent);
|
|
||||||
gComponentRegistry.register(WiredPinsComponent);
|
gComponentRegistry.register(WiredPinsComponent);
|
||||||
gComponentRegistry.register(EnergyConsumerComponent);
|
gComponentRegistry.register(BeltUnderlaysComponent);
|
||||||
|
|
||||||
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS
|
||||||
|
|
||||||
|
@ -2,23 +2,44 @@ import { enumDirection, Vector } from "../../core/vector";
|
|||||||
import { types } from "../../savegame/serialization";
|
import { types } from "../../savegame/serialization";
|
||||||
import { BeltPath } from "../belt_path";
|
import { BeltPath } from "../belt_path";
|
||||||
import { Component } from "../component";
|
import { Component } from "../component";
|
||||||
import { Entity } from "../entity";
|
|
||||||
import { enumLayer } from "../root";
|
|
||||||
|
|
||||||
export const curvedBeltLength = /* Math.PI / 4 */ 0.78;
|
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 {
|
export class BeltComponent extends Component {
|
||||||
static getId() {
|
static getId() {
|
||||||
return "Belt";
|
return "Belt";
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSchema() {
|
|
||||||
// The followUpCache field is not serialized.
|
|
||||||
return {
|
|
||||||
direction: types.string,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
duplicateWithoutContents() {
|
duplicateWithoutContents() {
|
||||||
return new BeltComponent({ direction: this.direction });
|
return new BeltComponent({ direction: this.direction });
|
||||||
}
|
}
|
||||||
@ -33,9 +54,6 @@ export class BeltComponent extends Component {
|
|||||||
|
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
|
|
||||||
/** @type {Entity} */
|
|
||||||
this.followUpCache = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The path this belt is contained in, not serialized
|
* The path this belt is contained in, not serialized
|
||||||
* @type {BeltPath}
|
* @type {BeltPath}
|
||||||
@ -45,77 +63,59 @@ export class BeltComponent extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the effective length of this belt in tile space
|
* Returns the effective length of this belt in tile space
|
||||||
* @param {enumLayer} layer
|
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
getEffectiveLengthTiles(layer) {
|
getEffectiveLengthTiles() {
|
||||||
assert(layer, "no layer given");
|
|
||||||
if (layer === enumLayer.wires) {
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
return this.direction === enumDirection.top ? 1.0 : curvedBeltLength;
|
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
|
* 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)
|
* belt coordinates (-0.5|-0.5 to 0.5|0.5)
|
||||||
* @param {number} progress
|
* @param {number} progress
|
||||||
* @param {enumLayer} layer
|
|
||||||
* @returns {Vector}
|
* @returns {Vector}
|
||||||
*/
|
*/
|
||||||
transformBeltToLocalSpace(progress, layer) {
|
transformBeltToLocalSpace(progress) {
|
||||||
assert(progress >= 0.0, "Invalid progress ( < 0): " + 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 enumDirection.right: {
|
||||||
case enumLayer.regular: {
|
assert(progress <= curvedBeltLength + 0.02, "Invalid progress 2: " + progress);
|
||||||
switch (this.direction) {
|
const arcProgress = (progress / curvedBeltLength) * 0.5 * Math.PI;
|
||||||
case enumDirection.top:
|
return new Vector(0.5 - 0.5 * Math.cos(arcProgress), 0.5 - 0.5 * Math.sin(arcProgress));
|
||||||
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 enumLayer.wires: {
|
case enumDirection.left: {
|
||||||
const pow = 0.5;
|
assert(progress <= curvedBeltLength + 0.02, "Invalid progress 3: " + progress);
|
||||||
switch (this.direction) {
|
const arcProgress = (progress / curvedBeltLength) * 0.5 * Math.PI;
|
||||||
case enumDirection.top:
|
return new Vector(-0.5 + 0.5 * Math.cos(arcProgress), 0.5 - 0.5 * Math.sin(arcProgress));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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 { Component } from "../component";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
|
||||||
import { types } from "../../savegame/serialization";
|
|
||||||
|
|
||||||
export class HubComponent extends Component {
|
export class HubComponent extends Component {
|
||||||
static getId() {
|
static getId() {
|
||||||
return "Hub";
|
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 { types } from "../../savegame/serialization";
|
||||||
import { BaseItem, enumItemType } from "../base_item";
|
import { BaseItem, enumItemType } from "../base_item";
|
||||||
import { Component } from "../component";
|
import { Component } from "../component";
|
||||||
import { enumLayer } from "../root";
|
|
||||||
|
|
||||||
/** @typedef {{
|
/** @typedef {{
|
||||||
* pos: Vector,
|
* pos: Vector,
|
||||||
* directions: enumDirection[],
|
* directions: enumDirection[],
|
||||||
* layer: enumLayer,
|
|
||||||
* filter?: enumItemType
|
* filter?: enumItemType
|
||||||
* }} ItemAcceptorSlot */
|
* }} ItemAcceptorSlot */
|
||||||
|
|
||||||
@ -22,7 +20,6 @@ import { enumLayer } from "../root";
|
|||||||
/** @typedef {{
|
/** @typedef {{
|
||||||
* pos: Vector,
|
* pos: Vector,
|
||||||
* directions: enumDirection[],
|
* directions: enumDirection[],
|
||||||
* layer?: enumLayer,
|
|
||||||
* filter?: enumItemType
|
* filter?: enumItemType
|
||||||
* }} ItemAcceptorSlotConfig */
|
* }} ItemAcceptorSlotConfig */
|
||||||
|
|
||||||
@ -31,31 +28,6 @@ export class ItemAcceptorComponent extends Component {
|
|||||||
return "ItemAcceptor";
|
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() {
|
duplicateWithoutContents() {
|
||||||
const slotsCopy = [];
|
const slotsCopy = [];
|
||||||
for (let i = 0; i < this.slots.length; ++i) {
|
for (let i = 0; i < this.slots.length; ++i) {
|
||||||
@ -64,24 +36,11 @@ export class ItemAcceptorComponent extends Component {
|
|||||||
pos: slot.pos.copy(),
|
pos: slot.pos.copy(),
|
||||||
directions: slot.directions.slice(),
|
directions: slot.directions.slice(),
|
||||||
filter: slot.filter,
|
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({
|
return new ItemAcceptorComponent({
|
||||||
slots: slotsCopy,
|
slots: slotsCopy,
|
||||||
beltUnderlays: beltUnderlaysCopy,
|
|
||||||
animated: this.animated,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,23 +48,16 @@ export class ItemAcceptorComponent extends Component {
|
|||||||
*
|
*
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
* @param {Array<ItemAcceptorSlotConfig>} param0.slots The slots from which we accept items
|
* @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();
|
super();
|
||||||
|
|
||||||
this.animated = animated;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fixes belt animations
|
* Fixes belt animations
|
||||||
* @type {Array<{ item: BaseItem, slotIndex: number, animProgress: number, direction: enumDirection }>}
|
* @type {Array<{ item: BaseItem, slotIndex: number, animProgress: number, direction: enumDirection }>}
|
||||||
*/
|
*/
|
||||||
this.itemConsumptionAnimations = [];
|
this.itemConsumptionAnimations = [];
|
||||||
|
|
||||||
/* Which belt underlays to render */
|
|
||||||
this.beltUnderlays = beltUnderlays;
|
|
||||||
|
|
||||||
this.setSlots(slots);
|
this.setSlots(slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +73,6 @@ export class ItemAcceptorComponent extends Component {
|
|||||||
this.slots.push({
|
this.slots.push({
|
||||||
pos: slot.pos,
|
pos: slot.pos,
|
||||||
directions: slot.directions,
|
directions: slot.directions,
|
||||||
layer: slot.layer || enumLayer.regular,
|
|
||||||
|
|
||||||
// Which type of item to accept (shape | color | all) @see enumItemType
|
// Which type of item to accept (shape | color | all) @see enumItemType
|
||||||
filter: slot.filter,
|
filter: slot.filter,
|
||||||
@ -164,24 +115,21 @@ export class ItemAcceptorComponent extends Component {
|
|||||||
* @param {BaseItem} item
|
* @param {BaseItem} item
|
||||||
*/
|
*/
|
||||||
onItemAccepted(slotIndex, direction, item) {
|
onItemAccepted(slotIndex, direction, item) {
|
||||||
if (this.animated) {
|
this.itemConsumptionAnimations.push({
|
||||||
this.itemConsumptionAnimations.push({
|
item,
|
||||||
item,
|
slotIndex,
|
||||||
slotIndex,
|
direction,
|
||||||
direction,
|
animProgress: 0.0,
|
||||||
animProgress: 0.0,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to find a slot which accepts the current item
|
* Tries to find a slot which accepts the current item
|
||||||
* @param {Vector} targetLocalTile
|
* @param {Vector} targetLocalTile
|
||||||
* @param {enumDirection} fromLocalDirection
|
* @param {enumDirection} fromLocalDirection
|
||||||
* @param {enumLayer} layer
|
|
||||||
* @returns {ItemAcceptorLocatedSlot|null}
|
* @returns {ItemAcceptorLocatedSlot|null}
|
||||||
*/
|
*/
|
||||||
findMatchingSlot(targetLocalTile, fromLocalDirection, layer) {
|
findMatchingSlot(targetLocalTile, fromLocalDirection) {
|
||||||
// We need to invert our direction since the acceptor specifies *from* which direction
|
// 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.
|
// 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.
|
// E.g.: Ejector ejects into "right" direction but acceptor accepts from "left" direction.
|
||||||
@ -196,11 +144,6 @@ export class ItemAcceptorComponent extends Component {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the layer matches
|
|
||||||
if (slot.layer !== layer) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the acceptor slot accepts items from our direction
|
// Check if the acceptor slot accepts items from our direction
|
||||||
for (let i = 0; i < slot.directions.length; ++i) {
|
for (let i = 0; i < slot.directions.length; ++i) {
|
||||||
// const localDirection = targetStaticComp.localDirectionToWorld(slot.directions[l]);
|
// const localDirection = targetStaticComp.localDirectionToWorld(slot.directions[l]);
|
||||||
|
@ -5,15 +5,16 @@ import { types } from "../../savegame/serialization";
|
|||||||
import { gItemRegistry } from "../../core/global_registries";
|
import { gItemRegistry } from "../../core/global_registries";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { enumLayer } from "../root";
|
import { enumLayer } from "../root";
|
||||||
|
import { BeltPath } from "../belt_path";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* pos: Vector,
|
* pos: Vector,
|
||||||
* direction: enumDirection,
|
* direction: enumDirection,
|
||||||
* item: BaseItem,
|
* item: BaseItem,
|
||||||
* layer: enumLayer,
|
|
||||||
* progress: number?,
|
* progress: number?,
|
||||||
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
||||||
|
* cachedBeltPath?: BeltPath,
|
||||||
* cachedTargetEntity?: Entity
|
* cachedTargetEntity?: Entity
|
||||||
* }} ItemEjectorSlot
|
* }} ItemEjectorSlot
|
||||||
*/
|
*/
|
||||||
@ -24,19 +25,12 @@ export class ItemEjectorComponent extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
// The cachedDestSlot, cachedTargetEntity, and cachedConnectedSlots fields
|
// The cachedDestSlot, cachedTargetEntity fields are not serialized.
|
||||||
// are not serialized.
|
|
||||||
return {
|
return {
|
||||||
instantEject: types.bool,
|
|
||||||
slots: types.array(
|
slots: types.array(
|
||||||
types.structured({
|
types.structured({
|
||||||
pos: types.vector,
|
|
||||||
direction: types.enum(enumDirection),
|
|
||||||
item: types.nullable(types.obj(gItemRegistry)),
|
item: types.nullable(types.obj(gItemRegistry)),
|
||||||
progress: types.float,
|
progress: types.float,
|
||||||
|
|
||||||
// TODO: Migrate
|
|
||||||
layer: types.enum(enumLayer),
|
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@ -49,33 +43,24 @@ export class ItemEjectorComponent extends Component {
|
|||||||
slotsCopy.push({
|
slotsCopy.push({
|
||||||
pos: slot.pos.copy(),
|
pos: slot.pos.copy(),
|
||||||
direction: slot.direction,
|
direction: slot.direction,
|
||||||
layer: slot.layer,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ItemEjectorComponent({
|
return new ItemEjectorComponent({
|
||||||
slots: slotsCopy,
|
slots: slotsCopy,
|
||||||
instantEject: this.instantEject,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
* @param {Array<{pos: Vector, direction: enumDirection, layer?: enumLayer}>=} param0.slots The slots to eject on
|
* @param {Array<{pos: Vector, direction: enumDirection }>=} param0.slots The slots to eject on
|
||||||
* @param {boolean=} param0.instantEject If the ejection is instant
|
|
||||||
*/
|
*/
|
||||||
constructor({ slots = [], instantEject = false }) {
|
constructor({ slots = [] }) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// How long items take to eject
|
|
||||||
this.instantEject = instantEject;
|
|
||||||
|
|
||||||
this.setSlots(slots);
|
this.setSlots(slots);
|
||||||
|
|
||||||
/** @type {ItemEjectorSlot[]} */
|
|
||||||
this.cachedConnectedSlots = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this ejector slot is enabled
|
* 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) {
|
setSlots(slots) {
|
||||||
/** @type {Array<ItemEjectorSlot>} */
|
/** @type {Array<ItemEjectorSlot>} */
|
||||||
@ -95,7 +80,6 @@ export class ItemEjectorComponent extends Component {
|
|||||||
direction: slot.direction,
|
direction: slot.direction,
|
||||||
item: null,
|
item: null,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
layer: slot.layer || enumLayer.regular,
|
|
||||||
cachedDestSlot: null,
|
cachedDestSlot: null,
|
||||||
cachedTargetEntity: null,
|
cachedTargetEntity: null,
|
||||||
});
|
});
|
||||||
@ -104,11 +88,10 @@ export class ItemEjectorComponent extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns where this slot ejects to
|
* Returns where this slot ejects to
|
||||||
* @param {number} index
|
* @param {ItemEjectorSlot} slot
|
||||||
* @returns {Vector}
|
* @returns {Vector}
|
||||||
*/
|
*/
|
||||||
getSlotTargetLocalTile(index) {
|
getSlotTargetLocalTile(slot) {
|
||||||
const slot = this.slots[index];
|
|
||||||
const directionVector = enumDirectionToVector[slot.direction];
|
const directionVector = enumDirectionToVector[slot.direction];
|
||||||
return slot.pos.add(directionVector);
|
return slot.pos.add(directionVector);
|
||||||
}
|
}
|
||||||
@ -116,11 +99,10 @@ export class ItemEjectorComponent extends Component {
|
|||||||
/**
|
/**
|
||||||
* Returns whether any slot ejects to the given local tile
|
* Returns whether any slot ejects to the given local tile
|
||||||
* @param {Vector} tile
|
* @param {Vector} tile
|
||||||
* @param {enumLayer} layer
|
|
||||||
*/
|
*/
|
||||||
anySlotEjectsToLocalTile(tile, layer) {
|
anySlotEjectsToLocalTile(tile) {
|
||||||
for (let i = 0; i < this.slots.length; ++i) {
|
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;
|
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
|
* Returns the first free slot on this ejector or null if there is none
|
||||||
* @param {enumLayer} layer
|
|
||||||
* @returns {number?}
|
* @returns {number?}
|
||||||
*/
|
*/
|
||||||
getFirstFreeSlot(layer) {
|
getFirstFreeSlot() {
|
||||||
for (let i = 0; i < this.slots.length; ++i) {
|
for (let i = 0; i < this.slots.length; ++i) {
|
||||||
if (this.canEjectOnSlot(i) && this.slots[i].layer === layer) {
|
if (this.canEjectOnSlot(i)) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,7 +143,7 @@ export class ItemEjectorComponent extends Component {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.slots[slotIndex].item = item;
|
this.slots[slotIndex].item = item;
|
||||||
this.slots[slotIndex].progress = this.instantEject ? 1 : 0;
|
this.slots[slotIndex].progress = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ export const enumItemProcessorTypes = {
|
|||||||
painter: "painter",
|
painter: "painter",
|
||||||
painterDouble: "painterDouble",
|
painterDouble: "painterDouble",
|
||||||
painterQuad: "painterQuad",
|
painterQuad: "painterQuad",
|
||||||
advancedProcessor: "advancedProcessor",
|
|
||||||
hub: "hub",
|
hub: "hub",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,9 +29,6 @@ export class ItemProcessorComponent extends Component {
|
|||||||
static getSchema() {
|
static getSchema() {
|
||||||
return {
|
return {
|
||||||
nextOutputSlot: types.uint,
|
nextOutputSlot: types.uint,
|
||||||
type: types.enum(enumItemProcessorTypes),
|
|
||||||
inputsPerCharge: types.uint,
|
|
||||||
|
|
||||||
inputSlots: types.array(
|
inputSlots: types.array(
|
||||||
types.structured({
|
types.structured({
|
||||||
item: types.obj(gItemRegistry),
|
item: types.obj(gItemRegistry),
|
||||||
|
@ -15,7 +15,6 @@ export class MinerComponent extends Component {
|
|||||||
// cachedMinedItem is not serialized.
|
// cachedMinedItem is not serialized.
|
||||||
return {
|
return {
|
||||||
lastMiningTime: types.ufloat,
|
lastMiningTime: types.ufloat,
|
||||||
chainable: types.bool,
|
|
||||||
itemChainBuffer: types.array(types.obj(gItemRegistry)),
|
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 { types } from "../../savegame/serialization";
|
||||||
import { Component } from "../component";
|
import { Component } from "../component";
|
||||||
import { getBuildingDataFromCode } from "../building_codes";
|
import { getBuildingDataFromCode } from "../building_codes";
|
||||||
|
import { MetaBuilding } from "../meta_building";
|
||||||
|
|
||||||
export class StaticMapEntityComponent extends Component {
|
export class StaticMapEntityComponent extends Component {
|
||||||
static getId() {
|
static getId() {
|
||||||
@ -15,7 +16,6 @@ export class StaticMapEntityComponent extends Component {
|
|||||||
static getSchema() {
|
static getSchema() {
|
||||||
return {
|
return {
|
||||||
origin: types.tileVector,
|
origin: types.tileVector,
|
||||||
tileSize: types.tileVector,
|
|
||||||
rotation: types.float,
|
rotation: types.float,
|
||||||
originalRotation: 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 the sprite
|
||||||
* @returns {AtlasSprite}
|
* @returns {AtlasSprite}
|
||||||
@ -48,10 +56,17 @@ export class StaticMapEntityComponent extends Component {
|
|||||||
return getBuildingDataFromCode(this.code).silhouetteColor;
|
return getBuildingDataFromCode(this.code).silhouetteColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the meta building
|
||||||
|
* @returns {MetaBuilding}
|
||||||
|
*/
|
||||||
|
getMetaBuilding() {
|
||||||
|
return getBuildingDataFromCode(this.code).metaInstance;
|
||||||
|
}
|
||||||
|
|
||||||
duplicateWithoutContents() {
|
duplicateWithoutContents() {
|
||||||
return new StaticMapEntityComponent({
|
return new StaticMapEntityComponent({
|
||||||
origin: this.origin.copy(),
|
origin: this.origin.copy(),
|
||||||
tileSize: this.tileSize.copy(),
|
|
||||||
rotation: this.rotation,
|
rotation: this.rotation,
|
||||||
originalRotation: this.originalRotation,
|
originalRotation: this.originalRotation,
|
||||||
code: this.code,
|
code: this.code,
|
||||||
@ -81,7 +96,6 @@ export class StaticMapEntityComponent extends Component {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
this.tileSize = tileSize;
|
|
||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.originalRotation = originalRotation;
|
this.originalRotation = originalRotation;
|
||||||
@ -92,30 +106,16 @@ export class StaticMapEntityComponent extends Component {
|
|||||||
* @returns {Rectangle}
|
* @returns {Rectangle}
|
||||||
*/
|
*/
|
||||||
getTileSpaceBounds() {
|
getTileSpaceBounds() {
|
||||||
|
const size = this.getTileSize();
|
||||||
switch (this.rotation) {
|
switch (this.rotation) {
|
||||||
case 0:
|
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:
|
case 90:
|
||||||
return new Rectangle(
|
return new Rectangle(this.origin.x - size.y + 1, this.origin.y, size.y, size.x);
|
||||||
this.origin.x - this.tileSize.y + 1,
|
|
||||||
this.origin.y,
|
|
||||||
this.tileSize.y,
|
|
||||||
this.tileSize.x
|
|
||||||
);
|
|
||||||
case 180:
|
case 180:
|
||||||
return new Rectangle(
|
return new Rectangle(this.origin.x - size.x + 1, this.origin.y - size.y + 1, size.x, size.y);
|
||||||
this.origin.x - this.tileSize.x + 1,
|
|
||||||
this.origin.y - this.tileSize.y + 1,
|
|
||||||
this.tileSize.x,
|
|
||||||
this.tileSize.y
|
|
||||||
);
|
|
||||||
case 270:
|
case 270:
|
||||||
return new Rectangle(
|
return new Rectangle(this.origin.x, this.origin.y - size.x + 1, size.y, size.x);
|
||||||
this.origin.x,
|
|
||||||
this.origin.y - this.tileSize.x + 1,
|
|
||||||
this.tileSize.y,
|
|
||||||
this.tileSize.x
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
assert(false, "Invalid rotation");
|
assert(false, "Invalid rotation");
|
||||||
}
|
}
|
||||||
@ -186,34 +186,35 @@ export class StaticMapEntityComponent extends Component {
|
|||||||
let y = 0;
|
let y = 0;
|
||||||
let w = 0;
|
let w = 0;
|
||||||
let h = 0;
|
let h = 0;
|
||||||
|
const size = this.getTileSize();
|
||||||
|
|
||||||
switch (this.rotation) {
|
switch (this.rotation) {
|
||||||
case 0: {
|
case 0: {
|
||||||
x = this.origin.x;
|
x = this.origin.x;
|
||||||
y = this.origin.y;
|
y = this.origin.y;
|
||||||
w = this.tileSize.x;
|
w = size.x;
|
||||||
h = this.tileSize.y;
|
h = size.y;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 90: {
|
case 90: {
|
||||||
x = this.origin.x - this.tileSize.y + 1;
|
x = this.origin.x - size.y + 1;
|
||||||
y = this.origin.y;
|
y = this.origin.y;
|
||||||
w = this.tileSize.y;
|
w = size.y;
|
||||||
h = this.tileSize.x;
|
h = size.x;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 180: {
|
case 180: {
|
||||||
x = this.origin.x - this.tileSize.x + 1;
|
x = this.origin.x - size.x + 1;
|
||||||
y = this.origin.y - this.tileSize.y + 1;
|
y = this.origin.y - size.y + 1;
|
||||||
w = this.tileSize.x;
|
w = size.x;
|
||||||
h = this.tileSize.y;
|
h = size.y;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 270: {
|
case 270: {
|
||||||
x = this.origin.x;
|
x = this.origin.x;
|
||||||
y = this.origin.y - this.tileSize.x + 1;
|
y = this.origin.y - size.x + 1;
|
||||||
w = this.tileSize.y;
|
w = size.y;
|
||||||
h = this.tileSize.x;
|
h = size.x;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -233,19 +234,13 @@ export class StaticMapEntityComponent extends Component {
|
|||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
* @param {AtlasSprite} sprite
|
* @param {AtlasSprite} sprite
|
||||||
* @param {number=} extrudePixels How many pixels to extrude the 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
|
* @param {Vector=} overridePosition Whether to drwa the entity at a different location
|
||||||
*/
|
*/
|
||||||
drawSpriteOnFullEntityBounds(
|
drawSpriteOnFullEntityBounds(parameters, sprite, extrudePixels = 0, overridePosition = null) {
|
||||||
parameters,
|
|
||||||
sprite,
|
|
||||||
extrudePixels = 0,
|
|
||||||
clipping = true,
|
|
||||||
overridePosition = null
|
|
||||||
) {
|
|
||||||
if (!this.shouldBeDrawn(parameters) && !overridePosition) {
|
if (!this.shouldBeDrawn(parameters) && !overridePosition) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const size = this.getTileSize();
|
||||||
let worldX = this.origin.x * globalConfig.tileSize;
|
let worldX = this.origin.x * globalConfig.tileSize;
|
||||||
let worldY = this.origin.y * globalConfig.tileSize;
|
let worldY = this.origin.y * globalConfig.tileSize;
|
||||||
|
|
||||||
@ -258,10 +253,10 @@ export class StaticMapEntityComponent extends Component {
|
|||||||
// Early out, is faster
|
// Early out, is faster
|
||||||
sprite.drawCached(
|
sprite.drawCached(
|
||||||
parameters,
|
parameters,
|
||||||
worldX - extrudePixels * this.tileSize.x,
|
worldX - extrudePixels * size.x,
|
||||||
worldY - extrudePixels * this.tileSize.y,
|
worldY - extrudePixels * size.y,
|
||||||
globalConfig.tileSize * this.tileSize.x + 2 * extrudePixels * this.tileSize.x,
|
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
|
||||||
globalConfig.tileSize * this.tileSize.y + 2 * extrudePixels * this.tileSize.y,
|
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -273,10 +268,10 @@ export class StaticMapEntityComponent extends Component {
|
|||||||
|
|
||||||
sprite.drawCached(
|
sprite.drawCached(
|
||||||
parameters,
|
parameters,
|
||||||
-globalConfig.halfTileSize - extrudePixels * this.tileSize.x,
|
-globalConfig.halfTileSize - extrudePixels * size.x,
|
||||||
-globalConfig.halfTileSize - extrudePixels * this.tileSize.y,
|
-globalConfig.halfTileSize - extrudePixels * size.y,
|
||||||
globalConfig.tileSize * this.tileSize.x + 2 * extrudePixels * this.tileSize.x,
|
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
|
||||||
globalConfig.tileSize * this.tileSize.y + 2 * extrudePixels * this.tileSize.y,
|
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -12,10 +12,8 @@ export class StorageComponent extends Component {
|
|||||||
|
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
return {
|
return {
|
||||||
maximumStorage: types.uint,
|
|
||||||
storedCount: types.uint,
|
storedCount: types.uint,
|
||||||
storedItem: types.nullable(types.obj(gItemRegistry)),
|
storedItem: types.nullable(types.obj(gItemRegistry)),
|
||||||
overlayOpacity: types.ufloat,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,9 +26,7 @@ export class UndergroundBeltComponent extends Component {
|
|||||||
|
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
return {
|
return {
|
||||||
mode: types.enum(enumUndergroundBeltMode),
|
|
||||||
pendingItems: types.array(types.pair(types.obj(gItemRegistry), types.float)),
|
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
|
// Notice: We assume that for all items the travel distance is the same
|
||||||
const maxItemsInTunnel =
|
const maxItemsInTunnel = (2 + travelDistance) / globalConfig.itemSpacingOnBelts;
|
||||||
(2 + travelDistance) / globalConfig.beltItemSpacingByLayer[enumLayer.regular];
|
|
||||||
if (this.pendingItems.length >= maxItemsInTunnel) {
|
if (this.pendingItems.length >= maxItemsInTunnel) {
|
||||||
// Simulate a real belt which gets full at some point
|
// Simulate a real belt which gets full at some point
|
||||||
return false;
|
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.
|
// 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.
|
// So instead of adding 1 we add 0.5 only.
|
||||||
// Additionally it takes 1 tile for the acceptor which we just add on top.
|
// Additionally it takes 1 tile for the acceptor which we just add on top.
|
||||||
const travelDuration =
|
const travelDuration = (travelDistance + 1.5) / beltSpeed / globalConfig.itemSpacingOnBelts;
|
||||||
(travelDistance + 1.5) / beltSpeed / globalConfig.beltItemSpacingByLayer[enumLayer.regular];
|
|
||||||
|
|
||||||
this.pendingItems.push([item, travelDuration]);
|
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",
|
negativeEnergyEjector: "negativeEnergyEjector",
|
||||||
positiveEnergyAcceptor: "positiveEnergyAcceptor",
|
positiveEnergyAcceptor: "positiveEnergyAcceptor",
|
||||||
negativeEnergyAcceptor: "negativeEnergyAcceptor",
|
negativeEnergyAcceptor: "negativeEnergyAcceptor",
|
||||||
|
logicalEjector: "logicalEjector",
|
||||||
|
logicalAcceptor: "logicalAcceptor",
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @typedef {{
|
/** @typedef {{
|
||||||
@ -27,17 +29,6 @@ export class WiredPinsComponent extends Component {
|
|||||||
return "WiredPins";
|
return "WiredPins";
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSchema() {
|
|
||||||
return {
|
|
||||||
slots: types.array(
|
|
||||||
types.structured({
|
|
||||||
pos: types.vector,
|
|
||||||
type: types.enum(enumPinSlotType),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
|
@ -349,8 +349,9 @@ export class GameCore {
|
|||||||
// Gather context and save all state
|
// Gather context and save all state
|
||||||
const context = root.context;
|
const context = root.context;
|
||||||
context.save();
|
context.save();
|
||||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
if (G_IS_DEV) {
|
||||||
context.clearRect(0, 0, window.innerWidth * 3, window.innerHeight * 3);
|
context.fillStyle = "#a10000";
|
||||||
|
context.fillRect(0, 0, window.innerWidth * 3, window.innerHeight * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute optimal zoom level and atlas scale
|
// Compute optimal zoom level and atlas scale
|
||||||
@ -394,49 +395,36 @@ export class GameCore {
|
|||||||
// -----
|
// -----
|
||||||
|
|
||||||
// BG / Map Resources / Belt Backgrounds
|
// BG / Map Resources / Belt Backgrounds
|
||||||
root.map.drawBackground(params);
|
|
||||||
|
|
||||||
if (!this.root.camera.getIsMapOverlayActive()) {
|
if (!this.root.camera.getIsMapOverlayActive()) {
|
||||||
|
root.map.drawBackground(params);
|
||||||
|
|
||||||
// Underlays for splitters / balancers
|
// Underlays for splitters / balancers
|
||||||
systems.itemAcceptor.drawUnderlays(params, enumLayer.regular);
|
systems.beltUnderlays.drawUnderlays(params);
|
||||||
|
|
||||||
// Belt items
|
// Belt items
|
||||||
systems.belt.drawLayerBeltItems(params, enumLayer.regular);
|
systems.belt.drawBeltItems(params);
|
||||||
|
|
||||||
// Items being ejected / accepted currently (animations)
|
// Items being ejected / accepted currently (animations)
|
||||||
systems.itemEjector.drawLayer(params, enumLayer.regular);
|
systems.itemEjector.draw(params);
|
||||||
systems.itemAcceptor.drawLayer(params, enumLayer.regular);
|
systems.itemAcceptor.draw(params);
|
||||||
}
|
|
||||||
|
|
||||||
// Miner & Static map entities
|
// Miner & Static map entities
|
||||||
root.map.drawForeground(params);
|
root.map.drawForeground(params);
|
||||||
|
|
||||||
if (!this.root.camera.getIsMapOverlayActive()) {
|
|
||||||
// HUB Overlay
|
// HUB Overlay
|
||||||
systems.hub.draw(params);
|
systems.hub.draw(params);
|
||||||
|
|
||||||
// Energy generator overlay
|
|
||||||
systems.energyGenerator.draw(params);
|
|
||||||
|
|
||||||
// Storage items
|
// Storage items
|
||||||
systems.storage.draw(params);
|
systems.storage.draw(params);
|
||||||
|
|
||||||
// Energy consumer (Battery icons)
|
// Green wires overlay
|
||||||
systems.energyConsumer.draw(params);
|
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()) {
|
if (this.root.currentLayer === enumLayer.wires && !this.root.camera.getIsMapOverlayActive()) {
|
||||||
// Belt sprites & Static map entities
|
// 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);
|
|
||||||
|
|
||||||
root.map.drawWiresForegroundLayer(params);
|
root.map.drawWiresForegroundLayer(params);
|
||||||
|
|
||||||
// pins
|
// pins
|
||||||
|