diff --git a/.travis.yml b/.travis.yml index c7003323..85abbc20 100644 --- a/.travis.yml +++ b/.travis.yml @@ -111,7 +111,7 @@ jobs: env: YARN_GPG=no before_install: - choco install git-lfs -y -f || echo "0" # choco fails but git-lfs is still installed - - choco install ffmpeg + - choco install ffmpeg --version=4.2.3 - export PATH=/C/ProgramData/chocolatey/lib/ffmpeg/tools/ffmpeg/bin:$PATH - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/avicap32.dll -P /C/Windows/System32/ - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/msvfw32.dll -P /C/Windows/System32/ @@ -129,7 +129,7 @@ jobs: env: YARN_GPG=no before_install: - choco install git-lfs -y -f || echo "0" # choco fails but git-lfs is still installed - - choco install ffmpeg + - choco install ffmpeg --version=4.2.3 - choco install wget - export PATH=/C/ProgramData/chocolatey/lib/ffmpeg/tools/ffmpeg/bin:$PATH - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/avicap32.dll -P /C/Windows/System32/ diff --git a/artwork/buildings/buildings_2x2.psd b/artwork/buildings/buildings_2x2.psd index 7017b4d9..00f31093 100644 --- a/artwork/buildings/buildings_2x2.psd +++ b/artwork/buildings/buildings_2x2.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7931a4ba2104bbf7de3a1332617d5a056e34c55bab751821a168a83e0b91793 -size 683910 +oid sha256:9d8a4254944d83882513a062e77406419d94c3058a7ad657a5a318d45cc5443f +size 902131 diff --git a/artwork/steam/announcement.png b/artwork/steam/announcement.png index cc0861db..f9596b0c 100644 Binary files a/artwork/steam/announcement.png and b/artwork/steam/announcement.png differ diff --git a/artwork/steam/announcement.psd b/artwork/steam/announcement.psd index 518a49ac..8ab98ce0 100644 --- a/artwork/steam/announcement.psd +++ b/artwork/steam/announcement.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae83b8805191eeba13016e40216fc4781ac3958e6886769ce8677ea28370d13e -size 239706 +oid sha256:747bd9b4a0b1d7cc3ba80ae1b899eed02f97615f9cffcdac2230fecf416cf8cf +size 188701 diff --git a/artwork/steam/devlog.png b/artwork/steam/devlog.png index 9abdefe1..20778f64 100644 Binary files a/artwork/steam/devlog.png and b/artwork/steam/devlog.png differ diff --git a/artwork/steam/devlog.psd b/artwork/steam/devlog.psd index 7cd9b0a7..f4f962e3 100644 --- a/artwork/steam/devlog.psd +++ b/artwork/steam/devlog.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40d458f800344d819ad4d3f38943f53f399258369ee5c20f6583d1b49847465f -size 188255 +oid sha256:4846f685f3e242444706e1f295347f6c6205984edff75ddf32fbb5a7d497c598 +size 215617 diff --git a/artwork/ui/toolbar-icons.psd b/artwork/ui/toolbar-icons.psd index e8efcfa3..9b453e15 100644 --- a/artwork/ui/toolbar-icons.psd +++ b/artwork/ui/toolbar-icons.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:037d27409bf78b828470e553ee0f29304eda24c46598151bb4b9c69c66a6c6c1 -size 146363 +oid sha256:27c8d1eaa48beb7187e37244bd6ef7f43429be84ac88fffa96a4d72eb0e6565b +size 158809 diff --git a/gulp/gulpfile.js b/gulp/gulpfile.js index f2fdd9d4..2cf51cc3 100644 --- a/gulp/gulpfile.js +++ b/gulp/gulpfile.js @@ -104,11 +104,14 @@ gulp.task("utils.requireCleanWorkingTree", cb => { let output = $.trim(execSync("git status -su").toString("ascii")).replace(/\r/gi, "").split("\n"); // Filter files which are OK to be untracked - output = output.filter(x => x.indexOf(".local.js") < 0); + output = output + .map(x => x.replace(/[\r\n]+/gi, "")) + .filter(x => x.indexOf(".local.js") < 0) + .filter(x => x.length > 0); if (output.length > 0) { console.error("\n\nYou have unstaged changes, please commit everything first!"); console.error("Unstaged files:"); - console.error(output.join("\n")); + console.error(output.map(x => "'" + x + "'").join("\n")); process.exit(1); } cb(); diff --git a/gulp/webpack.production.config.js b/gulp/webpack.production.config.js index f80a69d2..fb0fbe93 100644 --- a/gulp/webpack.production.config.js +++ b/gulp/webpack.production.config.js @@ -105,6 +105,8 @@ module.exports = ({ passes: 2, module: true, pure_funcs: [ + "Math.radians", + "Math.degrees", "Math.round", "Math.ceil", "Math.floor", @@ -119,21 +121,6 @@ module.exports = ({ "Math.sign", "Math.pow", "Math.atan2", - - "Math_round", - "Math_ceil", - "Math_floor", - "Math_sqrt", - "Math_hypot", - "Math_abs", - "Math_max", - "Math_min", - "Math_sin", - "Math_cos", - "Math_tan", - "Math_sign", - "Math_pow", - "Math_atan2", ], toplevel: true, unsafe_math: true, diff --git a/res/ui/building_icons/energy_generator.png b/res/ui/building_icons/energy_generator.png new file mode 100644 index 00000000..ab724a7b Binary files /dev/null and b/res/ui/building_icons/energy_generator.png differ diff --git a/res/ui/languages/zh-TW.svg b/res/ui/languages/zh-TW.svg index f89219a0..c3dab661 100644 --- a/res/ui/languages/zh-TW.svg +++ b/res/ui/languages/zh-TW.svg @@ -1,14 +1,15 @@ - - - - - - - - - + + + + + + diff --git a/res_built/atlas/atlas0_10.json b/res_built/atlas/atlas0_10.json index 8aeef6ae..342ea730 100644 --- a/res_built/atlas/atlas0_10.json +++ b/res_built/atlas/atlas0_10.json @@ -2,7 +2,7 @@ "sprites/belt/forward_0.png": { - "frame": {"x":204,"y":26,"w":13,"h":13}, + "frame": {"x":205,"y":110,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -10,7 +10,7 @@ }, "sprites/belt/forward_1.png": { - "frame": {"x":221,"y":26,"w":13,"h":13}, + "frame": {"x":222,"y":110,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -18,7 +18,7 @@ }, "sprites/belt/forward_2.png": { - "frame": {"x":238,"y":26,"w":13,"h":13}, + "frame": {"x":311,"y":26,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -26,7 +26,7 @@ }, "sprites/belt/forward_3.png": { - "frame": {"x":255,"y":26,"w":13,"h":13}, + "frame": {"x":379,"y":26,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -34,7 +34,7 @@ }, "sprites/belt/forward_4.png": { - "frame": {"x":272,"y":26,"w":13,"h":13}, + "frame": {"x":376,"y":66,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -42,7 +42,183 @@ }, "sprites/belt/forward_5.png": { - "frame": {"x":245,"y":112,"w":13,"h":13}, + "frame": {"x":396,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_6.png": +{ + "frame": {"x":393,"y":66,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_7.png": +{ + "frame": {"x":413,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_8.png": +{ + "frame": {"x":410,"y":66,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_9.png": +{ + "frame": {"x":430,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_10.png": +{ + "frame": {"x":239,"y":110,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_11.png": +{ + "frame": {"x":206,"y":68,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_12.png": +{ + "frame": {"x":223,"y":68,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_13.png": +{ + "frame": {"x":240,"y":68,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_14.png": +{ + "frame": {"x":243,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_15.png": +{ + "frame": {"x":260,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_16.png": +{ + "frame": {"x":277,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_17.png": +{ + "frame": {"x":257,"y":66,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_18.png": +{ + "frame": {"x":274,"y":66,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_19.png": +{ + "frame": {"x":294,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_20.png": +{ + "frame": {"x":328,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_21.png": +{ + "frame": {"x":291,"y":66,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_22.png": +{ + "frame": {"x":308,"y":66,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_23.png": +{ + "frame": {"x":325,"y":66,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_24.png": +{ + "frame": {"x":345,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_25.png": +{ + "frame": {"x":362,"y":26,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_26.png": +{ + "frame": {"x":342,"y":66,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/forward_27.png": +{ + "frame": {"x":359,"y":66,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -50,7 +226,7 @@ }, "sprites/belt/left_0.png": { - "frame": {"x":262,"y":112,"w":13,"h":13}, + "frame": {"x":443,"y":83,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -58,7 +234,7 @@ }, "sprites/belt/left_1.png": { - "frame": {"x":279,"y":112,"w":13,"h":13}, + "frame": {"x":450,"y":63,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -66,7 +242,7 @@ }, "sprites/belt/left_2.png": { - "frame": {"x":296,"y":112,"w":13,"h":13}, + "frame": {"x":484,"y":3,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -74,7 +250,7 @@ }, "sprites/belt/left_3.png": { - "frame": {"x":289,"y":49,"w":13,"h":13}, + "frame": {"x":501,"y":54,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -82,7 +258,7 @@ }, "sprites/belt/left_4.png": { - "frame": {"x":306,"y":49,"w":13,"h":13}, + "frame": {"x":513,"y":37,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -90,7 +266,183 @@ }, "sprites/belt/left_5.png": { - "frame": {"x":323,"y":49,"w":13,"h":13}, + "frame": {"x":515,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_6.png": +{ + "frame": {"x":518,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_7.png": +{ + "frame": {"x":485,"y":105,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_8.png": +{ + "frame": {"x":497,"y":88,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_9.png": +{ + "frame": {"x":511,"y":71,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_10.png": +{ + "frame": {"x":460,"y":80,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_11.png": +{ + "frame": {"x":433,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_12.png": +{ + "frame": {"x":447,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_13.png": +{ + "frame": {"x":450,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_14.png": +{ + "frame": {"x":462,"y":37,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_15.png": +{ + "frame": {"x":464,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_16.png": +{ + "frame": {"x":467,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_17.png": +{ + "frame": {"x":467,"y":54,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_18.png": +{ + "frame": {"x":479,"y":37,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_19.png": +{ + "frame": {"x":481,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_20.png": +{ + "frame": {"x":463,"y":97,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_21.png": +{ + "frame": {"x":477,"y":71,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_22.png": +{ + "frame": {"x":484,"y":54,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_23.png": +{ + "frame": {"x":496,"y":37,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_24.png": +{ + "frame": {"x":498,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_25.png": +{ + "frame": {"x":501,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_26.png": +{ + "frame": {"x":480,"y":88,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/left_27.png": +{ + "frame": {"x":494,"y":71,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -98,7 +450,7 @@ }, "sprites/belt/right_0.png": { - "frame": {"x":313,"y":112,"w":13,"h":13}, + "frame": {"x":518,"y":54,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -106,7 +458,7 @@ }, "sprites/belt/right_1.png": { - "frame": {"x":330,"y":112,"w":13,"h":13}, + "frame": {"x":530,"y":37,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -114,7 +466,7 @@ }, "sprites/belt/right_2.png": { - "frame": {"x":340,"y":49,"w":13,"h":13}, + "frame": {"x":531,"y":88,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -122,7 +474,7 @@ }, "sprites/belt/right_3.png": { - "frame": {"x":347,"y":112,"w":13,"h":13}, + "frame": {"x":569,"y":54,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -130,7 +482,7 @@ }, "sprites/belt/right_4.png": { - "frame": {"x":357,"y":49,"w":13,"h":13}, + "frame": {"x":581,"y":37,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -138,7 +490,183 @@ }, "sprites/belt/right_5.png": { - "frame": {"x":374,"y":49,"w":13,"h":13}, + "frame": {"x":583,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_6.png": +{ + "frame": {"x":586,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_7.png": +{ + "frame": {"x":553,"y":105,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_8.png": +{ + "frame": {"x":565,"y":88,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_9.png": +{ + "frame": {"x":579,"y":71,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_10.png": +{ + "frame": {"x":532,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_11.png": +{ + "frame": {"x":535,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_12.png": +{ + "frame": {"x":502,"y":105,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_13.png": +{ + "frame": {"x":514,"y":88,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_14.png": +{ + "frame": {"x":528,"y":71,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_15.png": +{ + "frame": {"x":535,"y":54,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_16.png": +{ + "frame": {"x":547,"y":37,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_17.png": +{ + "frame": {"x":549,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_18.png": +{ + "frame": {"x":552,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_19.png": +{ + "frame": {"x":519,"y":105,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_20.png": +{ + "frame": {"x":545,"y":71,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_21.png": +{ + "frame": {"x":552,"y":54,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_22.png": +{ + "frame": {"x":564,"y":37,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_23.png": +{ + "frame": {"x":566,"y":20,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_24.png": +{ + "frame": {"x":569,"y":3,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_25.png": +{ + "frame": {"x":536,"y":105,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_26.png": +{ + "frame": {"x":548,"y":88,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13} +}, +"sprites/belt/right_27.png": +{ + "frame": {"x":562,"y":71,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -146,7 +674,7 @@ }, "sprites/blueprints/belt_left.png": { - "frame": {"x":391,"y":48,"w":13,"h":13}, + "frame": {"x":586,"y":54,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -154,7 +682,7 @@ }, "sprites/blueprints/belt_right.png": { - "frame": {"x":404,"y":25,"w":13,"h":13}, + "frame": {"x":596,"y":71,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -162,7 +690,7 @@ }, "sprites/blueprints/belt_top.png": { - "frame": {"x":400,"y":85,"w":13,"h":13}, + "frame": {"x":570,"y":105,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -178,15 +706,23 @@ }, "sprites/blueprints/cutter.png": { - "frame": {"x":245,"y":66,"w":36,"h":19}, + "frame": {"x":284,"y":3,"w":36,"h":19}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":36,"h":19}, "sourceSize": {"w":38,"h":19} }, +"sprites/blueprints/energy_generator.png": +{ + "frame": {"x":126,"y":79,"w":36,"h":38}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":0,"w":36,"h":38}, + "sourceSize": {"w":38,"h":38} +}, "sprites/blueprints/miner-chainable.png": { - "frame": {"x":285,"y":66,"w":19,"h":19}, + "frame": {"x":364,"y":3,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -194,7 +730,7 @@ }, "sprites/blueprints/miner.png": { - "frame": {"x":285,"y":89,"w":19,"h":19}, + "frame": {"x":302,"y":106,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -202,7 +738,7 @@ }, "sprites/blueprints/mixer.png": { - "frame": {"x":204,"y":68,"w":37,"h":19}, + "frame": {"x":247,"y":85,"w":37,"h":19}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":37,"h":19}, @@ -242,7 +778,7 @@ }, "sprites/blueprints/rotater-ccw.png": { - "frame": {"x":289,"y":26,"w":19,"h":19}, + "frame": {"x":325,"y":106,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -250,7 +786,7 @@ }, "sprites/blueprints/rotater.png": { - "frame": {"x":312,"y":26,"w":19,"h":19}, + "frame": {"x":328,"y":83,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -258,7 +794,7 @@ }, "sprites/blueprints/splitter-compact-inverse.png": { - "frame": {"x":327,"y":3,"w":19,"h":19}, + "frame": {"x":348,"y":106,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -266,7 +802,7 @@ }, "sprites/blueprints/splitter-compact.png": { - "frame": {"x":308,"y":66,"w":19,"h":19}, + "frame": {"x":351,"y":83,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -274,7 +810,7 @@ }, "sprites/blueprints/splitter.png": { - "frame": {"x":249,"y":43,"w":36,"h":19}, + "frame": {"x":324,"y":3,"w":36,"h":19}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":36,"h":19}, @@ -282,7 +818,7 @@ }, "sprites/blueprints/stacker.png": { - "frame": {"x":204,"y":91,"w":37,"h":19}, + "frame": {"x":243,"y":3,"w":37,"h":19}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":37,"h":19}, @@ -290,7 +826,7 @@ }, "sprites/blueprints/trash-storage.png": { - "frame": {"x":126,"y":79,"w":35,"h":38}, + "frame": {"x":204,"y":3,"w":35,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":35,"h":38}, @@ -298,7 +834,7 @@ }, "sprites/blueprints/trash.png": { - "frame": {"x":335,"y":26,"w":19,"h":19}, + "frame": {"x":371,"y":106,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -306,7 +842,7 @@ }, "sprites/blueprints/underground_belt_entry-tier2.png": { - "frame": {"x":381,"y":26,"w":19,"h":18}, + "frame": {"x":417,"y":106,"w":19,"h":18}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":19,"h":18}, @@ -314,7 +850,7 @@ }, "sprites/blueprints/underground_belt_entry.png": { - "frame": {"x":354,"y":89,"w":19,"h":16}, + "frame": {"x":256,"y":108,"w":19,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":3,"w":19,"h":16}, @@ -322,7 +858,7 @@ }, "sprites/blueprints/underground_belt_exit-tier2.png": { - "frame": {"x":364,"y":109,"w":19,"h":16}, + "frame": {"x":279,"y":108,"w":19,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":16}, @@ -330,7 +866,7 @@ }, "sprites/blueprints/underground_belt_exit.png": { - "frame": {"x":377,"y":66,"w":19,"h":16}, + "frame": {"x":440,"y":105,"w":19,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":16}, @@ -338,7 +874,7 @@ }, "sprites/buildings/belt_left.png": { - "frame": {"x":262,"y":112,"w":13,"h":13}, + "frame": {"x":582,"y":88,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -346,7 +882,7 @@ }, "sprites/buildings/belt_right.png": { - "frame": {"x":313,"y":112,"w":13,"h":13}, + "frame": {"x":587,"y":105,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -354,7 +890,7 @@ }, "sprites/buildings/belt_top.png": { - "frame": {"x":204,"y":26,"w":13,"h":13}, + "frame": {"x":205,"y":110,"w":13,"h":13}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, @@ -370,12 +906,20 @@ }, "sprites/buildings/cutter.png": { - "frame": {"x":287,"y":3,"w":36,"h":19}, + "frame": {"x":288,"y":83,"w":36,"h":19}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":36,"h":19}, "sourceSize": {"w":38,"h":19} }, +"sprites/buildings/energy_generator.png": +{ + "frame": {"x":166,"y":45,"w":36,"h":38}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":0,"w":36,"h":38}, + "sourceSize": {"w":38,"h":38} +}, "sprites/buildings/hub.png": { "frame": {"x":3,"y":3,"w":75,"h":76}, @@ -386,7 +930,7 @@ }, "sprites/buildings/miner-chainable.png": { - "frame": {"x":350,"y":3,"w":19,"h":19}, + "frame": {"x":374,"y":83,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -394,7 +938,7 @@ }, "sprites/buildings/miner.png": { - "frame": {"x":308,"y":89,"w":19,"h":19}, + "frame": {"x":394,"y":106,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -402,7 +946,7 @@ }, "sprites/buildings/mixer.png": { - "frame": {"x":208,"y":43,"w":37,"h":19}, + "frame": {"x":248,"y":43,"w":37,"h":19}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":37,"h":19}, @@ -418,7 +962,7 @@ }, "sprites/buildings/painter-mirrored.png": { - "frame": {"x":166,"y":45,"w":38,"h":19}, + "frame": {"x":205,"y":87,"w":38,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":38,"h":19}, @@ -434,7 +978,7 @@ }, "sprites/buildings/painter.png": { - "frame": {"x":204,"y":3,"w":38,"h":19}, + "frame": {"x":206,"y":45,"w":38,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":38,"h":19}, @@ -442,7 +986,7 @@ }, "sprites/buildings/rotater-ccw.png": { - "frame": {"x":331,"y":66,"w":19,"h":19}, + "frame": {"x":370,"y":43,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -450,7 +994,7 @@ }, "sprites/buildings/rotater.png": { - "frame": {"x":331,"y":89,"w":19,"h":19}, + "frame": {"x":387,"y":3,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -458,7 +1002,7 @@ }, "sprites/buildings/splitter-compact-inverse.png": { - "frame": {"x":354,"y":66,"w":19,"h":19}, + "frame": {"x":393,"y":43,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -466,7 +1010,7 @@ }, "sprites/buildings/splitter-compact.png": { - "frame": {"x":358,"y":26,"w":19,"h":19}, + "frame": {"x":410,"y":3,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -474,7 +1018,7 @@ }, "sprites/buildings/splitter.png": { - "frame": {"x":245,"y":89,"w":36,"h":19}, + "frame": {"x":330,"y":43,"w":36,"h":19}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":36,"h":19}, @@ -482,7 +1026,7 @@ }, "sprites/buildings/stacker.png": { - "frame": {"x":246,"y":3,"w":37,"h":19}, + "frame": {"x":289,"y":43,"w":37,"h":19}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":37,"h":19}, @@ -490,7 +1034,7 @@ }, "sprites/buildings/trash-storage.png": { - "frame": {"x":165,"y":72,"w":35,"h":38}, + "frame": {"x":166,"y":87,"w":35,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":35,"h":38}, @@ -498,7 +1042,7 @@ }, "sprites/buildings/trash.png": { - "frame": {"x":373,"y":3,"w":19,"h":19}, + "frame": {"x":397,"y":83,"w":19,"h":19}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":19}, @@ -506,7 +1050,7 @@ }, "sprites/buildings/underground_belt_entry-tier2.png": { - "frame": {"x":396,"y":3,"w":19,"h":18}, + "frame": {"x":420,"y":83,"w":19,"h":18}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":19,"h":18}, @@ -514,7 +1058,7 @@ }, "sprites/buildings/underground_belt_entry.png": { - "frame": {"x":377,"y":86,"w":19,"h":16}, + "frame": {"x":416,"y":43,"w":19,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":3,"w":19,"h":16}, @@ -522,7 +1066,7 @@ }, "sprites/buildings/underground_belt_exit-tier2.png": { - "frame": {"x":387,"y":106,"w":19,"h":16}, + "frame": {"x":427,"y":63,"w":19,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":16}, @@ -530,7 +1074,7 @@ }, "sprites/buildings/underground_belt_exit.png": { - "frame": {"x":400,"y":65,"w":19,"h":16}, + "frame": {"x":439,"y":43,"w":19,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":19,"h":16}, @@ -578,7 +1122,7 @@ }, "sprites/misc/deletion_marker.png": { - "frame": {"x":187,"y":114,"w":10,"h":10}, + "frame": {"x":598,"y":37,"w":10,"h":10}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":10,"h":10}, @@ -602,7 +1146,7 @@ }, "sprites/misc/slot_bad_arrow.png": { - "frame": {"x":187,"y":114,"w":10,"h":10}, + "frame": {"x":598,"y":37,"w":10,"h":10}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":10,"h":10}, @@ -610,7 +1154,7 @@ }, "sprites/misc/slot_good_arrow.png": { - "frame": {"x":201,"y":114,"w":10,"h":10}, + "frame": {"x":599,"y":88,"w":10,"h":10}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":10,"h":10}, @@ -618,7 +1162,7 @@ }, "sprites/misc/storage_overlay.png": { - "frame": {"x":165,"y":114,"w":18,"h":9}, + "frame": {"x":463,"y":114,"w":18,"h":9}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":18,"h":9}, @@ -637,8 +1181,8 @@ "version": "1.0", "image": "atlas0_10.png", "format": "RGBA8888", - "size": {"w":422,"h":128}, + "size": {"w":612,"h":128}, "scale": "0.1", - "smartupdate": "$TexturePacker:SmartUpdate:8985524bba1a3f16cecab3c03b6aaa06:d614d20bfce033d8a8ab0990af085d16:f159918d23e5952766c6d23ab52278c6$" + "smartupdate": "$TexturePacker:SmartUpdate:dc34796c028235bbc69e5b8d854254ca:2765d0b8c8bcbb7a4aaaf1104853ad41:8778749683c68f53155587e6d831729a$" } } diff --git a/res_built/atlas/atlas0_10.png b/res_built/atlas/atlas0_10.png index adab820d..d774ecac 100644 Binary files a/res_built/atlas/atlas0_10.png and b/res_built/atlas/atlas0_10.png differ diff --git a/res_built/atlas/atlas0_100.json b/res_built/atlas/atlas0_100.json index ba671365..05465e84 100644 --- a/res_built/atlas/atlas0_100.json +++ b/res_built/atlas/atlas0_100.json @@ -2,7 +2,7 @@ "sprites/belt/forward_0.png": { - "frame": {"x":1876,"y":1166,"w":100,"h":126}, + "frame": {"x":1202,"y":1870,"w":100,"h":126}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, @@ -10,7 +10,7 @@ }, "sprites/belt/forward_1.png": { - "frame": {"x":1876,"y":1296,"w":100,"h":126}, + "frame": {"x":1871,"y":250,"w":100,"h":126}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, @@ -18,31 +18,103 @@ }, "sprites/belt/forward_2.png": { - "frame": {"x":1869,"y":1543,"w":100,"h":126}, + "frame": {"x":1631,"y":1865,"w":100,"h":126}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/forward_3.png": +"sprites/belt/forward_10.png": { - "frame": {"x":1857,"y":395,"w":100,"h":126}, + "frame": {"x":1450,"y":1487,"w":100,"h":126}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/forward_4.png": +"sprites/belt/forward_11.png": { - "frame": {"x":1865,"y":1690,"w":100,"h":126}, + "frame": {"x":1423,"y":1849,"w":100,"h":126}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/forward_5.png": +"sprites/belt/forward_12.png": { - "frame": {"x":920,"y":1370,"w":100,"h":126}, + "frame": {"x":1455,"y":1617,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_13.png": +{ + "frame": {"x":1793,"y":1624,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_14.png": +{ + "frame": {"x":1897,"y":1624,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_15.png": +{ + "frame": {"x":1559,"y":1735,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_16.png": +{ + "frame": {"x":1663,"y":1735,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_17.png": +{ + "frame": {"x":1767,"y":1754,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_18.png": +{ + "frame": {"x":1871,"y":1754,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_19.png": +{ + "frame": {"x":1527,"y":1865,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_20.png": +{ + "frame": {"x":1735,"y":1884,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_21.png": +{ + "frame": {"x":1839,"y":1884,"w":100,"h":126}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, @@ -50,7 +122,7 @@ }, "sprites/belt/left_0.png": { - "frame": {"x":1870,"y":1426,"w":113,"h":113}, + "frame": {"x":239,"y":1898,"w":113,"h":113}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, @@ -58,95 +130,71 @@ }, "sprites/belt/left_1.png": { - "frame": {"x":920,"y":1500,"w":113,"h":113}, + "frame": {"x":1085,"y":1870,"w":113,"h":113}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/left_2.png": +"sprites/belt/left_10.png": { - "frame": {"x":935,"y":1617,"w":113,"h":113}, + "frame": {"x":1306,"y":1849,"w":113,"h":113}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/left_3.png": +"sprites/belt/left_11.png": { - "frame": {"x":935,"y":1734,"w":113,"h":113}, + "frame": {"x":1873,"y":133,"w":113,"h":113}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/left_4.png": +"sprites/belt/left_12.png": { - "frame": {"x":1052,"y":1721,"w":113,"h":113}, + "frame": {"x":1582,"y":1501,"w":113,"h":113}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/left_5.png": +"sprites/belt/left_13.png": { - "frame": {"x":1169,"y":1721,"w":113,"h":113}, + "frame": {"x":1699,"y":1501,"w":113,"h":113}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/right_0.png": +"sprites/belt/left_14.png": { - "frame": {"x":1286,"y":1721,"w":113,"h":113}, + "frame": {"x":1816,"y":1507,"w":113,"h":113}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/right_1.png": +"sprites/belt/left_15.png": { - "frame": {"x":1403,"y":1721,"w":113,"h":113}, + "frame": {"x":1559,"y":1618,"w":113,"h":113}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, "sourceSize": {"w":126,"h":126} }, -"sprites/belt/right_2.png": +"sprites/belt/left_16.png": { - "frame": {"x":1520,"y":1721,"w":113,"h":113}, + "frame": {"x":1676,"y":1618,"w":113,"h":113}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, - "sourceSize": {"w":126,"h":126} -}, -"sprites/belt/right_3.png": -{ - "frame": {"x":1052,"y":1552,"w":113,"h":113}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, - "sourceSize": {"w":126,"h":126} -}, -"sprites/belt/right_4.png": -{ - "frame": {"x":1169,"y":1552,"w":113,"h":113}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, - "sourceSize": {"w":126,"h":126} -}, -"sprites/belt/right_5.png": -{ - "frame": {"x":1286,"y":1554,"w":113,"h":113}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, "sourceSize": {"w":126,"h":126} }, "sprites/blueprints/belt_left.png": { - "frame": {"x":1873,"y":122,"w":114,"h":114}, + "frame": {"x":3,"y":1898,"w":114,"h":114}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":12,"w":114,"h":114}, @@ -154,15 +202,15 @@ }, "sprites/blueprints/belt_right.png": { - "frame": {"x":1873,"y":3,"w":114,"h":115}, + "frame": {"x":121,"y":1898,"w":114,"h":114}, "rotated": false, "trimmed": true, - "spriteSourceSize": {"x":12,"y":11,"w":114,"h":115}, + "spriteSourceSize": {"x":12,"y":12,"w":114,"h":114}, "sourceSize": {"w":126,"h":126} }, "sprites/blueprints/belt_top.png": { - "frame": {"x":1871,"y":240,"w":102,"h":126}, + "frame": {"x":1873,"y":3,"w":102,"h":126}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":12,"y":0,"w":102,"h":126}, @@ -178,15 +226,23 @@ }, "sprites/blueprints/cutter.png": { - "frame": {"x":1433,"y":785,"w":341,"h":191}, + "frame": {"x":391,"y":1707,"w":341,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":23,"y":0,"w":341,"h":191}, "sourceSize": {"w":384,"h":192} }, +"sprites/blueprints/energy_generator.png": +{ + "frame": {"x":735,"y":590,"w":339,"h":373}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":25,"y":11,"w":339,"h":373}, + "sourceSize": {"w":384,"h":384} +}, "sprites/blueprints/miner-chainable.png": { - "frame": {"x":749,"y":1655,"w":182,"h":190}, + "frame": {"x":1842,"y":783,"w":182,"h":190}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":182,"h":190}, @@ -194,7 +250,7 @@ }, "sprites/blueprints/miner.png": { - "frame": {"x":1690,"y":1174,"w":182,"h":190}, + "frame": {"x":1843,"y":977,"w":182,"h":190}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":182,"h":190}, @@ -202,7 +258,7 @@ }, "sprites/blueprints/mixer.png": { - "frame": {"x":1123,"y":590,"w":347,"h":191}, + "frame": {"x":726,"y":1162,"w":347,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":18,"y":0,"w":347,"h":191}, @@ -218,7 +274,7 @@ }, "sprites/blueprints/painter-mirrored.png": { - "frame": {"x":1485,"y":3,"w":384,"h":192}, + "frame": {"x":3,"y":1702,"w":384,"h":192}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":384,"h":192}, @@ -234,7 +290,7 @@ }, "sprites/blueprints/painter.png": { - "frame": {"x":1483,"y":199,"w":384,"h":192}, + "frame": {"x":1485,"y":3,"w":384,"h":192}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":384,"h":192}, @@ -242,7 +298,7 @@ }, "sprites/blueprints/rotater-ccw.png": { - "frame": {"x":922,"y":1174,"w":189,"h":191}, + "frame": {"x":1458,"y":956,"w":189,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":189,"h":191}, @@ -250,7 +306,7 @@ }, "sprites/blueprints/rotater.png": { - "frame": {"x":1115,"y":1173,"w":189,"h":191}, + "frame": {"x":1811,"y":588,"w":189,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":189,"h":191}, @@ -258,7 +314,7 @@ }, "sprites/blueprints/splitter-compact-inverse.png": { - "frame": {"x":1756,"y":980,"w":188,"h":182}, + "frame": {"x":1651,"y":977,"w":188,"h":182}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":188,"h":182}, @@ -266,7 +322,7 @@ }, "sprites/blueprints/splitter-compact.png": { - "frame": {"x":1306,"y":1368,"w":185,"h":182}, + "frame": {"x":1649,"y":1163,"w":185,"h":182}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":4,"w":185,"h":182}, @@ -274,7 +330,7 @@ }, "sprites/blueprints/splitter.png": { - "frame": {"x":726,"y":979,"w":340,"h":191}, + "frame": {"x":1114,"y":966,"w":340,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":23,"y":0,"w":340,"h":191}, @@ -282,7 +338,7 @@ }, "sprites/blueprints/stacker.png": { - "frame": {"x":1474,"y":590,"w":347,"h":191}, + "frame": {"x":724,"y":1357,"w":347,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":18,"y":0,"w":347,"h":191}, @@ -298,7 +354,7 @@ }, "sprites/blueprints/trash.png": { - "frame": {"x":726,"y":1174,"w":192,"h":192}, + "frame": {"x":1419,"y":590,"w":192,"h":192}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":192,"h":192}, @@ -306,7 +362,7 @@ }, "sprites/blueprints/underground_belt_entry-tier2.png": { - "frame": {"x":1683,"y":1368,"w":183,"h":166}, + "frame": {"x":1419,"y":786,"w":183,"h":166}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":26,"w":183,"h":166}, @@ -314,7 +370,7 @@ }, "sprites/blueprints/underground_belt_entry.png": { - "frame": {"x":192,"y":1702,"w":182,"h":148}, + "frame": {"x":1646,"y":1349,"w":182,"h":148}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":44,"w":182,"h":148}, @@ -322,7 +378,7 @@ }, "sprites/blueprints/underground_belt_exit-tier2.png": { - "frame": {"x":3,"y":1702,"w":185,"h":148}, + "frame": {"x":1457,"y":1335,"w":185,"h":148}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":185,"h":148}, @@ -330,31 +386,15 @@ }, "sprites/blueprints/underground_belt_exit.png": { - "frame": {"x":1683,"y":1538,"w":182,"h":148}, + "frame": {"x":1832,"y":1355,"w":182,"h":148}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":182,"h":148}, "sourceSize": {"w":192,"h":192} }, -"sprites/buildings/belt_left.png": -{ - "frame": {"x":1870,"y":1426,"w":113,"h":113}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, - "sourceSize": {"w":126,"h":126} -}, -"sprites/buildings/belt_right.png": -{ - "frame": {"x":1286,"y":1721,"w":113,"h":113}, - "rotated": false, - "trimmed": true, - "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, - "sourceSize": {"w":126,"h":126} -}, "sprites/buildings/belt_top.png": { - "frame": {"x":1876,"y":1166,"w":100,"h":126}, + "frame": {"x":1202,"y":1870,"w":100,"h":126}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, @@ -370,12 +410,20 @@ }, "sprites/buildings/cutter.png": { - "frame": {"x":1070,"y":979,"w":339,"h":190}, + "frame": {"x":736,"y":1552,"w":339,"h":190}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":24,"y":0,"w":339,"h":190}, "sourceSize": {"w":384,"h":192} }, +"sprites/buildings/energy_generator.png": +{ + "frame": {"x":1078,"y":590,"w":337,"h":372}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":26,"y":12,"w":337,"h":372}, + "sourceSize": {"w":384,"h":384} +}, "sprites/buildings/hub.png": { "frame": {"x":3,"y":3,"w":728,"h":730}, @@ -386,7 +434,7 @@ }, "sprites/buildings/miner-chainable.png": { - "frame": {"x":1825,"y":590,"w":179,"h":188}, + "frame": {"x":1258,"y":1355,"w":179,"h":188}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":1,"w":179,"h":188}, @@ -394,7 +442,7 @@ }, "sprites/buildings/miner.png": { - "frame": {"x":1778,"y":785,"w":179,"h":189}, + "frame": {"x":1075,"y":1357,"w":179,"h":189}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":0,"w":179,"h":189}, @@ -402,7 +450,7 @@ }, "sprites/buildings/mixer.png": { - "frame": {"x":735,"y":785,"w":345,"h":190}, + "frame": {"x":736,"y":1746,"w":345,"h":190}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":19,"y":0,"w":345,"h":190}, @@ -418,7 +466,7 @@ }, "sprites/buildings/painter-mirrored.png": { - "frame": {"x":735,"y":590,"w":384,"h":191}, + "frame": {"x":1483,"y":199,"w":384,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":384,"h":191}, @@ -434,7 +482,7 @@ }, "sprites/buildings/painter.png": { - "frame": {"x":1469,"y":395,"w":384,"h":191}, + "frame": {"x":726,"y":967,"w":384,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":384,"h":191}, @@ -442,7 +490,7 @@ }, "sprites/buildings/rotater-ccw.png": { - "frame": {"x":1308,"y":1174,"w":187,"h":190}, + "frame": {"x":1832,"y":394,"w":187,"h":190}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":187,"h":190}, @@ -450,7 +498,7 @@ }, "sprites/buildings/rotater.png": { - "frame": {"x":1499,"y":1174,"w":187,"h":190}, + "frame": {"x":1651,"y":783,"w":187,"h":190}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":187,"h":190}, @@ -458,7 +506,7 @@ }, "sprites/buildings/splitter-compact-inverse.png": { - "frame": {"x":1115,"y":1368,"w":187,"h":180}, + "frame": {"x":1458,"y":1151,"w":187,"h":180}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":5,"w":187,"h":180}, @@ -466,7 +514,7 @@ }, "sprites/buildings/splitter-compact.png": { - "frame": {"x":1495,"y":1368,"w":184,"h":180}, + "frame": {"x":1838,"y":1171,"w":184,"h":180}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":5,"w":184,"h":180}, @@ -474,7 +522,7 @@ }, "sprites/buildings/splitter.png": { - "frame": {"x":1413,"y":980,"w":339,"h":190}, + "frame": {"x":1114,"y":1161,"w":339,"h":190}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":24,"y":0,"w":339,"h":190}, @@ -482,7 +530,7 @@ }, "sprites/buildings/stacker.png": { - "frame": {"x":1084,"y":785,"w":345,"h":190}, + "frame": {"x":1483,"y":394,"w":345,"h":190}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":19,"y":0,"w":345,"h":190}, @@ -498,7 +546,7 @@ }, "sprites/buildings/trash.png": { - "frame": {"x":724,"y":1370,"w":192,"h":191}, + "frame": {"x":1615,"y":588,"w":192,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":192,"h":191}, @@ -506,7 +554,7 @@ }, "sprites/buildings/underground_belt_entry-tier2.png": { - "frame": {"x":1495,"y":1552,"w":181,"h":165}, + "frame": {"x":1079,"y":1550,"w":181,"h":165}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":27,"w":181,"h":165}, @@ -514,7 +562,7 @@ }, "sprites/buildings/underground_belt_entry.png": { - "frame": {"x":564,"y":1707,"w":181,"h":147}, + "frame": {"x":1085,"y":1719,"w":181,"h":147}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":45,"w":181,"h":147}, @@ -522,7 +570,7 @@ }, "sprites/buildings/underground_belt_exit-tier2.png": { - "frame": {"x":378,"y":1707,"w":182,"h":147}, + "frame": {"x":1264,"y":1547,"w":182,"h":147}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":0,"w":182,"h":147}, @@ -530,7 +578,7 @@ }, "sprites/buildings/underground_belt_exit.png": { - "frame": {"x":1680,"y":1690,"w":181,"h":147}, + "frame": {"x":1270,"y":1698,"w":181,"h":147}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":0,"w":181,"h":147}, @@ -538,7 +586,7 @@ }, "sprites/debug/acceptor_slot.png": { - "frame": {"x":1961,"y":782,"w":50,"h":64}, + "frame": {"x":1975,"y":250,"w":50,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":0,"w":50,"h":64}, @@ -546,7 +594,7 @@ }, "sprites/debug/ejector_slot.png": { - "frame": {"x":1961,"y":850,"w":50,"h":64}, + "frame": {"x":1975,"y":318,"w":50,"h":64}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":0,"w":50,"h":64}, @@ -554,7 +602,7 @@ }, "sprites/map_overview/belt_forward.png": { - "frame": {"x":1637,"y":1721,"w":24,"h":32}, + "frame": {"x":706,"y":1902,"w":24,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":24,"h":32}, @@ -562,7 +610,7 @@ }, "sprites/map_overview/belt_left.png": { - "frame": {"x":1893,"y":525,"w":28,"h":28}, + "frame": {"x":1077,"y":1162,"w":28,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":28,"h":28}, @@ -570,7 +618,7 @@ }, "sprites/map_overview/belt_right.png": { - "frame": {"x":1080,"y":1669,"w":28,"h":28}, + "frame": {"x":1979,"y":3,"w":28,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":28,"h":28}, @@ -578,7 +626,7 @@ }, "sprites/misc/deletion_marker.png": { - "frame": {"x":1403,"y":1554,"w":82,"h":82}, + "frame": {"x":620,"y":1902,"w":82,"h":82}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":7,"w":82,"h":82}, @@ -586,7 +634,7 @@ }, "sprites/misc/hub_direction_indicator.png": { - "frame": {"x":1857,"y":525,"w":32,"h":32}, + "frame": {"x":1615,"y":783,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -594,7 +642,7 @@ }, "sprites/misc/lock_direction_indicator.png": { - "frame": {"x":1961,"y":918,"w":48,"h":30}, + "frame": {"x":1306,"y":1966,"w":48,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":48,"h":30}, @@ -602,7 +650,7 @@ }, "sprites/misc/slot_bad_arrow.png": { - "frame": {"x":1403,"y":1554,"w":82,"h":82}, + "frame": {"x":620,"y":1902,"w":82,"h":82}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":7,"w":82,"h":82}, @@ -610,7 +658,7 @@ }, "sprites/misc/slot_good_arrow.png": { - "frame": {"x":1024,"y":1369,"w":80,"h":96}, + "frame": {"x":536,"y":1902,"w":80,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":0,"w":80,"h":96}, @@ -618,7 +666,7 @@ }, "sprites/misc/storage_overlay.png": { - "frame": {"x":724,"y":1565,"w":176,"h":86}, + "frame": {"x":356,"y":1902,"w":176,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":4,"w":176,"h":86}, @@ -626,7 +674,7 @@ }, "sprites/misc/waypoint.png": { - "frame": {"x":1052,"y":1669,"w":24,"h":32}, + "frame": {"x":1554,"y":1487,"w":24,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":24,"h":32}, @@ -637,8 +685,9 @@ "version": "1.0", "image": "atlas0_100.png", "format": "RGBA8888", - "size": {"w":2014,"h":1857}, + "size": {"w":2028,"h":2015}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:8985524bba1a3f16cecab3c03b6aaa06:d614d20bfce033d8a8ab0990af085d16:f159918d23e5952766c6d23ab52278c6$" + "related_multi_packs": [ "atlas1_100.json" ], + "smartupdate": "$TexturePacker:SmartUpdate:dc34796c028235bbc69e5b8d854254ca:2765d0b8c8bcbb7a4aaaf1104853ad41:8778749683c68f53155587e6d831729a$" } } diff --git a/res_built/atlas/atlas0_100.png b/res_built/atlas/atlas0_100.png index 181adc30..2fb0cffb 100644 Binary files a/res_built/atlas/atlas0_100.png and b/res_built/atlas/atlas0_100.png differ diff --git a/res_built/atlas/atlas0_25.json b/res_built/atlas/atlas0_25.json index a4aac8ed..2065027f 100644 --- a/res_built/atlas/atlas0_25.json +++ b/res_built/atlas/atlas0_25.json @@ -2,7 +2,7 @@ "sprites/belt/forward_0.png": { - "frame": {"x":3,"y":1010,"w":28,"h":32}, + "frame": {"x":191,"y":155,"w":28,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, @@ -10,7 +10,7 @@ }, "sprites/belt/forward_1.png": { - "frame": {"x":3,"y":967,"w":28,"h":32}, + "frame": {"x":223,"y":155,"w":28,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, @@ -18,7 +18,7 @@ }, "sprites/belt/forward_2.png": { - "frame": {"x":3,"y":1046,"w":28,"h":32}, + "frame": {"x":865,"y":55,"w":28,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, @@ -26,7 +26,7 @@ }, "sprites/belt/forward_3.png": { - "frame": {"x":35,"y":1044,"w":28,"h":32}, + "frame": {"x":1121,"y":55,"w":28,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, @@ -34,7 +34,7 @@ }, "sprites/belt/forward_4.png": { - "frame": {"x":67,"y":1044,"w":28,"h":32}, + "frame": {"x":1149,"y":3,"w":28,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, @@ -42,7 +42,183 @@ }, "sprites/belt/forward_5.png": { - "frame": {"x":133,"y":1053,"w":28,"h":32}, + "frame": {"x":1087,"y":194,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_6.png": +{ + "frame": {"x":1108,"y":139,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_7.png": +{ + "frame": {"x":1122,"y":91,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_8.png": +{ + "frame": {"x":1153,"y":51,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_9.png": +{ + "frame": {"x":1181,"y":3,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_10.png": +{ + "frame": {"x":255,"y":155,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_11.png": +{ + "frame": {"x":287,"y":155,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_12.png": +{ + "frame": {"x":319,"y":155,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_13.png": +{ + "frame": {"x":351,"y":155,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_14.png": +{ + "frame": {"x":673,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_15.png": +{ + "frame": {"x":705,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_16.png": +{ + "frame": {"x":737,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_17.png": +{ + "frame": {"x":769,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_18.png": +{ + "frame": {"x":801,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_19.png": +{ + "frame": {"x":833,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_20.png": +{ + "frame": {"x":897,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_21.png": +{ + "frame": {"x":929,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_22.png": +{ + "frame": {"x":961,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_23.png": +{ + "frame": {"x":993,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_24.png": +{ + "frame": {"x":1025,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_25.png": +{ + "frame": {"x":1057,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_26.png": +{ + "frame": {"x":1089,"y":55,"w":28,"h":32}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/forward_27.png": +{ + "frame": {"x":1090,"y":91,"w":28,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, @@ -50,7 +226,7 @@ }, "sprites/belt/left_0.png": { - "frame": {"x":143,"y":883,"w":30,"h":30}, + "frame": {"x":1140,"y":139,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, @@ -58,7 +234,7 @@ }, "sprites/belt/left_1.png": { - "frame": {"x":143,"y":917,"w":30,"h":30}, + "frame": {"x":1123,"y":211,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, @@ -66,7 +242,7 @@ }, "sprites/belt/left_2.png": { - "frame": {"x":143,"y":951,"w":30,"h":30}, + "frame": {"x":1208,"y":107,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, @@ -74,7 +250,7 @@ }, "sprites/belt/left_3.png": { - "frame": {"x":35,"y":960,"w":30,"h":30}, + "frame": {"x":1315,"y":3,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, @@ -82,7 +258,7 @@ }, "sprites/belt/left_4.png": { - "frame": {"x":69,"y":967,"w":30,"h":30}, + "frame": {"x":1225,"y":175,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, @@ -90,7 +266,183 @@ }, "sprites/belt/left_5.png": { - "frame": {"x":35,"y":994,"w":30,"h":30}, + "frame": {"x":1253,"y":139,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_6.png": +{ + "frame": {"x":1276,"y":105,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_7.png": +{ + "frame": {"x":1290,"y":71,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_8.png": +{ + "frame": {"x":1321,"y":37,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_9.png": +{ + "frame": {"x":1349,"y":3,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_10.png": +{ + "frame": {"x":1151,"y":173,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_11.png": +{ + "frame": {"x":1157,"y":207,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_12.png": +{ + "frame": {"x":1154,"y":87,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_13.png": +{ + "frame": {"x":1185,"y":39,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_14.png": +{ + "frame": {"x":1213,"y":3,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_15.png": +{ + "frame": {"x":1174,"y":121,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_16.png": +{ + "frame": {"x":1188,"y":73,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_17.png": +{ + "frame": {"x":1219,"y":37,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_18.png": +{ + "frame": {"x":1247,"y":3,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_19.png": +{ + "frame": {"x":1185,"y":155,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_20.png": +{ + "frame": {"x":1222,"y":71,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_21.png": +{ + "frame": {"x":1253,"y":37,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_22.png": +{ + "frame": {"x":1281,"y":3,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_23.png": +{ + "frame": {"x":1191,"y":189,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_24.png": +{ + "frame": {"x":1219,"y":141,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_25.png": +{ + "frame": {"x":1242,"y":105,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_26.png": +{ + "frame": {"x":1256,"y":71,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/left_27.png": +{ + "frame": {"x":1287,"y":37,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, @@ -98,7 +450,7 @@ }, "sprites/belt/right_0.png": { - "frame": {"x":103,"y":967,"w":30,"h":30}, + "frame": {"x":1259,"y":173,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, @@ -106,7 +458,7 @@ }, "sprites/belt/right_1.png": { - "frame": {"x":137,"y":985,"w":30,"h":30}, + "frame": {"x":1287,"y":139,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, @@ -114,7 +466,7 @@ }, "sprites/belt/right_2.png": { - "frame": {"x":171,"y":997,"w":30,"h":30}, + "frame": {"x":1417,"y":3,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, @@ -122,7 +474,7 @@ }, "sprites/belt/right_3.png": { - "frame": {"x":205,"y":1010,"w":30,"h":30}, + "frame": {"x":1361,"y":173,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, @@ -130,7 +482,7 @@ }, "sprites/belt/right_4.png": { - "frame": {"x":99,"y":1001,"w":30,"h":30}, + "frame": {"x":1389,"y":139,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, @@ -138,7 +490,183 @@ }, "sprites/belt/right_5.png": { - "frame": {"x":133,"y":1019,"w":30,"h":30}, + "frame": {"x":1412,"y":105,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_6.png": +{ + "frame": {"x":1426,"y":71,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_7.png": +{ + "frame": {"x":1446,"y":105,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_8.png": +{ + "frame": {"x":1368,"y":207,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_9.png": +{ + "frame": {"x":1395,"y":173,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_10.png": +{ + "frame": {"x":1310,"y":105,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_11.png": +{ + "frame": {"x":1324,"y":71,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_12.png": +{ + "frame": {"x":1355,"y":37,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_13.png": +{ + "frame": {"x":1383,"y":3,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_14.png": +{ + "frame": {"x":1266,"y":207,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_15.png": +{ + "frame": {"x":1293,"y":173,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_16.png": +{ + "frame": {"x":1321,"y":139,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_17.png": +{ + "frame": {"x":1344,"y":105,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_18.png": +{ + "frame": {"x":1358,"y":71,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_19.png": +{ + "frame": {"x":1389,"y":37,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_20.png": +{ + "frame": {"x":1451,"y":3,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_21.png": +{ + "frame": {"x":1300,"y":207,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_22.png": +{ + "frame": {"x":1327,"y":173,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_23.png": +{ + "frame": {"x":1355,"y":139,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_24.png": +{ + "frame": {"x":1378,"y":105,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_25.png": +{ + "frame": {"x":1392,"y":71,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_26.png": +{ + "frame": {"x":1423,"y":37,"w":30,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, + "sourceSize": {"w":32,"h":32} +}, +"sprites/belt/right_27.png": +{ + "frame": {"x":1334,"y":207,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, @@ -146,7 +674,7 @@ }, "sprites/blueprints/belt_left.png": { - "frame": {"x":99,"y":1035,"w":30,"h":30}, + "frame": {"x":1423,"y":139,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, @@ -154,7 +682,7 @@ }, "sprites/blueprints/belt_right.png": { - "frame": {"x":167,"y":1031,"w":30,"h":30}, + "frame": {"x":1402,"y":207,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, @@ -162,7 +690,7 @@ }, "sprites/blueprints/belt_top.png": { - "frame": {"x":201,"y":1044,"w":28,"h":32}, + "frame": {"x":1119,"y":175,"w":28,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, @@ -170,7 +698,7 @@ }, "sprites/blueprints/cutter-quad.png": { - "frame": {"x":3,"y":296,"w":184,"h":48}, + "frame": {"x":195,"y":191,"w":184,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":184,"h":48}, @@ -178,15 +706,23 @@ }, "sprites/blueprints/cutter.png": { - "frame": {"x":155,"y":419,"w":87,"h":48}, + "frame": {"x":844,"y":91,"w":87,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":87,"h":48}, "sourceSize": {"w":96,"h":48} }, +"sprites/blueprints/energy_generator.png": +{ + "frame": {"x":472,"y":103,"w":87,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":2,"w":87,"h":94}, + "sourceSize": {"w":96,"h":96} +}, "sprites/blueprints/miner-chainable.png": { - "frame": {"x":195,"y":3,"w":47,"h":48}, + "frame": {"x":1006,"y":143,"w":47,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":48}, @@ -194,7 +730,7 @@ }, "sprites/blueprints/miner.png": { - "frame": {"x":195,"y":55,"w":47,"h":48}, + "frame": {"x":1039,"y":91,"w":47,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":48}, @@ -202,7 +738,7 @@ }, "sprites/blueprints/mixer.png": { - "frame": {"x":3,"y":808,"w":89,"h":48}, + "frame": {"x":751,"y":91,"w":89,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":89,"h":48}, @@ -210,7 +746,7 @@ }, "sprites/blueprints/painter-double.png": { - "frame": {"x":3,"y":400,"w":96,"h":96}, + "frame": {"x":191,"y":3,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -218,7 +754,7 @@ }, "sprites/blueprints/painter-mirrored.png": { - "frame": {"x":3,"y":600,"w":96,"h":48}, + "frame": {"x":589,"y":195,"w":96,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":48}, @@ -226,7 +762,7 @@ }, "sprites/blueprints/painter-quad.png": { - "frame": {"x":3,"y":3,"w":188,"h":48}, + "frame": {"x":3,"y":192,"w":188,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":188,"h":48}, @@ -234,7 +770,7 @@ }, "sprites/blueprints/painter.png": { - "frame": {"x":3,"y":652,"w":96,"h":48}, + "frame": {"x":689,"y":195,"w":96,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":48}, @@ -242,7 +778,7 @@ }, "sprites/blueprints/rotater-ccw.png": { - "frame": {"x":191,"y":107,"w":48,"h":48}, + "frame": {"x":935,"y":91,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -250,7 +786,7 @@ }, "sprites/blueprints/rotater.png": { - "frame": {"x":191,"y":159,"w":48,"h":48}, + "frame": {"x":880,"y":195,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -258,7 +794,7 @@ }, "sprites/blueprints/splitter-compact-inverse.png": { - "frame": {"x":191,"y":211,"w":48,"h":48}, + "frame": {"x":932,"y":195,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -266,7 +802,7 @@ }, "sprites/blueprints/splitter-compact.png": { - "frame": {"x":103,"y":780,"w":47,"h":47}, + "frame": {"x":538,"y":201,"w":47,"h":47}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":47}, @@ -274,7 +810,7 @@ }, "sprites/blueprints/splitter.png": { - "frame": {"x":155,"y":471,"w":87,"h":48}, + "frame": {"x":955,"y":3,"w":87,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":87,"h":48}, @@ -282,7 +818,7 @@ }, "sprites/blueprints/stacker.png": { - "frame": {"x":96,"y":831,"w":89,"h":48}, + "frame": {"x":770,"y":143,"w":89,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":89,"h":48}, @@ -290,7 +826,7 @@ }, "sprites/blueprints/trash-storage.png": { - "frame": {"x":155,"y":627,"w":85,"h":96}, + "frame": {"x":383,"y":103,"w":85,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":85,"h":96}, @@ -298,7 +834,7 @@ }, "sprites/blueprints/trash.png": { - "frame": {"x":191,"y":263,"w":48,"h":48}, + "frame": {"x":954,"y":143,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -306,7 +842,7 @@ }, "sprites/blueprints/underground_belt_entry-tier2.png": { - "frame": {"x":103,"y":503,"w":48,"h":43}, + "frame": {"x":383,"y":203,"w":48,"h":43}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":5,"w":48,"h":43}, @@ -314,7 +850,7 @@ }, "sprites/blueprints/underground_belt_entry.png": { - "frame": {"x":103,"y":550,"w":48,"h":38}, + "frame": {"x":563,"y":153,"w":48,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":10,"w":48,"h":38}, @@ -322,7 +858,7 @@ }, "sprites/blueprints/underground_belt_exit-tier2.png": { - "frame": {"x":103,"y":592,"w":48,"h":38}, + "frame": {"x":615,"y":153,"w":48,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":38}, @@ -330,7 +866,7 @@ }, "sprites/blueprints/underground_belt_exit.png": { - "frame": {"x":103,"y":634,"w":48,"h":38}, + "frame": {"x":667,"y":153,"w":48,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":38}, @@ -338,7 +874,7 @@ }, "sprites/buildings/belt_left.png": { - "frame": {"x":143,"y":883,"w":30,"h":30}, + "frame": {"x":1429,"y":173,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":30,"h":30}, @@ -346,7 +882,7 @@ }, "sprites/buildings/belt_right.png": { - "frame": {"x":103,"y":967,"w":30,"h":30}, + "frame": {"x":1436,"y":207,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":30,"h":30}, @@ -354,7 +890,7 @@ }, "sprites/buildings/belt_top.png": { - "frame": {"x":3,"y":1010,"w":28,"h":32}, + "frame": {"x":191,"y":155,"w":28,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":28,"h":32}, @@ -362,7 +898,7 @@ }, "sprites/buildings/cutter-quad.png": { - "frame": {"x":3,"y":348,"w":184,"h":48}, + "frame": {"x":563,"y":101,"w":184,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":184,"h":48}, @@ -370,15 +906,23 @@ }, "sprites/buildings/cutter.png": { - "frame": {"x":155,"y":523,"w":87,"h":48}, + "frame": {"x":789,"y":195,"w":87,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":87,"h":48}, "sourceSize": {"w":96,"h":48} }, +"sprites/buildings/energy_generator.png": +{ + "frame": {"x":480,"y":3,"w":87,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":2,"w":87,"h":94}, + "sourceSize": {"w":96,"h":96} +}, "sprites/buildings/hub.png": { - "frame": {"x":3,"y":107,"w":184,"h":185}, + "frame": {"x":3,"y":3,"w":184,"h":185}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":184,"h":185}, @@ -386,7 +930,7 @@ }, "sprites/buildings/miner-chainable.png": { - "frame": {"x":103,"y":676,"w":47,"h":48}, + "frame": {"x":1098,"y":3,"w":47,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":48}, @@ -394,7 +938,7 @@ }, "sprites/buildings/miner.png": { - "frame": {"x":103,"y":728,"w":47,"h":48}, + "frame": {"x":1036,"y":195,"w":47,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":48}, @@ -402,7 +946,7 @@ }, "sprites/buildings/mixer.png": { - "frame": {"x":154,"y":727,"w":88,"h":48}, + "frame": {"x":771,"y":3,"w":88,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":88,"h":48}, @@ -410,7 +954,7 @@ }, "sprites/buildings/painter-double.png": { - "frame": {"x":3,"y":500,"w":96,"h":96}, + "frame": {"x":291,"y":3,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -418,7 +962,7 @@ }, "sprites/buildings/painter-mirrored.png": { - "frame": {"x":3,"y":704,"w":96,"h":48}, + "frame": {"x":571,"y":3,"w":96,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":48}, @@ -426,7 +970,7 @@ }, "sprites/buildings/painter-quad.png": { - "frame": {"x":3,"y":55,"w":188,"h":48}, + "frame": {"x":191,"y":103,"w":188,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":188,"h":48}, @@ -434,7 +978,7 @@ }, "sprites/buildings/painter.png": { - "frame": {"x":3,"y":756,"w":96,"h":48}, + "frame": {"x":671,"y":3,"w":96,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":48}, @@ -442,7 +986,7 @@ }, "sprites/buildings/rotater-ccw.png": { - "frame": {"x":191,"y":315,"w":48,"h":48}, + "frame": {"x":987,"y":91,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -450,7 +994,7 @@ }, "sprites/buildings/rotater.png": { - "frame": {"x":191,"y":367,"w":48,"h":48}, + "frame": {"x":1046,"y":3,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -458,7 +1002,7 @@ }, "sprites/buildings/splitter-compact-inverse.png": { - "frame": {"x":103,"y":452,"w":48,"h":47}, + "frame": {"x":486,"y":201,"w":48,"h":47}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":47}, @@ -466,7 +1010,7 @@ }, "sprites/buildings/splitter-compact.png": { - "frame": {"x":189,"y":831,"w":47,"h":47}, + "frame": {"x":1057,"y":143,"w":47,"h":47}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":47}, @@ -474,7 +1018,7 @@ }, "sprites/buildings/splitter.png": { - "frame": {"x":155,"y":575,"w":87,"h":48}, + "frame": {"x":863,"y":143,"w":87,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":87,"h":48}, @@ -482,7 +1026,7 @@ }, "sprites/buildings/stacker.png": { - "frame": {"x":154,"y":779,"w":88,"h":48}, + "frame": {"x":863,"y":3,"w":88,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":88,"h":48}, @@ -490,7 +1034,7 @@ }, "sprites/buildings/trash-storage.png": { - "frame": {"x":3,"y":860,"w":85,"h":96}, + "frame": {"x":391,"y":3,"w":85,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":85,"h":96}, @@ -498,7 +1042,7 @@ }, "sprites/buildings/trash.png": { - "frame": {"x":103,"y":400,"w":48,"h":48}, + "frame": {"x":984,"y":195,"w":48,"h":48}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":48,"h":48}, @@ -506,7 +1050,7 @@ }, "sprites/buildings/underground_belt_entry-tier2.png": { - "frame": {"x":189,"y":882,"w":47,"h":42}, + "frame": {"x":435,"y":203,"w":47,"h":42}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":6,"w":47,"h":42}, @@ -514,7 +1058,7 @@ }, "sprites/buildings/underground_belt_entry.png": { - "frame": {"x":92,"y":883,"w":47,"h":38}, + "frame": {"x":719,"y":153,"w":47,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":10,"w":47,"h":38}, @@ -522,7 +1066,7 @@ }, "sprites/buildings/underground_belt_exit-tier2.png": { - "frame": {"x":92,"y":925,"w":47,"h":38}, + "frame": {"x":571,"y":55,"w":47,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":38}, @@ -530,7 +1074,7 @@ }, "sprites/buildings/underground_belt_exit.png": { - "frame": {"x":189,"y":928,"w":47,"h":38}, + "frame": {"x":622,"y":55,"w":47,"h":38}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":47,"h":38}, @@ -538,7 +1082,7 @@ }, "sprites/debug/acceptor_slot.png": { - "frame": {"x":226,"y":970,"w":14,"h":16}, + "frame": {"x":1087,"y":230,"w":14,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":14,"h":16}, @@ -546,7 +1090,7 @@ }, "sprites/debug/ejector_slot.png": { - "frame": {"x":226,"y":990,"w":14,"h":16}, + "frame": {"x":1105,"y":230,"w":14,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":14,"h":16}, @@ -554,7 +1098,7 @@ }, "sprites/map_overview/belt_forward.png": { - "frame": {"x":171,"y":400,"w":8,"h":8}, + "frame": {"x":1106,"y":127,"w":8,"h":8}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":8,"h":8}, @@ -562,7 +1106,7 @@ }, "sprites/map_overview/belt_left.png": { - "frame": {"x":177,"y":883,"w":8,"h":8}, + "frame": {"x":1149,"y":39,"w":8,"h":8}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":8,"h":8}, @@ -570,7 +1114,7 @@ }, "sprites/map_overview/belt_right.png": { - "frame": {"x":177,"y":895,"w":8,"h":8}, + "frame": {"x":1118,"y":127,"w":8,"h":8}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":8,"h":8}, @@ -578,7 +1122,7 @@ }, "sprites/misc/deletion_marker.png": { - "frame": {"x":165,"y":1065,"w":22,"h":22}, + "frame": {"x":1457,"y":37,"w":22,"h":22}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":22,"h":22}, @@ -586,7 +1130,7 @@ }, "sprites/misc/hub_direction_indicator.png": { - "frame": {"x":177,"y":907,"w":8,"h":8}, + "frame": {"x":1130,"y":127,"w":8,"h":8}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":8,"h":8}, @@ -594,7 +1138,7 @@ }, "sprites/misc/lock_direction_indicator.png": { - "frame": {"x":155,"y":400,"w":12,"h":10}, + "frame": {"x":1090,"y":127,"w":12,"h":10}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":12,"h":10}, @@ -602,7 +1146,7 @@ }, "sprites/misc/slot_bad_arrow.png": { - "frame": {"x":165,"y":1065,"w":22,"h":22}, + "frame": {"x":1457,"y":37,"w":22,"h":22}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":22,"h":22}, @@ -610,7 +1154,7 @@ }, "sprites/misc/slot_good_arrow.png": { - "frame": {"x":69,"y":1001,"w":22,"h":24}, + "frame": {"x":1191,"y":223,"w":22,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":22,"h":24}, @@ -618,7 +1162,7 @@ }, "sprites/misc/storage_overlay.png": { - "frame": {"x":177,"y":970,"w":45,"h":23}, + "frame": {"x":1217,"y":223,"w":45,"h":23}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":45,"h":23}, @@ -626,7 +1170,7 @@ }, "sprites/misc/waypoint.png": { - "frame": {"x":177,"y":919,"w":8,"h":8}, + "frame": {"x":1161,"y":39,"w":8,"h":8}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":8,"h":8}, @@ -637,8 +1181,8 @@ "version": "1.0", "image": "atlas0_25.png", "format": "RGBA8888", - "size": {"w":245,"h":1090}, + "size": {"w":1484,"h":251}, "scale": "0.25", - "smartupdate": "$TexturePacker:SmartUpdate:8985524bba1a3f16cecab3c03b6aaa06:d614d20bfce033d8a8ab0990af085d16:f159918d23e5952766c6d23ab52278c6$" + "smartupdate": "$TexturePacker:SmartUpdate:dc34796c028235bbc69e5b8d854254ca:2765d0b8c8bcbb7a4aaaf1104853ad41:8778749683c68f53155587e6d831729a$" } } diff --git a/res_built/atlas/atlas0_25.png b/res_built/atlas/atlas0_25.png index 4581d71d..0d49e2e8 100644 Binary files a/res_built/atlas/atlas0_25.png and b/res_built/atlas/atlas0_25.png differ diff --git a/res_built/atlas/atlas0_50.json b/res_built/atlas/atlas0_50.json index f31bbb3d..f9651cb6 100644 --- a/res_built/atlas/atlas0_50.json +++ b/res_built/atlas/atlas0_50.json @@ -2,7 +2,7 @@ "sprites/belt/forward_0.png": { - "frame": {"x":1966,"y":3,"w":51,"h":63}, + "frame": {"x":1234,"y":403,"w":51,"h":63}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, @@ -10,7 +10,7 @@ }, "sprites/belt/forward_1.png": { - "frame": {"x":1835,"y":203,"w":51,"h":63}, + "frame": {"x":905,"y":503,"w":51,"h":63}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, @@ -18,7 +18,7 @@ }, "sprites/belt/forward_2.png": { - "frame": {"x":1879,"y":304,"w":51,"h":63}, + "frame": {"x":588,"y":678,"w":51,"h":63}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, @@ -26,7 +26,7 @@ }, "sprites/belt/forward_3.png": { - "frame": {"x":1934,"y":304,"w":51,"h":63}, + "frame": {"x":753,"y":666,"w":51,"h":63}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, @@ -34,7 +34,7 @@ }, "sprites/belt/forward_4.png": { - "frame": {"x":1844,"y":381,"w":51,"h":63}, + "frame": {"x":753,"y":733,"w":51,"h":63}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, @@ -42,7 +42,183 @@ }, "sprites/belt/forward_5.png": { - "frame": {"x":1899,"y":371,"w":51,"h":63}, + "frame": {"x":808,"y":676,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_6.png": +{ + "frame": {"x":808,"y":743,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_7.png": +{ + "frame": {"x":863,"y":692,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_8.png": +{ + "frame": {"x":863,"y":759,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_9.png": +{ + "frame": {"x":918,"y":698,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_10.png": +{ + "frame": {"x":960,"y":503,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_11.png": +{ + "frame": {"x":966,"y":570,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_12.png": +{ + "frame": {"x":1021,"y":564,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_13.png": +{ + "frame": {"x":1076,"y":564,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_14.png": +{ + "frame": {"x":1131,"y":564,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_15.png": +{ + "frame": {"x":1198,"y":503,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_16.png": +{ + "frame": {"x":1247,"y":570,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_17.png": +{ + "frame": {"x":472,"y":657,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_18.png": +{ + "frame": {"x":463,"y":785,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_19.png": +{ + "frame": {"x":466,"y":852,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_20.png": +{ + "frame": {"x":643,"y":677,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_21.png": +{ + "frame": {"x":588,"y":745,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_22.png": +{ + "frame": {"x":643,"y":744,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_23.png": +{ + "frame": {"x":521,"y":852,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_24.png": +{ + "frame": {"x":698,"y":677,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_25.png": +{ + "frame": {"x":698,"y":744,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_26.png": +{ + "frame": {"x":722,"y":599,"w":51,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/forward_27.png": +{ + "frame": {"x":777,"y":597,"w":51,"h":63}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, @@ -50,7 +226,7 @@ }, "sprites/belt/left_0.png": { - "frame": {"x":1890,"y":243,"w":57,"h":57}, + "frame": {"x":1015,"y":503,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, @@ -58,7 +234,7 @@ }, "sprites/belt/left_1.png": { - "frame": {"x":1951,"y":243,"w":57,"h":57}, + "frame": {"x":1076,"y":503,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, @@ -66,7 +242,7 @@ }, "sprites/belt/left_2.png": { - "frame": {"x":1783,"y":381,"w":57,"h":57}, + "frame": {"x":973,"y":698,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, @@ -74,7 +250,7 @@ }, "sprites/belt/left_3.png": { - "frame": {"x":1954,"y":371,"w":57,"h":57}, + "frame": {"x":1156,"y":692,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, @@ -82,7 +258,7 @@ }, "sprites/belt/left_4.png": { - "frame": {"x":1954,"y":432,"w":57,"h":57}, + "frame": {"x":1162,"y":753,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, @@ -90,7 +266,183 @@ }, "sprites/belt/left_5.png": { - "frame": {"x":1363,"y":403,"w":57,"h":57}, + "frame": {"x":576,"y":812,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_6.png": +{ + "frame": {"x":576,"y":873,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_7.png": +{ + "frame": {"x":561,"y":934,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_8.png": +{ + "frame": {"x":622,"y":934,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_9.png": +{ + "frame": {"x":979,"y":820,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_10.png": +{ + "frame": {"x":1137,"y":503,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_11.png": +{ + "frame": {"x":905,"y":570,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_12.png": +{ + "frame": {"x":1186,"y":570,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_13.png": +{ + "frame": {"x":527,"y":678,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_14.png": +{ + "frame": {"x":466,"y":724,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_15.png": +{ + "frame": {"x":527,"y":739,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_16.png": +{ + "frame": {"x":832,"y":615,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_17.png": +{ + "frame": {"x":893,"y":631,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_18.png": +{ + "frame": {"x":954,"y":637,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_19.png": +{ + "frame": {"x":918,"y":765,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_20.png": +{ + "frame": {"x":979,"y":759,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_21.png": +{ + "frame": {"x":1015,"y":637,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_22.png": +{ + "frame": {"x":1076,"y":631,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_23.png": +{ + "frame": {"x":1137,"y":631,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_24.png": +{ + "frame": {"x":1034,"y":698,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_25.png": +{ + "frame": {"x":1040,"y":759,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_26.png": +{ + "frame": {"x":1095,"y":692,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/left_27.png": +{ + "frame": {"x":1101,"y":753,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, @@ -98,7 +450,7 @@ }, "sprites/belt/right_0.png": { - "frame": {"x":1424,"y":403,"w":57,"h":57}, + "frame": {"x":1040,"y":820,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, @@ -106,7 +458,7 @@ }, "sprites/belt/right_1.png": { - "frame": {"x":1485,"y":403,"w":57,"h":57}, + "frame": {"x":1101,"y":814,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, @@ -114,7 +466,7 @@ }, "sprites/belt/right_2.png": { - "frame": {"x":1546,"y":403,"w":57,"h":57}, + "frame": {"x":881,"y":826,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, @@ -122,7 +474,7 @@ }, "sprites/belt/right_3.png": { - "frame": {"x":1607,"y":402,"w":57,"h":57}, + "frame": {"x":1186,"y":875,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, @@ -130,7 +482,7 @@ }, "sprites/belt/right_4.png": { - "frame": {"x":1668,"y":402,"w":57,"h":57}, + "frame": {"x":927,"y":948,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, @@ -138,7 +490,183 @@ }, "sprites/belt/right_5.png": { - "frame": {"x":1952,"y":181,"w":57,"h":57}, + "frame": {"x":988,"y":942,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_6.png": +{ + "frame": {"x":1049,"y":942,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_7.png": +{ + "frame": {"x":1110,"y":942,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_8.png": +{ + "frame": {"x":1171,"y":936,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_9.png": +{ + "frame": {"x":1232,"y":936,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_10.png": +{ + "frame": {"x":1162,"y":814,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_11.png": +{ + "frame": {"x":637,"y":812,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_12.png": +{ + "frame": {"x":637,"y":873,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_13.png": +{ + "frame": {"x":698,"y":811,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_14.png": +{ + "frame": {"x":698,"y":872,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_15.png": +{ + "frame": {"x":683,"y":934,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_16.png": +{ + "frame": {"x":744,"y":933,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_17.png": +{ + "frame": {"x":759,"y":810,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_18.png": +{ + "frame": {"x":759,"y":871,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_19.png": +{ + "frame": {"x":820,"y":826,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_20.png": +{ + "frame": {"x":820,"y":887,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_21.png": +{ + "frame": {"x":881,"y":887,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_22.png": +{ + "frame": {"x":805,"y":948,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_23.png": +{ + "frame": {"x":866,"y":948,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_24.png": +{ + "frame": {"x":942,"y":881,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_25.png": +{ + "frame": {"x":1003,"y":881,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_26.png": +{ + "frame": {"x":1064,"y":881,"w":57,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, + "sourceSize": {"w":63,"h":63} +}, +"sprites/belt/right_27.png": +{ + "frame": {"x":1125,"y":875,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, @@ -146,7 +674,7 @@ }, "sprites/blueprints/belt_left.png": { - "frame": {"x":1301,"y":400,"w":58,"h":58}, + "frame": {"x":843,"y":491,"w":58,"h":58}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":5,"w":58,"h":58}, @@ -154,7 +682,7 @@ }, "sprites/blueprints/belt_right.png": { - "frame": {"x":1890,"y":181,"w":58,"h":58}, + "frame": {"x":843,"y":553,"w":58,"h":58}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":5,"w":58,"h":58}, @@ -162,7 +690,7 @@ }, "sprites/blueprints/belt_top.png": { - "frame": {"x":1151,"y":403,"w":53,"h":63}, + "frame": {"x":472,"y":590,"w":53,"h":63}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":53,"h":63}, @@ -178,15 +706,23 @@ }, "sprites/blueprints/cutter.png": { - "frame": {"x":1285,"y":203,"w":172,"h":96}, + "frame": {"x":1126,"y":103,"w":172,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":0,"w":172,"h":96}, "sourceSize": {"w":192,"h":96} }, +"sprites/blueprints/energy_generator.png": +{ + "frame": {"x":199,"y":474,"w":170,"h":187}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":12,"y":5,"w":170,"h":187}, + "sourceSize": {"w":192,"h":192} +}, "sprites/blueprints/miner-chainable.png": { - "frame": {"x":1401,"y":303,"w":92,"h":96}, + "frame": {"x":173,"y":669,"w":92,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":92,"h":96}, @@ -194,7 +730,7 @@ }, "sprites/blueprints/miner.png": { - "frame": {"x":1497,"y":303,"w":92,"h":96}, + "frame": {"x":178,"y":864,"w":92,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":92,"h":96}, @@ -202,7 +738,7 @@ }, "sprites/blueprints/mixer.png": { - "frame": {"x":1143,"y":103,"w":175,"h":96}, + "frame": {"x":1121,"y":3,"w":175,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":0,"w":175,"h":96}, @@ -234,7 +770,7 @@ }, "sprites/blueprints/painter.png": { - "frame": {"x":1121,"y":3,"w":192,"h":96}, + "frame": {"x":740,"y":203,"w":192,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":192,"h":96}, @@ -242,7 +778,7 @@ }, "sprites/blueprints/rotater-ccw.png": { - "frame": {"x":1866,"y":3,"w":96,"h":96}, + "frame": {"x":1188,"y":303,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -250,7 +786,7 @@ }, "sprites/blueprints/rotater.png": { - "frame": {"x":1675,"y":103,"w":96,"h":96}, + "frame": {"x":743,"y":497,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -258,7 +794,7 @@ }, "sprites/blueprints/splitter-compact-inverse.png": { - "frame": {"x":1205,"y":303,"w":95,"h":93}, + "frame": {"x":269,"y":665,"w":95,"h":93}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":95,"h":93}, @@ -266,7 +802,7 @@ }, "sprites/blueprints/splitter-compact.png": { - "frame": {"x":1304,"y":303,"w":93,"h":93}, + "frame": {"x":270,"y":762,"w":93,"h":93}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":1,"w":93,"h":93}, @@ -274,7 +810,7 @@ }, "sprites/blueprints/splitter.png": { - "frame": {"x":1691,"y":3,"w":171,"h":96}, + "frame": {"x":3,"y":865,"w":171,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":0,"w":171,"h":96}, @@ -282,7 +818,7 @@ }, "sprites/blueprints/stacker.png": { - "frame": {"x":1106,"y":203,"w":175,"h":96}, + "frame": {"x":947,"y":103,"w":175,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":0,"w":175,"h":96}, @@ -290,7 +826,7 @@ }, "sprites/blueprints/trash-storage.png": { - "frame": {"x":765,"y":203,"w":167,"h":192}, + "frame": {"x":569,"y":203,"w":167,"h":192}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":167,"h":192}, @@ -298,7 +834,7 @@ }, "sprites/blueprints/trash.png": { - "frame": {"x":1636,"y":203,"w":96,"h":96}, + "frame": {"x":935,"y":403,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -306,7 +842,7 @@ }, "sprites/blueprints/underground_belt_entry-tier2.png": { - "frame": {"x":764,"y":399,"w":93,"h":84}, + "frame": {"x":838,"y":403,"w":93,"h":84}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":12,"w":93,"h":84}, @@ -314,7 +850,7 @@ }, "sprites/blueprints/underground_belt_entry.png": { - "frame": {"x":373,"y":399,"w":93,"h":75}, + "frame": {"x":369,"y":856,"w":93,"h":75}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":21,"w":93,"h":75}, @@ -322,7 +858,7 @@ }, "sprites/blueprints/underground_belt_exit-tier2.png": { - "frame": {"x":957,"y":399,"w":94,"h":75}, + "frame": {"x":368,"y":690,"w":94,"h":75}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":94,"h":75}, @@ -330,7 +866,7 @@ }, "sprites/blueprints/underground_belt_exit.png": { - "frame": {"x":470,"y":399,"w":93,"h":75}, + "frame": {"x":529,"y":599,"w":93,"h":75}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":93,"h":75}, @@ -338,7 +874,7 @@ }, "sprites/buildings/belt_left.png": { - "frame": {"x":1890,"y":243,"w":57,"h":57}, + "frame": {"x":1228,"y":637,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":6,"w":57,"h":57}, @@ -346,7 +882,7 @@ }, "sprites/buildings/belt_right.png": { - "frame": {"x":1424,"y":403,"w":57,"h":57}, + "frame": {"x":1223,"y":698,"w":57,"h":57}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":6,"w":57,"h":57}, @@ -354,7 +890,7 @@ }, "sprites/buildings/belt_top.png": { - "frame": {"x":1966,"y":3,"w":51,"h":63}, + "frame": {"x":1234,"y":403,"w":51,"h":63}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":6,"y":0,"w":51,"h":63}, @@ -370,12 +906,20 @@ }, "sprites/buildings/cutter.png": { - "frame": {"x":1500,"y":103,"w":171,"h":96}, + "frame": {"x":838,"y":303,"w":171,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":0,"w":171,"h":96}, "sourceSize": {"w":192,"h":96} }, +"sprites/buildings/energy_generator.png": +{ + "frame": {"x":373,"y":399,"w":170,"h":187}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":12,"y":5,"w":170,"h":187}, + "sourceSize": {"w":192,"h":192} +}, "sprites/buildings/hub.png": { "frame": {"x":3,"y":3,"w":366,"h":367}, @@ -386,7 +930,7 @@ }, "sprites/buildings/miner-chainable.png": { - "frame": {"x":1593,"y":303,"w":91,"h":95}, + "frame": {"x":743,"y":398,"w":91,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":91,"h":95}, @@ -394,7 +938,7 @@ }, "sprites/buildings/miner.png": { - "frame": {"x":1688,"y":303,"w":91,"h":95}, + "frame": {"x":274,"y":859,"w":91,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":91,"h":95}, @@ -402,7 +946,7 @@ }, "sprites/buildings/mixer.png": { - "frame": {"x":1513,"y":3,"w":174,"h":96}, + "frame": {"x":936,"y":203,"w":174,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":174,"h":96}, @@ -410,7 +954,7 @@ }, "sprites/buildings/painter-double.png": { - "frame": {"x":569,"y":203,"w":192,"h":191}, + "frame": {"x":3,"y":474,"w":192,"h":191}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":192,"h":191}, @@ -418,7 +962,7 @@ }, "sprites/buildings/painter-mirrored.png": { - "frame": {"x":947,"y":103,"w":192,"h":96}, + "frame": {"x":547,"y":399,"w":192,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":192,"h":96}, @@ -434,7 +978,7 @@ }, "sprites/buildings/painter.png": { - "frame": {"x":1317,"y":3,"w":192,"h":96}, + "frame": {"x":547,"y":499,"w":192,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":192,"h":96}, @@ -442,7 +986,7 @@ }, "sprites/buildings/rotater-ccw.png": { - "frame": {"x":1736,"y":203,"w":95,"h":96}, + "frame": {"x":1135,"y":403,"w":95,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":95,"h":96}, @@ -450,7 +994,7 @@ }, "sprites/buildings/rotater.png": { - "frame": {"x":1106,"y":303,"w":95,"h":96}, + "frame": {"x":373,"y":590,"w":95,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":95,"h":96}, @@ -458,7 +1002,7 @@ }, "sprites/buildings/splitter-compact-inverse.png": { - "frame": {"x":569,"y":398,"w":94,"h":91}, + "frame": {"x":740,"y":303,"w":94,"h":91}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":94,"h":91}, @@ -466,7 +1010,7 @@ }, "sprites/buildings/splitter-compact.png": { - "frame": {"x":667,"y":398,"w":93,"h":91}, + "frame": {"x":173,"y":769,"w":93,"h":91}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":2,"w":93,"h":91}, @@ -474,7 +1018,7 @@ }, "sprites/buildings/splitter.png": { - "frame": {"x":1461,"y":203,"w":171,"h":96}, + "frame": {"x":1013,"y":303,"w":171,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":0,"w":171,"h":96}, @@ -482,7 +1026,7 @@ }, "sprites/buildings/stacker.png": { - "frame": {"x":1322,"y":103,"w":174,"h":96}, + "frame": {"x":1114,"y":203,"w":174,"h":96}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":174,"h":96}, @@ -490,7 +1034,7 @@ }, "sprites/buildings/trash-storage.png": { - "frame": {"x":936,"y":203,"w":166,"h":192}, + "frame": {"x":3,"y":669,"w":166,"h":192}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":166,"h":192}, @@ -498,7 +1042,7 @@ }, "sprites/buildings/trash.png": { - "frame": {"x":1775,"y":103,"w":96,"h":96}, + "frame": {"x":1035,"y":403,"w":96,"h":96}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":96,"h":96}, @@ -506,7 +1050,7 @@ }, "sprites/buildings/underground_belt_entry-tier2.png": { - "frame": {"x":861,"y":399,"w":92,"h":83}, + "frame": {"x":367,"y":769,"w":92,"h":83}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":13,"w":92,"h":83}, @@ -514,7 +1058,7 @@ }, "sprites/buildings/underground_belt_entry.png": { - "frame": {"x":1875,"y":103,"w":92,"h":74}, + "frame": {"x":369,"y":935,"w":92,"h":74}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":22,"w":92,"h":74}, @@ -522,7 +1066,7 @@ }, "sprites/buildings/underground_belt_exit-tier2.png": { - "frame": {"x":1783,"y":303,"w":92,"h":74}, + "frame": {"x":465,"y":935,"w":92,"h":74}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":92,"h":74}, @@ -530,7 +1074,7 @@ }, "sprites/buildings/underground_belt_exit.png": { - "frame": {"x":1055,"y":403,"w":92,"h":74}, + "frame": {"x":626,"y":599,"w":92,"h":74}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":92,"h":74}, @@ -538,7 +1082,7 @@ }, "sprites/debug/acceptor_slot.png": { - "frame": {"x":1989,"y":304,"w":26,"h":32}, + "frame": {"x":142,"y":965,"w":26,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":26,"h":32}, @@ -546,7 +1090,7 @@ }, "sprites/debug/ejector_slot.png": { - "frame": {"x":1971,"y":116,"w":26,"h":32}, + "frame": {"x":1198,"y":631,"w":26,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":26,"h":32}, @@ -554,7 +1098,7 @@ }, "sprites/map_overview/belt_forward.png": { - "frame": {"x":1835,"y":270,"w":14,"h":16}, + "frame": {"x":1284,"y":698,"w":14,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":14,"h":16}, @@ -562,7 +1106,7 @@ }, "sprites/map_overview/belt_left.png": { - "frame": {"x":3,"y":474,"w":15,"h":15}, + "frame": {"x":744,"y":994,"w":15,"h":15}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":15,"h":15}, @@ -570,7 +1114,7 @@ }, "sprites/map_overview/belt_right.png": { - "frame": {"x":22,"y":474,"w":15,"h":15}, + "frame": {"x":763,"y":994,"w":15,"h":15}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":15,"h":15}, @@ -578,7 +1122,7 @@ }, "sprites/misc/deletion_marker.png": { - "frame": {"x":1971,"y":70,"w":42,"h":42}, + "frame": {"x":96,"y":965,"w":42,"h":42}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":3,"w":42,"h":42}, @@ -586,7 +1130,7 @@ }, "sprites/misc/hub_direction_indicator.png": { - "frame": {"x":2001,"y":116,"w":16,"h":16}, + "frame": {"x":942,"y":826,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -594,7 +1138,7 @@ }, "sprites/misc/lock_direction_indicator.png": { - "frame": {"x":1208,"y":448,"w":24,"h":16}, + "frame": {"x":1234,"y":470,"w":24,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":24,"h":16}, @@ -602,7 +1146,7 @@ }, "sprites/misc/slot_bad_arrow.png": { - "frame": {"x":1971,"y":70,"w":42,"h":42}, + "frame": {"x":96,"y":965,"w":42,"h":42}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":3,"w":42,"h":42}, @@ -610,7 +1154,7 @@ }, "sprites/misc/slot_good_arrow.png": { - "frame": {"x":1729,"y":402,"w":42,"h":48}, + "frame": {"x":518,"y":800,"w":42,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":0,"w":42,"h":48}, @@ -618,7 +1162,7 @@ }, "sprites/misc/storage_overlay.png": { - "frame": {"x":1208,"y":400,"w":89,"h":44}, + "frame": {"x":3,"y":965,"w":89,"h":44}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":89,"h":44}, @@ -626,7 +1170,7 @@ }, "sprites/misc/waypoint.png": { - "frame": {"x":1989,"y":340,"w":14,"h":16}, + "frame": {"x":1284,"y":718,"w":14,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":14,"h":16}, @@ -637,8 +1181,8 @@ "version": "1.0", "image": "atlas0_50.png", "format": "RGBA8888", - "size": {"w":2020,"h":492}, + "size": {"w":1301,"h":1012}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:8985524bba1a3f16cecab3c03b6aaa06:d614d20bfce033d8a8ab0990af085d16:f159918d23e5952766c6d23ab52278c6$" + "smartupdate": "$TexturePacker:SmartUpdate:dc34796c028235bbc69e5b8d854254ca:2765d0b8c8bcbb7a4aaaf1104853ad41:8778749683c68f53155587e6d831729a$" } } diff --git a/res_built/atlas/atlas0_50.png b/res_built/atlas/atlas0_50.png index 60e1f644..29116b1b 100644 Binary files a/res_built/atlas/atlas0_50.png and b/res_built/atlas/atlas0_50.png differ diff --git a/res_built/atlas/atlas0_75.json b/res_built/atlas/atlas0_75.json index 392448be..147b55c9 100644 --- a/res_built/atlas/atlas0_75.json +++ b/res_built/atlas/atlas0_75.json @@ -2,7 +2,7 @@ "sprites/belt/forward_0.png": { - "frame": {"x":1963,"y":102,"w":77,"h":95}, + "frame": {"x":86,"y":1431,"w":77,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, @@ -10,7 +10,7 @@ }, "sprites/belt/forward_1.png": { - "frame": {"x":1963,"y":201,"w":77,"h":95}, + "frame": {"x":167,"y":1431,"w":77,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, @@ -18,7 +18,7 @@ }, "sprites/belt/forward_2.png": { - "frame": {"x":1967,"y":300,"w":77,"h":95}, + "frame": {"x":1519,"y":1282,"w":77,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, @@ -26,7 +26,7 @@ }, "sprites/belt/forward_3.png": { - "frame": {"x":1967,"y":399,"w":77,"h":95}, + "frame": {"x":960,"y":3,"w":77,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, @@ -34,7 +34,7 @@ }, "sprites/belt/forward_4.png": { - "frame": {"x":1967,"y":498,"w":77,"h":95}, + "frame": {"x":1041,"y":3,"w":77,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, @@ -42,7 +42,183 @@ }, "sprites/belt/forward_5.png": { - "frame": {"x":1679,"y":952,"w":77,"h":95}, + "frame": {"x":774,"y":548,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_6.png": +{ + "frame": {"x":815,"y":254,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_7.png": +{ + "frame": {"x":815,"y":353,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_8.png": +{ + "frame": {"x":855,"y":542,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_9.png": +{ + "frame": {"x":896,"y":254,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_10.png": +{ + "frame": {"x":248,"y":1431,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_11.png": +{ + "frame": {"x":329,"y":1428,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_12.png": +{ + "frame": {"x":410,"y":1428,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_13.png": +{ + "frame": {"x":491,"y":1428,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_14.png": +{ + "frame": {"x":572,"y":1428,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_15.png": +{ + "frame": {"x":653,"y":1426,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_16.png": +{ + "frame": {"x":734,"y":1426,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_17.png": +{ + "frame": {"x":815,"y":1426,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_18.png": +{ + "frame": {"x":1438,"y":1284,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_19.png": +{ + "frame": {"x":1811,"y":806,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_20.png": +{ + "frame": {"x":1600,"y":1282,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_21.png": +{ + "frame": {"x":1681,"y":1282,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_22.png": +{ + "frame": {"x":1755,"y":1406,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_23.png": +{ + "frame": {"x":555,"y":3,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_24.png": +{ + "frame": {"x":636,"y":3,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_25.png": +{ + "frame": {"x":717,"y":3,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_26.png": +{ + "frame": {"x":798,"y":3,"w":77,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/forward_27.png": +{ + "frame": {"x":879,"y":3,"w":77,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, @@ -50,7 +226,7 @@ }, "sprites/belt/left_0.png": { - "frame": {"x":1217,"y":969,"w":86,"h":86}, + "frame": {"x":1707,"y":1102,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, @@ -58,7 +234,7 @@ }, "sprites/belt/left_1.png": { - "frame": {"x":1307,"y":969,"w":86,"h":86}, + "frame": {"x":1797,"y":1136,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, @@ -66,7 +242,7 @@ }, "sprites/belt/left_2.png": { - "frame": {"x":1916,"y":597,"w":86,"h":86}, + "frame": {"x":995,"y":434,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, @@ -74,7 +250,7 @@ }, "sprites/belt/left_3.png": { - "frame": {"x":1916,"y":687,"w":86,"h":86}, + "frame": {"x":1212,"y":3,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, @@ -82,7 +258,7 @@ }, "sprites/belt/left_4.png": { - "frame": {"x":1916,"y":777,"w":86,"h":86}, + "frame": {"x":1166,"y":287,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, @@ -90,7 +266,183 @@ }, "sprites/belt/left_5.png": { - "frame": {"x":1914,"y":867,"w":86,"h":86}, + "frame": {"x":1247,"y":197,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_6.png": +{ + "frame": {"x":1287,"y":107,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_7.png": +{ + "frame": {"x":1302,"y":3,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_8.png": +{ + "frame": {"x":1175,"y":377,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_9.png": +{ + "frame": {"x":1256,"y":287,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_10.png": +{ + "frame": {"x":1707,"y":1192,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_11.png": +{ + "frame": {"x":1797,"y":1226,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_12.png": +{ + "frame": {"x":1762,"y":1316,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_13.png": +{ + "frame": {"x":815,"y":452,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_14.png": +{ + "frame": {"x":896,"y":353,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_15.png": +{ + "frame": {"x":977,"y":254,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_16.png": +{ + "frame": {"x":905,"y":443,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_17.png": +{ + "frame": {"x":986,"y":344,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_18.png": +{ + "frame": {"x":1067,"y":254,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_19.png": +{ + "frame": {"x":936,"y":533,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_20.png": +{ + "frame": {"x":1076,"y":344,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_21.png": +{ + "frame": {"x":1026,"y":524,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_22.png": +{ + "frame": {"x":1085,"y":434,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_23.png": +{ + "frame": {"x":1116,"y":524,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_24.png": +{ + "frame": {"x":1107,"y":107,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_25.png": +{ + "frame": {"x":1122,"y":3,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_26.png": +{ + "frame": {"x":1157,"y":197,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/left_27.png": +{ + "frame": {"x":1197,"y":107,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, @@ -98,7 +450,7 @@ }, "sprites/belt/right_0.png": { - "frame": {"x":1499,"y":852,"w":86,"h":86}, + "frame": {"x":1337,"y":197,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, @@ -106,7 +458,7 @@ }, "sprites/belt/right_1.png": { - "frame": {"x":1397,"y":967,"w":86,"h":86}, + "frame": {"x":1377,"y":107,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, @@ -114,7 +466,7 @@ }, "sprites/belt/right_2.png": { - "frame": {"x":1589,"y":862,"w":86,"h":86}, + "frame": {"x":1647,"y":107,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, @@ -122,7 +474,7 @@ }, "sprites/belt/right_3.png": { - "frame": {"x":1679,"y":862,"w":86,"h":86}, + "frame": {"x":1386,"y":543,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, @@ -130,7 +482,7 @@ }, "sprites/belt/right_4.png": { - "frame": {"x":1499,"y":942,"w":86,"h":86}, + "frame": {"x":1476,"y":453,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, @@ -138,7 +490,183 @@ }, "sprites/belt/right_5.png": { - "frame": {"x":1589,"y":952,"w":86,"h":86}, + "frame": {"x":1476,"y":543,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_6.png": +{ + "frame": {"x":1566,"y":377,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_7.png": +{ + "frame": {"x":1656,"y":377,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_8.png": +{ + "frame": {"x":1566,"y":467,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_9.png": +{ + "frame": {"x":1656,"y":467,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_10.png": +{ + "frame": {"x":1392,"y":3,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_11.png": +{ + "frame": {"x":1206,"y":467,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_12.png": +{ + "frame": {"x":1265,"y":377,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_13.png": +{ + "frame": {"x":1346,"y":287,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_14.png": +{ + "frame": {"x":1427,"y":197,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_15.png": +{ + "frame": {"x":1467,"y":107,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_16.png": +{ + "frame": {"x":1206,"y":557,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_17.png": +{ + "frame": {"x":1482,"y":3,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_18.png": +{ + "frame": {"x":1572,"y":3,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_19.png": +{ + "frame": {"x":1557,"y":93,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_20.png": +{ + "frame": {"x":1557,"y":183,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_21.png": +{ + "frame": {"x":1647,"y":197,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_22.png": +{ + "frame": {"x":1517,"y":273,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_23.png": +{ + "frame": {"x":1607,"y":287,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_24.png": +{ + "frame": {"x":1436,"y":363,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_25.png": +{ + "frame": {"x":1296,"y":467,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_26.png": +{ + "frame": {"x":1296,"y":557,"w":86,"h":86}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, + "sourceSize": {"w":95,"h":95} +}, +"sprites/belt/right_27.png": +{ + "frame": {"x":1386,"y":453,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, @@ -146,7 +674,7 @@ }, "sprites/blueprints/belt_left.png": { - "frame": {"x":1397,"y":593,"w":87,"h":87}, + "frame": {"x":1803,"y":954,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":8,"w":87,"h":87}, @@ -154,7 +682,7 @@ }, "sprites/blueprints/belt_right.png": { - "frame": {"x":1397,"y":684,"w":87,"h":87}, + "frame": {"x":1803,"y":1045,"w":87,"h":87}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":8,"w":87,"h":87}, @@ -162,7 +690,7 @@ }, "sprites/blueprints/belt_top.png": { - "frame": {"x":1963,"y":3,"w":79,"h":95}, + "frame": {"x":3,"y":1431,"w":79,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":8,"y":0,"w":79,"h":95}, @@ -170,7 +698,7 @@ }, "sprites/blueprints/cutter-quad.png": { - "frame": {"x":3,"y":556,"w":548,"h":144}, + "frame": {"x":547,"y":988,"w":548,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":548,"h":144}, @@ -178,15 +706,23 @@ }, "sprites/blueprints/cutter.png": { - "frame": {"x":1411,"y":150,"w":256,"h":144}, + "frame": {"x":1111,"y":807,"w":256,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":256,"h":144}, "sourceSize": {"w":288,"h":144} }, +"sprites/blueprints/energy_generator.png": +{ + "frame": {"x":257,"y":556,"w":255,"h":280}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":18,"y":8,"w":255,"h":280}, + "sourceSize": {"w":288,"h":288} +}, "sprites/blueprints/miner-chainable.png": { - "frame": {"x":1827,"y":447,"w":136,"h":143}, + "frame": {"x":1755,"y":3,"w":136,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":136,"h":143}, @@ -194,7 +730,7 @@ }, "sprites/blueprints/miner.png": { - "frame": {"x":1076,"y":739,"w":136,"h":143}, + "frame": {"x":1755,"y":150,"w":136,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":136,"h":143}, @@ -202,7 +738,7 @@ }, "sprites/blueprints/mixer.png": { - "frame": {"x":547,"y":738,"w":261,"h":144}, + "frame": {"x":774,"y":659,"w":261,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":261,"h":144}, @@ -210,7 +746,7 @@ }, "sprites/blueprints/painter-double.png": { - "frame": {"x":555,"y":299,"w":288,"h":287}, + "frame": {"x":3,"y":1140,"w":288,"h":287}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":288,"h":287}, @@ -218,7 +754,7 @@ }, "sprites/blueprints/painter-mirrored.png": { - "frame": {"x":1119,"y":150,"w":288,"h":144}, + "frame": {"x":859,"y":1136,"w":288,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":288,"h":144}, @@ -226,7 +762,7 @@ }, "sprites/blueprints/painter-quad.png": { - "frame": {"x":555,"y":3,"w":560,"h":144}, + "frame": {"x":295,"y":1138,"w":560,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":560,"h":144}, @@ -234,7 +770,7 @@ }, "sprites/blueprints/painter.png": { - "frame": {"x":1671,"y":3,"w":288,"h":144}, + "frame": {"x":1099,"y":988,"w":288,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":288,"h":144}, @@ -242,7 +778,7 @@ }, "sprites/blueprints/rotater-ccw.png": { - "frame": {"x":1639,"y":299,"w":143,"h":144}, + "frame": {"x":1519,"y":807,"w":143,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":143,"h":144}, @@ -250,7 +786,7 @@ }, "sprites/blueprints/rotater.png": { - "frame": {"x":1786,"y":299,"w":143,"h":144}, + "frame": {"x":1564,"y":659,"w":143,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":143,"h":144}, @@ -258,7 +794,7 @@ }, "sprites/blueprints/splitter-compact-inverse.png": { - "frame": {"x":1071,"y":886,"w":142,"h":138}, + "frame": {"x":295,"y":1286,"w":142,"h":138}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":142,"h":138}, @@ -266,7 +802,7 @@ }, "sprites/blueprints/splitter-compact.png": { - "frame": {"x":1488,"y":594,"w":139,"h":138}, + "frame": {"x":441,"y":1286,"w":139,"h":138}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":2,"w":139,"h":138}, @@ -274,7 +810,7 @@ }, "sprites/blueprints/splitter.png": { - "frame": {"x":811,"y":886,"w":256,"h":144}, + "frame": {"x":1304,"y":659,"w":256,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":256,"h":144}, @@ -282,7 +818,7 @@ }, "sprites/blueprints/stacker.png": { - "frame": {"x":847,"y":591,"w":261,"h":144}, + "frame": {"x":1039,"y":659,"w":261,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":13,"y":0,"w":261,"h":144}, @@ -290,7 +826,7 @@ }, "sprites/blueprints/trash-storage.png": { - "frame": {"x":847,"y":299,"w":250,"h":288}, + "frame": {"x":3,"y":556,"w":250,"h":288}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":21,"y":0,"w":250,"h":288}, @@ -298,7 +834,7 @@ }, "sprites/blueprints/trash.png": { - "frame": {"x":1241,"y":445,"w":144,"h":144}, + "frame": {"x":1371,"y":807,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -306,7 +842,7 @@ }, "sprites/blueprints/underground_belt_entry-tier2.png": { - "frame": {"x":1774,"y":594,"w":138,"h":125}, + "frame": {"x":872,"y":1284,"w":138,"h":125}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":19,"w":138,"h":125}, @@ -314,7 +850,7 @@ }, "sprites/blueprints/underground_belt_entry.png": { - "frame": {"x":1774,"y":723,"w":138,"h":112}, + "frame": {"x":1039,"y":1412,"w":138,"h":112}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":32,"w":138,"h":112}, @@ -322,7 +858,7 @@ }, "sprites/blueprints/underground_belt_exit-tier2.png": { - "frame": {"x":1488,"y":736,"w":139,"h":112}, + "frame": {"x":896,"y":1413,"w":139,"h":112}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":139,"h":112}, @@ -330,7 +866,7 @@ }, "sprites/blueprints/underground_belt_exit.png": { - "frame": {"x":1772,"y":839,"w":138,"h":112}, + "frame": {"x":1155,"y":1284,"w":138,"h":112}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":0,"w":138,"h":112}, @@ -338,7 +874,7 @@ }, "sprites/buildings/belt_left.png": { - "frame": {"x":1217,"y":969,"w":86,"h":86}, + "frame": {"x":1566,"y":557,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":9,"w":86,"h":86}, @@ -346,7 +882,7 @@ }, "sprites/buildings/belt_right.png": { - "frame": {"x":1499,"y":852,"w":86,"h":86}, + "frame": {"x":1656,"y":557,"w":86,"h":86}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":9,"w":86,"h":86}, @@ -354,7 +890,7 @@ }, "sprites/buildings/belt_top.png": { - "frame": {"x":1963,"y":102,"w":77,"h":95}, + "frame": {"x":86,"y":1431,"w":77,"h":95}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":9,"y":0,"w":77,"h":95}, @@ -362,7 +898,7 @@ }, "sprites/buildings/cutter-quad.png": { - "frame": {"x":1119,"y":3,"w":548,"h":143}, + "frame": {"x":555,"y":107,"w":548,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":548,"h":143}, @@ -370,12 +906,20 @@ }, "sprites/buildings/cutter.png": { - "frame": {"x":1119,"y":298,"w":256,"h":143}, + "frame": {"x":555,"y":254,"w":256,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":256,"h":143}, "sourceSize": {"w":288,"h":144} }, +"sprites/buildings/energy_generator.png": +{ + "frame": {"x":516,"y":556,"w":254,"h":280}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":19,"y":8,"w":254,"h":280}, + "sourceSize": {"w":288,"h":288} +}, "sprites/buildings/hub.png": { "frame": {"x":3,"y":3,"w":548,"h":549}, @@ -386,7 +930,7 @@ }, "sprites/buildings/miner-chainable.png": { - "frame": {"x":1101,"y":445,"w":136,"h":142}, + "frame": {"x":1755,"y":297,"w":136,"h":142}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":136,"h":142}, @@ -394,7 +938,7 @@ }, "sprites/buildings/miner.png": { - "frame": {"x":1112,"y":593,"w":136,"h":142}, + "frame": {"x":1755,"y":443,"w":136,"h":142}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":136,"h":142}, @@ -402,7 +946,7 @@ }, "sprites/buildings/mixer.png": { - "frame": {"x":812,"y":739,"w":260,"h":143}, + "frame": {"x":1443,"y":1103,"w":260,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":260,"h":143}, @@ -410,7 +954,7 @@ }, "sprites/buildings/painter-double.png": { - "frame": {"x":3,"y":704,"w":288,"h":286}, + "frame": {"x":255,"y":848,"w":288,"h":286}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":288,"h":286}, @@ -418,7 +962,7 @@ }, "sprites/buildings/painter-mirrored.png": { - "frame": {"x":1671,"y":151,"w":288,"h":144}, + "frame": {"x":1151,"y":1136,"w":288,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":288,"h":144}, @@ -426,7 +970,7 @@ }, "sprites/buildings/painter-quad.png": { - "frame": {"x":555,"y":151,"w":560,"h":144}, + "frame": {"x":547,"y":840,"w":560,"h":144}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":560,"h":144}, @@ -434,7 +978,7 @@ }, "sprites/buildings/painter.png": { - "frame": {"x":555,"y":590,"w":288,"h":144}, + "frame": {"x":1463,"y":1383,"w":288,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":288,"h":144}, @@ -442,7 +986,7 @@ }, "sprites/buildings/rotater-ccw.png": { - "frame": {"x":1537,"y":447,"w":141,"h":143}, + "frame": {"x":1666,"y":807,"w":141,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":141,"h":143}, @@ -450,7 +994,7 @@ }, "sprites/buildings/rotater.png": { - "frame": {"x":1682,"y":447,"w":141,"h":143}, + "frame": {"x":1711,"y":659,"w":141,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":141,"h":143}, @@ -458,7 +1002,7 @@ }, "sprites/buildings/splitter-compact-inverse.png": { - "frame": {"x":1252,"y":593,"w":141,"h":136}, + "frame": {"x":584,"y":1286,"w":141,"h":136}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":3,"w":141,"h":136}, @@ -466,7 +1010,7 @@ }, "sprites/buildings/splitter-compact.png": { - "frame": {"x":1631,"y":594,"w":139,"h":136}, + "frame": {"x":729,"y":1286,"w":139,"h":136}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":3,"w":139,"h":136}, @@ -474,7 +1018,7 @@ }, "sprites/buildings/splitter.png": { - "frame": {"x":1379,"y":298,"w":256,"h":143}, + "frame": {"x":555,"y":401,"w":256,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":17,"y":0,"w":256,"h":143}, @@ -482,7 +1026,7 @@ }, "sprites/buildings/stacker.png": { - "frame": {"x":547,"y":886,"w":260,"h":143}, + "frame": {"x":1539,"y":955,"w":260,"h":143}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":14,"y":0,"w":260,"h":143}, @@ -490,7 +1034,7 @@ }, "sprites/buildings/trash-storage.png": { - "frame": {"x":295,"y":704,"w":248,"h":288}, + "frame": {"x":3,"y":848,"w":248,"h":288}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":22,"y":0,"w":248,"h":288}, @@ -498,7 +1042,7 @@ }, "sprites/buildings/trash.png": { - "frame": {"x":1389,"y":445,"w":144,"h":144}, + "frame": {"x":1391,"y":955,"w":144,"h":144}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":144,"h":144}, @@ -506,7 +1050,7 @@ }, "sprites/buildings/underground_belt_entry-tier2.png": { - "frame": {"x":1631,"y":734,"w":137,"h":124}, + "frame": {"x":1014,"y":1284,"w":137,"h":124}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":20,"w":137,"h":124}, @@ -514,7 +1058,7 @@ }, "sprites/buildings/underground_belt_entry.png": { - "frame": {"x":1216,"y":739,"w":137,"h":111}, + "frame": {"x":1181,"y":1400,"w":137,"h":111}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":33,"w":137,"h":111}, @@ -522,7 +1066,7 @@ }, "sprites/buildings/underground_belt_exit-tier2.png": { - "frame": {"x":1217,"y":854,"w":137,"h":111}, + "frame": {"x":1297,"y":1284,"w":137,"h":111}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":137,"h":111}, @@ -530,7 +1074,7 @@ }, "sprites/buildings/underground_belt_exit.png": { - "frame": {"x":1358,"y":852,"w":137,"h":111}, + "frame": {"x":1322,"y":1399,"w":137,"h":111}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":137,"h":111}, @@ -538,7 +1082,7 @@ }, "sprites/debug/acceptor_slot.png": { - "frame": {"x":2006,"y":597,"w":38,"h":48}, + "frame": {"x":1711,"y":3,"w":38,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":38,"h":48}, @@ -546,7 +1090,7 @@ }, "sprites/debug/ejector_slot.png": { - "frame": {"x":2006,"y":649,"w":38,"h":48}, + "frame": {"x":1711,"y":55,"w":38,"h":48}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":38,"h":48}, @@ -554,7 +1098,7 @@ }, "sprites/map_overview/belt_forward.png": { - "frame": {"x":1826,"y":955,"w":20,"h":24}, + "frame": {"x":842,"y":807,"w":20,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":20,"h":24}, @@ -562,7 +1106,7 @@ }, "sprites/map_overview/belt_left.png": { - "frame": {"x":206,"y":994,"w":22,"h":22}, + "frame": {"x":890,"y":807,"w":22,"h":22}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":22,"h":22}, @@ -570,7 +1114,7 @@ }, "sprites/map_overview/belt_right.png": { - "frame": {"x":2006,"y":701,"w":22,"h":22}, + "frame": {"x":916,"y":807,"w":22,"h":22}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":22,"h":22}, @@ -578,7 +1122,7 @@ }, "sprites/misc/deletion_marker.png": { - "frame": {"x":140,"y":994,"w":62,"h":62}, + "frame": {"x":1355,"y":377,"w":62,"h":62}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":5,"w":62,"h":62}, @@ -586,7 +1130,7 @@ }, "sprites/misc/hub_direction_indicator.png": { - "frame": {"x":1933,"y":299,"w":24,"h":24}, + "frame": {"x":814,"y":807,"w":24,"h":24}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":24,"h":24}, @@ -594,7 +1138,7 @@ }, "sprites/misc/lock_direction_indicator.png": { - "frame": {"x":1357,"y":733,"w":36,"h":24}, + "frame": {"x":774,"y":807,"w":36,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":6,"w":36,"h":24}, @@ -602,7 +1146,7 @@ }, "sprites/misc/slot_bad_arrow.png": { - "frame": {"x":140,"y":994,"w":62,"h":62}, + "frame": {"x":1355,"y":377,"w":62,"h":62}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":5,"w":62,"h":62}, @@ -610,7 +1154,7 @@ }, "sprites/misc/slot_good_arrow.png": { - "frame": {"x":1760,"y":955,"w":62,"h":72}, + "frame": {"x":1436,"y":287,"w":62,"h":72}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":0,"w":62,"h":72}, @@ -618,7 +1162,7 @@ }, "sprites/misc/storage_overlay.png": { - "frame": {"x":3,"y":994,"w":133,"h":66}, + "frame": {"x":1755,"y":589,"w":133,"h":66}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":2,"w":133,"h":66}, @@ -626,7 +1170,7 @@ }, "sprites/misc/waypoint.png": { - "frame": {"x":1357,"y":761,"w":20,"h":24}, + "frame": {"x":866,"y":807,"w":20,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":20,"h":24}, @@ -637,8 +1181,8 @@ "version": "1.0", "image": "atlas0_75.png", "format": "RGBA8888", - "size": {"w":2047,"h":1063}, + "size": {"w":1894,"h":1530}, "scale": "0.75", - "smartupdate": "$TexturePacker:SmartUpdate:8985524bba1a3f16cecab3c03b6aaa06:d614d20bfce033d8a8ab0990af085d16:f159918d23e5952766c6d23ab52278c6$" + "smartupdate": "$TexturePacker:SmartUpdate:dc34796c028235bbc69e5b8d854254ca:2765d0b8c8bcbb7a4aaaf1104853ad41:8778749683c68f53155587e6d831729a$" } } diff --git a/res_built/atlas/atlas0_75.png b/res_built/atlas/atlas0_75.png index f4844add..5b42712a 100644 Binary files a/res_built/atlas/atlas0_75.png and b/res_built/atlas/atlas0_75.png differ diff --git a/res_built/atlas/atlas1_100.json b/res_built/atlas/atlas1_100.json new file mode 100644 index 00000000..3581b36a --- /dev/null +++ b/res_built/atlas/atlas1_100.json @@ -0,0 +1,509 @@ +{"frames": { + +"sprites/belt/forward_3.png": +{ + "frame": {"x":3,"y":783,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_4.png": +{ + "frame": {"x":3,"y":913,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_5.png": +{ + "frame": {"x":3,"y":1043,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_6.png": +{ + "frame": {"x":3,"y":1173,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_7.png": +{ + "frame": {"x":3,"y":1303,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_8.png": +{ + "frame": {"x":107,"y":3,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_9.png": +{ + "frame": {"x":107,"y":133,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_22.png": +{ + "frame": {"x":3,"y":3,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_23.png": +{ + "frame": {"x":3,"y":133,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_24.png": +{ + "frame": {"x":3,"y":263,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_25.png": +{ + "frame": {"x":3,"y":393,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_26.png": +{ + "frame": {"x":3,"y":523,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/forward_27.png": +{ + "frame": {"x":3,"y":653,"w":100,"h":126}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":0,"w":100,"h":126}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_2.png": +{ + "frame": {"x":3,"y":1784,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_3.png": +{ + "frame": {"x":107,"y":965,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_4.png": +{ + "frame": {"x":107,"y":1082,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_5.png": +{ + "frame": {"x":107,"y":1199,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_6.png": +{ + "frame": {"x":107,"y":1316,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_7.png": +{ + "frame": {"x":120,"y":1433,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_8.png": +{ + "frame": {"x":120,"y":1550,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_9.png": +{ + "frame": {"x":120,"y":1667,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_17.png": +{ + "frame": {"x":3,"y":1433,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_18.png": +{ + "frame": {"x":3,"y":1550,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_19.png": +{ + "frame": {"x":3,"y":1667,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_20.png": +{ + "frame": {"x":107,"y":263,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_21.png": +{ + "frame": {"x":211,"y":3,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_22.png": +{ + "frame": {"x":211,"y":120,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_23.png": +{ + "frame": {"x":107,"y":380,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_24.png": +{ + "frame": {"x":107,"y":497,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_25.png": +{ + "frame": {"x":107,"y":614,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_26.png": +{ + "frame": {"x":107,"y":731,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/left_27.png": +{ + "frame": {"x":107,"y":848,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_0.png": +{ + "frame": {"x":120,"y":1784,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_1.png": +{ + "frame": {"x":224,"y":237,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_2.png": +{ + "frame": {"x":224,"y":1290,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_3.png": +{ + "frame": {"x":341,"y":705,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_4.png": +{ + "frame": {"x":341,"y":822,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_5.png": +{ + "frame": {"x":341,"y":939,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_6.png": +{ + "frame": {"x":341,"y":1056,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_7.png": +{ + "frame": {"x":341,"y":1173,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_8.png": +{ + "frame": {"x":341,"y":1290,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_9.png": +{ + "frame": {"x":354,"y":1407,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_10.png": +{ + "frame": {"x":328,"y":3,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_11.png": +{ + "frame": {"x":328,"y":120,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_12.png": +{ + "frame": {"x":224,"y":354,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_13.png": +{ + "frame": {"x":224,"y":471,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_14.png": +{ + "frame": {"x":224,"y":588,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_15.png": +{ + "frame": {"x":224,"y":705,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_16.png": +{ + "frame": {"x":224,"y":822,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_17.png": +{ + "frame": {"x":224,"y":939,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_18.png": +{ + "frame": {"x":224,"y":1056,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_19.png": +{ + "frame": {"x":224,"y":1173,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_20.png": +{ + "frame": {"x":237,"y":1407,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_21.png": +{ + "frame": {"x":237,"y":1524,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_22.png": +{ + "frame": {"x":237,"y":1641,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_23.png": +{ + "frame": {"x":237,"y":1758,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_24.png": +{ + "frame": {"x":341,"y":237,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_25.png": +{ + "frame": {"x":341,"y":354,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_26.png": +{ + "frame": {"x":341,"y":471,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/belt/right_27.png": +{ + "frame": {"x":341,"y":588,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/buildings/belt_left.png": +{ + "frame": {"x":354,"y":1524,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":0,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}, +"sprites/buildings/belt_right.png": +{ + "frame": {"x":354,"y":1641,"w":113,"h":113}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":13,"y":13,"w":113,"h":113}, + "sourceSize": {"w":126,"h":126} +}}, +"meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "1.0", + "image": "atlas1_100.png", + "format": "RGBA8888", + "size": {"w":470,"h":1900}, + "scale": "1", + "related_multi_packs": [ "atlas0_100.json" ], + "smartupdate": "$TexturePacker:SmartUpdate:dc34796c028235bbc69e5b8d854254ca:2765d0b8c8bcbb7a4aaaf1104853ad41:8778749683c68f53155587e6d831729a$" +} +} diff --git a/res_built/atlas/atlas1_100.png b/res_built/atlas/atlas1_100.png new file mode 100644 index 00000000..75210aad Binary files /dev/null and b/res_built/atlas/atlas1_100.png differ diff --git a/res_raw/atlas.tps b/res_raw/atlas.tps index 07da7809..d4e8f4e0 100644 --- a/res_raw/atlas.tps +++ b/res_raw/atlas.tps @@ -269,21 +269,87 @@ scale9FromFile + sprites/belt/forward_10.png + sprites/belt/forward_11.png + sprites/belt/forward_12.png + sprites/belt/forward_13.png + sprites/belt/forward_14.png + sprites/belt/forward_15.png + sprites/belt/forward_16.png + sprites/belt/forward_17.png + sprites/belt/forward_18.png + sprites/belt/forward_19.png + sprites/belt/forward_20.png + sprites/belt/forward_21.png + sprites/belt/forward_22.png + sprites/belt/forward_23.png + sprites/belt/forward_24.png + sprites/belt/forward_25.png + sprites/belt/forward_26.png + sprites/belt/forward_27.png sprites/belt/forward_3.png sprites/belt/forward_4.png sprites/belt/forward_5.png + sprites/belt/forward_6.png + sprites/belt/forward_7.png + sprites/belt/forward_8.png + sprites/belt/forward_9.png sprites/belt/left_0.png sprites/belt/left_1.png + sprites/belt/left_10.png + sprites/belt/left_11.png + sprites/belt/left_12.png + sprites/belt/left_13.png + sprites/belt/left_14.png + sprites/belt/left_15.png + sprites/belt/left_16.png + sprites/belt/left_17.png + sprites/belt/left_18.png + sprites/belt/left_19.png sprites/belt/left_2.png + sprites/belt/left_20.png + sprites/belt/left_21.png + sprites/belt/left_22.png + sprites/belt/left_23.png + sprites/belt/left_24.png + sprites/belt/left_25.png + sprites/belt/left_26.png + sprites/belt/left_27.png sprites/belt/left_3.png sprites/belt/left_4.png sprites/belt/left_5.png + sprites/belt/left_6.png + sprites/belt/left_7.png + sprites/belt/left_8.png + sprites/belt/left_9.png sprites/belt/right_0.png sprites/belt/right_1.png + sprites/belt/right_10.png + sprites/belt/right_11.png + sprites/belt/right_12.png + sprites/belt/right_13.png + sprites/belt/right_14.png + sprites/belt/right_15.png + sprites/belt/right_16.png + sprites/belt/right_17.png + sprites/belt/right_18.png + sprites/belt/right_19.png sprites/belt/right_2.png + sprites/belt/right_20.png + sprites/belt/right_21.png + sprites/belt/right_22.png + sprites/belt/right_23.png + sprites/belt/right_24.png + sprites/belt/right_25.png + sprites/belt/right_26.png + sprites/belt/right_27.png sprites/belt/right_3.png sprites/belt/right_4.png sprites/belt/right_5.png + sprites/belt/right_6.png + sprites/belt/right_7.png + sprites/belt/right_8.png + sprites/belt/right_9.png sprites/blueprints/belt_left.png sprites/blueprints/belt_right.png sprites/blueprints/belt_top.png @@ -343,6 +409,25 @@ scale9FromFile + sprites/blueprints/energy_generator.png + sprites/blueprints/painter-double.png + sprites/blueprints/trash-storage.png + sprites/buildings/energy_generator.png + sprites/buildings/painter-double.png + + pivotPoint + 0.5,0.5 + spriteScale + 1 + scale9Enabled + + scale9Borders + 96,96,192,192 + scale9Paddings + 96,96,192,192 + scale9FromFile + + sprites/blueprints/miner-chainable.png sprites/blueprints/miner.png sprites/blueprints/rotater-ccw.png @@ -376,23 +461,6 @@ scale9FromFile - sprites/blueprints/painter-double.png - sprites/blueprints/trash-storage.png - sprites/buildings/painter-double.png - - pivotPoint - 0.5,0.5 - spriteScale - 1 - scale9Enabled - - scale9Borders - 96,96,192,192 - scale9Paddings - 96,96,192,192 - scale9FromFile - - sprites/buildings/cutter.png sprites/buildings/mixer.png sprites/buildings/painter.png diff --git a/res_raw/sprites/belt/forward_0.png b/res_raw/sprites/belt/forward_0.png index def37d7d..cf5b20d7 100644 Binary files a/res_raw/sprites/belt/forward_0.png and b/res_raw/sprites/belt/forward_0.png differ diff --git a/res_raw/sprites/belt/forward_1.png b/res_raw/sprites/belt/forward_1.png index 1ed11ef2..44e5b8c8 100644 Binary files a/res_raw/sprites/belt/forward_1.png and b/res_raw/sprites/belt/forward_1.png differ diff --git a/res_raw/sprites/belt/forward_10.png b/res_raw/sprites/belt/forward_10.png new file mode 100644 index 00000000..d599463d Binary files /dev/null and b/res_raw/sprites/belt/forward_10.png differ diff --git a/res_raw/sprites/belt/forward_11.png b/res_raw/sprites/belt/forward_11.png new file mode 100644 index 00000000..69fff65c Binary files /dev/null and b/res_raw/sprites/belt/forward_11.png differ diff --git a/res_raw/sprites/belt/forward_12.png b/res_raw/sprites/belt/forward_12.png new file mode 100644 index 00000000..a278e6f0 Binary files /dev/null and b/res_raw/sprites/belt/forward_12.png differ diff --git a/res_raw/sprites/belt/forward_13.png b/res_raw/sprites/belt/forward_13.png new file mode 100644 index 00000000..13630f42 Binary files /dev/null and b/res_raw/sprites/belt/forward_13.png differ diff --git a/res_raw/sprites/belt/forward_14.png b/res_raw/sprites/belt/forward_14.png new file mode 100644 index 00000000..1c6bdfb8 Binary files /dev/null and b/res_raw/sprites/belt/forward_14.png differ diff --git a/res_raw/sprites/belt/forward_15.png b/res_raw/sprites/belt/forward_15.png new file mode 100644 index 00000000..be3daa82 Binary files /dev/null and b/res_raw/sprites/belt/forward_15.png differ diff --git a/res_raw/sprites/belt/forward_16.png b/res_raw/sprites/belt/forward_16.png new file mode 100644 index 00000000..7b9c9dcf Binary files /dev/null and b/res_raw/sprites/belt/forward_16.png differ diff --git a/res_raw/sprites/belt/forward_17.png b/res_raw/sprites/belt/forward_17.png new file mode 100644 index 00000000..cc37d11a Binary files /dev/null and b/res_raw/sprites/belt/forward_17.png differ diff --git a/res_raw/sprites/belt/forward_18.png b/res_raw/sprites/belt/forward_18.png new file mode 100644 index 00000000..7a6460f3 Binary files /dev/null and b/res_raw/sprites/belt/forward_18.png differ diff --git a/res_raw/sprites/belt/forward_19.png b/res_raw/sprites/belt/forward_19.png new file mode 100644 index 00000000..c18e2773 Binary files /dev/null and b/res_raw/sprites/belt/forward_19.png differ diff --git a/res_raw/sprites/belt/forward_2.png b/res_raw/sprites/belt/forward_2.png index f8620703..5a621fbb 100644 Binary files a/res_raw/sprites/belt/forward_2.png and b/res_raw/sprites/belt/forward_2.png differ diff --git a/res_raw/sprites/belt/forward_20.png b/res_raw/sprites/belt/forward_20.png new file mode 100644 index 00000000..ac3f50db Binary files /dev/null and b/res_raw/sprites/belt/forward_20.png differ diff --git a/res_raw/sprites/belt/forward_21.png b/res_raw/sprites/belt/forward_21.png new file mode 100644 index 00000000..67d63a4a Binary files /dev/null and b/res_raw/sprites/belt/forward_21.png differ diff --git a/res_raw/sprites/belt/forward_22.png b/res_raw/sprites/belt/forward_22.png new file mode 100644 index 00000000..f20ec8d3 Binary files /dev/null and b/res_raw/sprites/belt/forward_22.png differ diff --git a/res_raw/sprites/belt/forward_23.png b/res_raw/sprites/belt/forward_23.png new file mode 100644 index 00000000..daa5d1d8 Binary files /dev/null and b/res_raw/sprites/belt/forward_23.png differ diff --git a/res_raw/sprites/belt/forward_24.png b/res_raw/sprites/belt/forward_24.png new file mode 100644 index 00000000..c21bc67d Binary files /dev/null and b/res_raw/sprites/belt/forward_24.png differ diff --git a/res_raw/sprites/belt/forward_25.png b/res_raw/sprites/belt/forward_25.png new file mode 100644 index 00000000..4e603e43 Binary files /dev/null and b/res_raw/sprites/belt/forward_25.png differ diff --git a/res_raw/sprites/belt/forward_26.png b/res_raw/sprites/belt/forward_26.png new file mode 100644 index 00000000..55ae0c96 Binary files /dev/null and b/res_raw/sprites/belt/forward_26.png differ diff --git a/res_raw/sprites/belt/forward_27.png b/res_raw/sprites/belt/forward_27.png new file mode 100644 index 00000000..816ac1c6 Binary files /dev/null and b/res_raw/sprites/belt/forward_27.png differ diff --git a/res_raw/sprites/belt/forward_3.png b/res_raw/sprites/belt/forward_3.png index 0c0647fa..cf2ef011 100644 Binary files a/res_raw/sprites/belt/forward_3.png and b/res_raw/sprites/belt/forward_3.png differ diff --git a/res_raw/sprites/belt/forward_4.png b/res_raw/sprites/belt/forward_4.png index 5b6f59c0..b06fb99f 100644 Binary files a/res_raw/sprites/belt/forward_4.png and b/res_raw/sprites/belt/forward_4.png differ diff --git a/res_raw/sprites/belt/forward_5.png b/res_raw/sprites/belt/forward_5.png index 850f14a8..79354fbb 100644 Binary files a/res_raw/sprites/belt/forward_5.png and b/res_raw/sprites/belt/forward_5.png differ diff --git a/res_raw/sprites/belt/forward_6.png b/res_raw/sprites/belt/forward_6.png new file mode 100644 index 00000000..3df44015 Binary files /dev/null and b/res_raw/sprites/belt/forward_6.png differ diff --git a/res_raw/sprites/belt/forward_7.png b/res_raw/sprites/belt/forward_7.png new file mode 100644 index 00000000..72251706 Binary files /dev/null and b/res_raw/sprites/belt/forward_7.png differ diff --git a/res_raw/sprites/belt/forward_8.png b/res_raw/sprites/belt/forward_8.png new file mode 100644 index 00000000..ad28047a Binary files /dev/null and b/res_raw/sprites/belt/forward_8.png differ diff --git a/res_raw/sprites/belt/forward_9.png b/res_raw/sprites/belt/forward_9.png new file mode 100644 index 00000000..daae565f Binary files /dev/null and b/res_raw/sprites/belt/forward_9.png differ diff --git a/res_raw/sprites/belt/generate_belt_sprites.js b/res_raw/sprites/belt/generate_belt_sprites.js new file mode 100644 index 00000000..f0bd4aa1 --- /dev/null +++ b/res_raw/sprites/belt/generate_belt_sprites.js @@ -0,0 +1,173 @@ +/** + * + * Run `yarn global add canvas` first + */ + +const { createCanvas, loadImage } = require("canvas"); +const fs = require("fs"); +const path = require("path"); +const { fileURLToPath } = require("url"); + +async function run() { + console.log("Running"); + + const fps = 28; + const dimensions = 126; + const beltBorder = 15.5; + + const borderColor = "#91949e"; + const fillColor = "#d2d4d9"; + const arrowColor = "#c0c2c7"; + + // Generate arrow sprite + + const arrowW = 40; + const arrowH = 20; + /** @type {HTMLCanvasElement} */ + const arrowSprite = createCanvas(arrowW, arrowH); + const arrowContext = arrowSprite.getContext("2d"); + + arrowContext.quality = "best"; + arrowContext.fillStyle = arrowColor; + arrowContext.clearRect(0, 0, arrowW, arrowH); + arrowContext.beginPath(); + arrowContext.moveTo(0, arrowH); + arrowContext.lineTo(arrowW / 2, 0); + arrowContext.lineTo(arrowW, arrowH); + arrowContext.closePath(); + arrowContext.fill(); + + // First, generate the forward belt + for (let i = 0; i < fps; ++i) { + /** @type {HTMLCanvasElement} */ + const canvas = createCanvas(dimensions, dimensions); + const context = canvas.getContext("2d"); + context.quality = "best"; + + const procentual = i / fps; + context.clearRect(0, 0, dimensions, dimensions); + + context.fillStyle = fillColor; + context.strokeStyle = borderColor; + context.lineWidth = 3; + + context.beginPath(); + context.rect(beltBorder, -10, dimensions - 2 * beltBorder, dimensions + 20); + context.fill(); + context.stroke(); + + const spacingBetweenArrows = (dimensions - 3 * arrowH) / 3; + const spacingTotal = spacingBetweenArrows + arrowH; + + for (let k = 0; k < 5; ++k) { + let y = dimensions - arrowH - (k - 1) * spacingTotal - procentual * spacingTotal; + context.drawImage(arrowSprite, dimensions / 2 - arrowW / 2, y); + } + + const out = fs.createWriteStream(path.join(__dirname, "forward_" + i + ".png")); + const stream = canvas.createPNGStream(); + stream.pipe(out); + } + + // Generate left and right side belt + for (let i = 0; i < fps; ++i) { + /** @type {HTMLCanvasElement} */ + const canvas = createCanvas(dimensions, dimensions); + const context = canvas.getContext("2d"); + context.quality = "best"; + + const procentual = i / fps; + const innerRadius = beltBorder; + context.clearRect(0, 0, dimensions, dimensions); + + context.fillStyle = fillColor; + context.strokeStyle = borderColor; + context.lineWidth = 3; + + context.beginPath(); + context.moveTo(beltBorder, dimensions + 10); + context.lineTo(beltBorder, dimensions - innerRadius); + + const steps = 256; + + const outerRadius = dimensions - 2 * beltBorder; + + const originX = dimensions - innerRadius; + const originY = dimensions - innerRadius; + + const sqrt = x => Math.pow(Math.abs(x), 0.975) * Math.sign(x); + + for (let k = 0; k <= steps; ++k) { + const pct = k / steps; + const angleRad = Math.PI + pct * Math.PI * 0.5; + const offX = originX + sqrt(Math.cos(angleRad)) * outerRadius; + const offY = originY + sqrt(Math.sin(angleRad)) * outerRadius; + + context.lineTo(offX, offY); + } + + context.lineTo(dimensions + 10, beltBorder); + context.lineTo(dimensions + 10, dimensions - beltBorder); + context.lineTo(dimensions, dimensions - beltBorder); + + for (let k = 0; k <= steps; ++k) { + const pct = 1 - k / steps; + const angleRad = Math.PI + pct * Math.PI * 0.5; + const offX = dimensions + Math.cos(angleRad) * innerRadius; + const offY = dimensions + Math.sin(angleRad) * innerRadius; + + context.lineTo(offX, offY); + } + + context.lineTo(dimensions - beltBorder, dimensions + 10); + + context.closePath(); + context.fill(); + context.stroke(); + + // Arrows + const rotationalRadius = dimensions / 2 - arrowH / 2 + 0.5; + + const circumfence = (rotationalRadius * Math.PI * 2) / 4; + console.log("Circumfence:", circumfence, "px"); + + const remainingSpace = circumfence - 3 * arrowH + arrowH; + console.log("Remainig:", remainingSpace); + const spacing = remainingSpace / 3 + arrowH; + + console.log("Spacing: ", spacing); + const angleSpacing = ((spacing / circumfence) * Math.PI) / 2; + + for (let i = 0; i < 5; ++i) { + let angleRad = Math.PI + procentual * angleSpacing + (i - 1) * angleSpacing; + const offX = dimensions - arrowH / 2 + Math.cos(angleRad * 0.995) * rotationalRadius; + const offY = dimensions - arrowH / 2 + Math.sin(angleRad * 0.995) * rotationalRadius; + + angleRad = Math.max(Math.PI, Math.min(1.5 * Math.PI, angleRad)); + + context.save(); + context.translate(offX, offY); + context.rotate(angleRad + Math.PI); + context.drawImage(arrowSprite, -arrowW / 2, -arrowH / 2); + context.restore(); + } + + /** @type {HTMLCanvasElement} */ + const flippedCanvas = createCanvas(dimensions, dimensions); + const flippedContext = flippedCanvas.getContext("2d"); + flippedContext.quality = "best"; + flippedContext.clearRect(0, 0, dimensions, dimensions); + flippedContext.scale(-1, 1); + flippedContext.drawImage(canvas, -dimensions, 0, dimensions, dimensions); + + const out = fs.createWriteStream(path.join(__dirname, "right_" + i + ".png")); + const stream = canvas.createPNGStream(); + stream.pipe(out); + + const outLeft = fs.createWriteStream(path.join(__dirname, "left_" + i + ".png")); + const streamLeft = flippedCanvas.createPNGStream(); + streamLeft.pipe(outLeft); + } +} + +run(); diff --git a/res_raw/sprites/belt/left_0.png b/res_raw/sprites/belt/left_0.png index 822d922d..7da4c390 100644 Binary files a/res_raw/sprites/belt/left_0.png and b/res_raw/sprites/belt/left_0.png differ diff --git a/res_raw/sprites/belt/left_1.png b/res_raw/sprites/belt/left_1.png index 24f77c4a..fa446ce4 100644 Binary files a/res_raw/sprites/belt/left_1.png and b/res_raw/sprites/belt/left_1.png differ diff --git a/res_raw/sprites/belt/left_10.png b/res_raw/sprites/belt/left_10.png new file mode 100644 index 00000000..260a4c23 Binary files /dev/null and b/res_raw/sprites/belt/left_10.png differ diff --git a/res_raw/sprites/belt/left_11.png b/res_raw/sprites/belt/left_11.png new file mode 100644 index 00000000..d0d70ab1 Binary files /dev/null and b/res_raw/sprites/belt/left_11.png differ diff --git a/res_raw/sprites/belt/left_12.png b/res_raw/sprites/belt/left_12.png new file mode 100644 index 00000000..3f916501 Binary files /dev/null and b/res_raw/sprites/belt/left_12.png differ diff --git a/res_raw/sprites/belt/left_13.png b/res_raw/sprites/belt/left_13.png new file mode 100644 index 00000000..71d9a634 Binary files /dev/null and b/res_raw/sprites/belt/left_13.png differ diff --git a/res_raw/sprites/belt/left_14.png b/res_raw/sprites/belt/left_14.png new file mode 100644 index 00000000..134b90af Binary files /dev/null and b/res_raw/sprites/belt/left_14.png differ diff --git a/res_raw/sprites/belt/left_15.png b/res_raw/sprites/belt/left_15.png new file mode 100644 index 00000000..623069a1 Binary files /dev/null and b/res_raw/sprites/belt/left_15.png differ diff --git a/res_raw/sprites/belt/left_16.png b/res_raw/sprites/belt/left_16.png new file mode 100644 index 00000000..14b18b45 Binary files /dev/null and b/res_raw/sprites/belt/left_16.png differ diff --git a/res_raw/sprites/belt/left_17.png b/res_raw/sprites/belt/left_17.png new file mode 100644 index 00000000..5aa84e5c Binary files /dev/null and b/res_raw/sprites/belt/left_17.png differ diff --git a/res_raw/sprites/belt/left_18.png b/res_raw/sprites/belt/left_18.png new file mode 100644 index 00000000..0fe1dedd Binary files /dev/null and b/res_raw/sprites/belt/left_18.png differ diff --git a/res_raw/sprites/belt/left_19.png b/res_raw/sprites/belt/left_19.png new file mode 100644 index 00000000..46e1b2b3 Binary files /dev/null and b/res_raw/sprites/belt/left_19.png differ diff --git a/res_raw/sprites/belt/left_2.png b/res_raw/sprites/belt/left_2.png index e8473cc7..9a8d15c0 100644 Binary files a/res_raw/sprites/belt/left_2.png and b/res_raw/sprites/belt/left_2.png differ diff --git a/res_raw/sprites/belt/left_20.png b/res_raw/sprites/belt/left_20.png new file mode 100644 index 00000000..1d6dd020 Binary files /dev/null and b/res_raw/sprites/belt/left_20.png differ diff --git a/res_raw/sprites/belt/left_21.png b/res_raw/sprites/belt/left_21.png new file mode 100644 index 00000000..413afb3c Binary files /dev/null and b/res_raw/sprites/belt/left_21.png differ diff --git a/res_raw/sprites/belt/left_22.png b/res_raw/sprites/belt/left_22.png new file mode 100644 index 00000000..c56c9607 Binary files /dev/null and b/res_raw/sprites/belt/left_22.png differ diff --git a/res_raw/sprites/belt/left_23.png b/res_raw/sprites/belt/left_23.png new file mode 100644 index 00000000..22c7e9d1 Binary files /dev/null and b/res_raw/sprites/belt/left_23.png differ diff --git a/res_raw/sprites/belt/left_24.png b/res_raw/sprites/belt/left_24.png new file mode 100644 index 00000000..1c15838a Binary files /dev/null and b/res_raw/sprites/belt/left_24.png differ diff --git a/res_raw/sprites/belt/left_25.png b/res_raw/sprites/belt/left_25.png new file mode 100644 index 00000000..20120842 Binary files /dev/null and b/res_raw/sprites/belt/left_25.png differ diff --git a/res_raw/sprites/belt/left_26.png b/res_raw/sprites/belt/left_26.png new file mode 100644 index 00000000..1d4de05f Binary files /dev/null and b/res_raw/sprites/belt/left_26.png differ diff --git a/res_raw/sprites/belt/left_27.png b/res_raw/sprites/belt/left_27.png new file mode 100644 index 00000000..ba0d699a Binary files /dev/null and b/res_raw/sprites/belt/left_27.png differ diff --git a/res_raw/sprites/belt/left_3.png b/res_raw/sprites/belt/left_3.png index d0984a31..7fc8c6fc 100644 Binary files a/res_raw/sprites/belt/left_3.png and b/res_raw/sprites/belt/left_3.png differ diff --git a/res_raw/sprites/belt/left_4.png b/res_raw/sprites/belt/left_4.png index 068c2c25..e8da79fd 100644 Binary files a/res_raw/sprites/belt/left_4.png and b/res_raw/sprites/belt/left_4.png differ diff --git a/res_raw/sprites/belt/left_5.png b/res_raw/sprites/belt/left_5.png index 52c53bcb..1a26dc72 100644 Binary files a/res_raw/sprites/belt/left_5.png and b/res_raw/sprites/belt/left_5.png differ diff --git a/res_raw/sprites/belt/left_6.png b/res_raw/sprites/belt/left_6.png new file mode 100644 index 00000000..c2322d45 Binary files /dev/null and b/res_raw/sprites/belt/left_6.png differ diff --git a/res_raw/sprites/belt/left_7.png b/res_raw/sprites/belt/left_7.png new file mode 100644 index 00000000..a85a31fc Binary files /dev/null and b/res_raw/sprites/belt/left_7.png differ diff --git a/res_raw/sprites/belt/left_8.png b/res_raw/sprites/belt/left_8.png new file mode 100644 index 00000000..9ff9945b Binary files /dev/null and b/res_raw/sprites/belt/left_8.png differ diff --git a/res_raw/sprites/belt/left_9.png b/res_raw/sprites/belt/left_9.png new file mode 100644 index 00000000..bfe4360e Binary files /dev/null and b/res_raw/sprites/belt/left_9.png differ diff --git a/res_raw/sprites/belt/package.json b/res_raw/sprites/belt/package.json new file mode 100644 index 00000000..eba97da2 --- /dev/null +++ b/res_raw/sprites/belt/package.json @@ -0,0 +1,12 @@ +{ + "name": "belt", + "version": "1.0.0", + "main": "index.js", + "private": "true", + "devDependencies": { + "canvas": "^2.6.1" + }, + "scripts": { + "generate": "node generate_belt_sprites.js" + } +} diff --git a/res_raw/sprites/belt/right_0.png b/res_raw/sprites/belt/right_0.png index cdebc96f..fca35eab 100644 Binary files a/res_raw/sprites/belt/right_0.png and b/res_raw/sprites/belt/right_0.png differ diff --git a/res_raw/sprites/belt/right_1.png b/res_raw/sprites/belt/right_1.png index 4582b007..101578e9 100644 Binary files a/res_raw/sprites/belt/right_1.png and b/res_raw/sprites/belt/right_1.png differ diff --git a/res_raw/sprites/belt/right_10.png b/res_raw/sprites/belt/right_10.png new file mode 100644 index 00000000..cd733017 Binary files /dev/null and b/res_raw/sprites/belt/right_10.png differ diff --git a/res_raw/sprites/belt/right_11.png b/res_raw/sprites/belt/right_11.png new file mode 100644 index 00000000..5d082ecc Binary files /dev/null and b/res_raw/sprites/belt/right_11.png differ diff --git a/res_raw/sprites/belt/right_12.png b/res_raw/sprites/belt/right_12.png new file mode 100644 index 00000000..3c7f08bb Binary files /dev/null and b/res_raw/sprites/belt/right_12.png differ diff --git a/res_raw/sprites/belt/right_13.png b/res_raw/sprites/belt/right_13.png new file mode 100644 index 00000000..71364d48 Binary files /dev/null and b/res_raw/sprites/belt/right_13.png differ diff --git a/res_raw/sprites/belt/right_14.png b/res_raw/sprites/belt/right_14.png new file mode 100644 index 00000000..cf5df0a4 Binary files /dev/null and b/res_raw/sprites/belt/right_14.png differ diff --git a/res_raw/sprites/belt/right_15.png b/res_raw/sprites/belt/right_15.png new file mode 100644 index 00000000..1292ec67 Binary files /dev/null and b/res_raw/sprites/belt/right_15.png differ diff --git a/res_raw/sprites/belt/right_16.png b/res_raw/sprites/belt/right_16.png new file mode 100644 index 00000000..4dcc2c5f Binary files /dev/null and b/res_raw/sprites/belt/right_16.png differ diff --git a/res_raw/sprites/belt/right_17.png b/res_raw/sprites/belt/right_17.png new file mode 100644 index 00000000..489f0e54 Binary files /dev/null and b/res_raw/sprites/belt/right_17.png differ diff --git a/res_raw/sprites/belt/right_18.png b/res_raw/sprites/belt/right_18.png new file mode 100644 index 00000000..208f402b Binary files /dev/null and b/res_raw/sprites/belt/right_18.png differ diff --git a/res_raw/sprites/belt/right_19.png b/res_raw/sprites/belt/right_19.png new file mode 100644 index 00000000..5e4e376b Binary files /dev/null and b/res_raw/sprites/belt/right_19.png differ diff --git a/res_raw/sprites/belt/right_2.png b/res_raw/sprites/belt/right_2.png index 79b30f46..c9affce1 100644 Binary files a/res_raw/sprites/belt/right_2.png and b/res_raw/sprites/belt/right_2.png differ diff --git a/res_raw/sprites/belt/right_20.png b/res_raw/sprites/belt/right_20.png new file mode 100644 index 00000000..77a4f43b Binary files /dev/null and b/res_raw/sprites/belt/right_20.png differ diff --git a/res_raw/sprites/belt/right_21.png b/res_raw/sprites/belt/right_21.png new file mode 100644 index 00000000..ca1d0f5f Binary files /dev/null and b/res_raw/sprites/belt/right_21.png differ diff --git a/res_raw/sprites/belt/right_22.png b/res_raw/sprites/belt/right_22.png new file mode 100644 index 00000000..d07bf72d Binary files /dev/null and b/res_raw/sprites/belt/right_22.png differ diff --git a/res_raw/sprites/belt/right_23.png b/res_raw/sprites/belt/right_23.png new file mode 100644 index 00000000..babfca3f Binary files /dev/null and b/res_raw/sprites/belt/right_23.png differ diff --git a/res_raw/sprites/belt/right_24.png b/res_raw/sprites/belt/right_24.png new file mode 100644 index 00000000..1c614c85 Binary files /dev/null and b/res_raw/sprites/belt/right_24.png differ diff --git a/res_raw/sprites/belt/right_25.png b/res_raw/sprites/belt/right_25.png new file mode 100644 index 00000000..d72a8615 Binary files /dev/null and b/res_raw/sprites/belt/right_25.png differ diff --git a/res_raw/sprites/belt/right_26.png b/res_raw/sprites/belt/right_26.png new file mode 100644 index 00000000..ed37636f Binary files /dev/null and b/res_raw/sprites/belt/right_26.png differ diff --git a/res_raw/sprites/belt/right_27.png b/res_raw/sprites/belt/right_27.png new file mode 100644 index 00000000..fe9ffc10 Binary files /dev/null and b/res_raw/sprites/belt/right_27.png differ diff --git a/res_raw/sprites/belt/right_3.png b/res_raw/sprites/belt/right_3.png index 7e55e829..feb7101b 100644 Binary files a/res_raw/sprites/belt/right_3.png and b/res_raw/sprites/belt/right_3.png differ diff --git a/res_raw/sprites/belt/right_4.png b/res_raw/sprites/belt/right_4.png index 9862d108..22c885a6 100644 Binary files a/res_raw/sprites/belt/right_4.png and b/res_raw/sprites/belt/right_4.png differ diff --git a/res_raw/sprites/belt/right_5.png b/res_raw/sprites/belt/right_5.png index f735403a..27bb27aa 100644 Binary files a/res_raw/sprites/belt/right_5.png and b/res_raw/sprites/belt/right_5.png differ diff --git a/res_raw/sprites/belt/right_6.png b/res_raw/sprites/belt/right_6.png new file mode 100644 index 00000000..5ca871b7 Binary files /dev/null and b/res_raw/sprites/belt/right_6.png differ diff --git a/res_raw/sprites/belt/right_7.png b/res_raw/sprites/belt/right_7.png new file mode 100644 index 00000000..a9cde450 Binary files /dev/null and b/res_raw/sprites/belt/right_7.png differ diff --git a/res_raw/sprites/belt/right_8.png b/res_raw/sprites/belt/right_8.png new file mode 100644 index 00000000..f09195fe Binary files /dev/null and b/res_raw/sprites/belt/right_8.png differ diff --git a/res_raw/sprites/belt/right_9.png b/res_raw/sprites/belt/right_9.png new file mode 100644 index 00000000..b6ca38c1 Binary files /dev/null and b/res_raw/sprites/belt/right_9.png differ diff --git a/res_raw/sprites/belt/yarn.lock b/res_raw/sprites/belt/yarn.lock new file mode 100644 index 00000000..4011ebc0 --- /dev/null +++ b/res_raw/sprites/belt/yarn.lock @@ -0,0 +1,514 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +canvas@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.6.1.tgz#0d087dd4d60f5a5a9efa202757270abea8bef89e" + integrity sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA== + dependencies: + nan "^2.14.0" + node-pre-gyp "^0.11.0" + simple-get "^3.0.3" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nan@^2.14.0: + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== + +needle@^2.2.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.0.tgz#e6fc4b3cc6c25caed7554bd613a5cf0bac8c31c0" + integrity sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +node-pre-gyp@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" + integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +nopt@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +once@^1.3.0, once@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^2.0.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +semver@^5.3.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +signal-exit@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + +simple-get@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" + integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +tar@^4: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== diff --git a/res_raw/sprites/blueprints/belt_left.png b/res_raw/sprites/blueprints/belt_left.png index 476a17e2..594bb203 100644 Binary files a/res_raw/sprites/blueprints/belt_left.png and b/res_raw/sprites/blueprints/belt_left.png differ diff --git a/res_raw/sprites/blueprints/belt_right.png b/res_raw/sprites/blueprints/belt_right.png index 77ba6a33..9c33404b 100644 Binary files a/res_raw/sprites/blueprints/belt_right.png and b/res_raw/sprites/blueprints/belt_right.png differ diff --git a/res_raw/sprites/blueprints/belt_top.png b/res_raw/sprites/blueprints/belt_top.png index d8532b64..881c443a 100644 Binary files a/res_raw/sprites/blueprints/belt_top.png and b/res_raw/sprites/blueprints/belt_top.png differ diff --git a/res_raw/sprites/blueprints/energy_generator.png b/res_raw/sprites/blueprints/energy_generator.png new file mode 100644 index 00000000..e1534b57 Binary files /dev/null and b/res_raw/sprites/blueprints/energy_generator.png differ diff --git a/res_raw/sprites/buildings/belt_left.png b/res_raw/sprites/buildings/belt_left.png index 822d922d..60bebd29 100644 Binary files a/res_raw/sprites/buildings/belt_left.png and b/res_raw/sprites/buildings/belt_left.png differ diff --git a/res_raw/sprites/buildings/belt_right.png b/res_raw/sprites/buildings/belt_right.png index cdebc96f..547387b4 100644 Binary files a/res_raw/sprites/buildings/belt_right.png and b/res_raw/sprites/buildings/belt_right.png differ diff --git a/res_raw/sprites/buildings/belt_top.png b/res_raw/sprites/buildings/belt_top.png index def37d7d..cf5b20d7 100644 Binary files a/res_raw/sprites/buildings/belt_top.png and b/res_raw/sprites/buildings/belt_top.png differ diff --git a/res_raw/sprites/buildings/energy_generator.png b/res_raw/sprites/buildings/energy_generator.png new file mode 100644 index 00000000..e06b86ea Binary files /dev/null and b/res_raw/sprites/buildings/energy_generator.png differ diff --git a/src/css/icons.scss b/src/css/icons.scss index 2128fde3..bfc67713 100644 --- a/src/css/icons.scss +++ b/src/css/icons.scss @@ -1,4 +1,5 @@ -$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; @each $building in $buildings { [data-icon="building_icons/#{$building}.png"] { @@ -27,8 +28,8 @@ $icons: notification_saved, notification_success, notification_upgrade; } } -$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi, th, - hu, pl, ja, kor, no, pt-PT; +$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi, + th, hu, pl, ja, kor, no, pt-PT; @each $language in $languages { [data-languageicon="#{$language}"] { diff --git a/src/js/application.js b/src/js/application.js index f08b467e..1726bd2d 100644 --- a/src/js/application.js +++ b/src/js/application.js @@ -1,6 +1,5 @@ import { AnimationFrame } from "./core/animation_frame"; import { BackgroundResourcesLoader } from "./core/background_resources_loader"; -import { performanceNow } from "./core/builtins"; import { IS_MOBILE } from "./core/config"; import { GameState } from "./core/game_state"; import { GLOBAL_APP, setGlobalApp } from "./core/globals"; @@ -356,7 +355,7 @@ export class Application { return; } - const time = performanceNow(); + const time = performance.now(); // Periodically check for resizes, this is expensive (takes 2-3ms so only do it once in a while!) if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) { diff --git a/src/js/changelog.js b/src/js/changelog.js index e84cb269..f079cafa 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -1,14 +1,18 @@ export const CHANGELOG = [ { version: "1.1.18", - date: "24.06.2020", + date: "27.06.2020", entries: [ - "Preparations for the wires update", - "Update belt placement performance on huge factories (by Phlosioneer)", + "Huge performance improvements - up to double fps and tick-rate! This will wipe out all current items on belts.", + "Reduce story shapes required until unlocking blueprints", "Allow clicking on variants to select them", - "Allow clicking 'Q' over a shape or color patch to automatically select the miner building (by Gerdon262)", "Add 'copy key' button to shape viewer", + "Add more FPS to the belt animation and fix belt animation seeming to go 'backwards' on high belt speeds", "Fix deconstruct sound being played when right clicking hub", + "Allow clicking 'Q' over a shape or color patch to automatically select the miner building (by Gerdon262)", + "Update belt placement performance on huge factories (by Phlosioneer)", + "Fix duplicate waypoints with a shape not rendering (by hexy)", + "Fix smart tunnel placement deleting wrong tunnels (by mordof)", "Add setting (on by default) to store the last used rotation per building instead of globally storing it (by Magos)", "Added chinese (traditional) translation", "Updated translations", diff --git a/src/js/core/animation_frame.js b/src/js/core/animation_frame.js index b7243af7..0e921174 100644 --- a/src/js/core/animation_frame.js +++ b/src/js/core/animation_frame.js @@ -4,8 +4,6 @@ import { Signal } from "./signal"; import BackgroundAnimationFrameEmitterWorker from "../webworkers/background_animation_frame_emittter.worker"; import { createLogger } from "./logging"; -import { performanceNow } from "./builtins"; - const logger = createLogger("animation_frame"); const maxDtMs = 1000; @@ -34,7 +32,7 @@ export class AnimationFrame { * @param {MessageEvent} event */ handleBackgroundTick(event) { - const time = performanceNow(); + const time = performance.now(); if (!this.bgLastTime) { // First update, first delta is always 16ms this.bgFrameEmitted.dispatch(1000 / 60); diff --git a/src/js/core/async_compression.js b/src/js/core/async_compression.js index 4afed1ea..b11f8fd5 100644 --- a/src/js/core/async_compression.js +++ b/src/js/core/async_compression.js @@ -2,8 +2,6 @@ import CompressionWorker from "../webworkers/compression.worker"; import { createLogger } from "./logging"; import { compressX64 } from "./lzstring"; -import { performanceNow, JSON_stringify } from "./builtins"; - const logger = createLogger("async_compression"); export let compressionPrefix = String.fromCodePoint(1); @@ -53,7 +51,7 @@ class AsynCompression { return; } - const duration = performanceNow() - jobData.startTime; + const duration = performance.now() - jobData.startTime; // log(this, "Got response from worker within", duration.toFixed(2), "ms"); const resolver = jobData.resolver; delete this.currentJobs[jobId]; @@ -100,7 +98,7 @@ class AsynCompression { this.currentJobs[jobId] = { errorHandler, resolver: resolve, - startTime: performanceNow(), + startTime: performance.now(), }; this.worker.postMessage({ jobId, job, data }); }); diff --git a/src/js/core/buffer_utils.js b/src/js/core/buffer_utils.js index 3c087d1b..228560bc 100644 --- a/src/js/core/buffer_utils.js +++ b/src/js/core/buffer_utils.js @@ -1,5 +1,4 @@ import { globalConfig } from "./config"; -import { Math_max, Math_floor, Math_abs } from "./builtins"; import { fastArrayDelete } from "./utils"; import { createLogger } from "./logging"; @@ -70,8 +69,8 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe } if (w < 1 || h < 1) { logger.error("Offscreen buffer size < 0:", w, "x", h); - w = Math_max(1, w); - h = Math_max(1, h); + w = Math.max(1, w); + h = Math.max(1, h); } const recommendedSize = 1024 * 1024; @@ -79,8 +78,8 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe logger.warn("Creating huge buffer:", w, "x", h, "with label", label); } - w = Math_floor(w); - h = Math_floor(h); + w = Math.floor(w); + h = Math.floor(h); let canvas = null; let context = null; @@ -103,7 +102,7 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe } const otherPixels = useableCanvas.width * useableCanvas.height; - const diff = Math_abs(otherPixels - currentPixels); + const diff = Math.abs(otherPixels - currentPixels); if (diff < bestMatchingPixelsDiff) { bestMatchingPixelsDiff = diff; bestMatchingOne = { diff --git a/src/js/core/builtins.js b/src/js/core/builtins.js deleted file mode 100644 index 77f40b77..00000000 --- a/src/js/core/builtins.js +++ /dev/null @@ -1,34 +0,0 @@ -// Store the original version of all builtins to prevent modification - -export const JSON_stringify = JSON.stringify.bind(JSON); -export const JSON_parse = JSON.parse.bind(JSON); - -export function Math_radians(degrees) { - return (degrees * Math_PI) / 180.0; -} - -export function Math_degrees(radians) { - return (radians * 180.0) / Math_PI; -} - -export const performanceNow = performance.now.bind(performance); - -export const Math_abs = Math.abs.bind(Math); -export const Math_ceil = Math.ceil.bind(Math); -export const Math_floor = Math.floor.bind(Math); -export const Math_round = Math.round.bind(Math); -export const Math_sign = Math.sign.bind(Math); -export const Math_sqrt = Math.sqrt.bind(Math); -export const Math_min = Math.min.bind(Math); -export const Math_max = Math.max.bind(Math); -export const Math_sin = Math.sin.bind(Math); -export const Math_cos = Math.cos.bind(Math); -export const Math_tan = Math.tan.bind(Math); -export const Math_hypot = Math.hypot.bind(Math); -export const Math_atan2 = Math.atan2.bind(Math); -export const Math_pow = Math.pow.bind(Math); -export const Math_random = Math.random.bind(Math); -export const Math_exp = Math.exp.bind(Math); -export const Math_log10 = Math.log10.bind(Math); - -export const Math_PI = 3.1415926; diff --git a/src/js/core/click_detector.js b/src/js/core/click_detector.js index 508e9375..ea6abf48 100644 --- a/src/js/core/click_detector.js +++ b/src/js/core/click_detector.js @@ -1,4 +1,3 @@ -import { performanceNow } from "../core/builtins"; import { createLogger } from "../core/logging"; import { Signal } from "../core/signal"; import { fastArrayDelete, fastArrayDeleteValueIfContained } from "./utils"; @@ -246,7 +245,7 @@ export class ClickDetector { } if (window.TouchEvent && event instanceof TouchEvent) { - clickDetectorGlobals.lastTouchTime = performanceNow(); + clickDetectorGlobals.lastTouchTime = performance.now(); // console.log("Got touches", event.targetTouches.length, "vs", expectedRemainingTouches); if (event.targetTouches.length !== expectedRemainingTouches) { @@ -255,7 +254,7 @@ export class ClickDetector { } if (event instanceof MouseEvent) { - if (performanceNow() - clickDetectorGlobals.lastTouchTime < 1000.0) { + if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) { return false; } } @@ -340,7 +339,7 @@ export class ClickDetector { // Store where the touch started this.clickDownPosition = position; - this.clickStartTime = performanceNow(); + this.clickStartTime = performance.now(); this.touchstartSimple.dispatch(this.clickDownPosition.x, this.clickDownPosition.y); // If we are not currently within a click, register it diff --git a/src/js/core/config.js b/src/js/core/config.js index 6825e762..96a2b40b 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -38,7 +38,7 @@ export const globalConfig = { // Production analytics statisticsGraphDpi: 2.5, statisticsGraphSlices: 100, - analyticsSliceDurationSeconds: 10, + analyticsSliceDurationSeconds: G_IS_DEV ? 1 : 10, minimumTickRate: 25, maximumTickRate: 500, @@ -51,7 +51,7 @@ export const globalConfig = { // Belt speeds // NOTICE: Update webpack.production.config too! beltSpeedItemsPerSecond: 2, - itemSpacingOnBelts: 0.8, + itemSpacingOnBelts: 0.63, minerSpeedItemsPerSecond: 0, // COMPUTED undergroundBeltMaxTilesByTier: [5, 8], diff --git a/src/js/core/config.local.js b/src/js/core/config.local.js index 2060f495..e45072d1 100644 --- a/src/js/core/config.local.js +++ b/src/js/core/config.local.js @@ -78,10 +78,19 @@ export default { // instantMiners: true, // ----------------------------------------------------------------------------------- // When using fastGameEnter, controls whether a new game is started or the last one is resumed - // resumeGameOnFastEnter: false, + // resumeGameOnFastEnter: true, // ----------------------------------------------------------------------------------- // Special option used to render the trailer // renderForTrailer: true, // ----------------------------------------------------------------------------------- + // Whether to render changes + // renderChanges: true, + // ----------------------------------------------------------------------------------- + // Whether to render belt paths + // renderBeltPaths: true, + // ----------------------------------------------------------------------------------- + // Whether to check belt paths + // checkBeltPaths: true, + // ----------------------------------------------------------------------------------- /* dev:end */ }; diff --git a/src/js/core/dpi_manager.js b/src/js/core/dpi_manager.js index 2045a5e0..0388c5f9 100644 --- a/src/js/core/dpi_manager.js +++ b/src/js/core/dpi_manager.js @@ -1,5 +1,4 @@ import { globalConfig } from "../core/config"; -import { Math_ceil, Math_floor, Math_round } from "./builtins"; import { round1Digit, round2Digits } from "./utils"; /** @@ -23,7 +22,7 @@ export function smoothenDpi(dpi) { } else if (dpi < 1) { return round1Digit(dpi); } else { - return round1Digit(Math_round(dpi / 0.5) * 0.5); + return round1Digit(Math.round(dpi / 0.5) * 0.5); } } @@ -59,11 +58,11 @@ export function prepareHighDPIContext(context, smooth = true) { export function resizeHighDPICanvas(canvas, w, h, smooth = true) { const dpi = getDeviceDPI(); - const wNumber = Math_floor(w); - const hNumber = Math_floor(h); + const wNumber = Math.floor(w); + const hNumber = Math.floor(h); - const targetW = Math_floor(wNumber * dpi); - const targetH = Math_floor(hNumber * dpi); + const targetW = Math.floor(wNumber * dpi); + const targetH = Math.floor(hNumber * dpi); if (targetW !== canvas.width || targetH !== canvas.height) { // console.log("Resize Canvas from", canvas.width, canvas.height, "to", targetW, targetH) @@ -82,8 +81,8 @@ export function resizeHighDPICanvas(canvas, w, h, smooth = true) { * @param {number} h */ export function resizeCanvas(canvas, w, h, setStyle = true) { - const actualW = Math_ceil(w); - const actualH = Math_ceil(h); + const actualW = Math.ceil(w); + const actualH = Math.ceil(h); if (actualW !== canvas.width || actualH !== canvas.height) { canvas.width = actualW; canvas.height = actualH; @@ -103,8 +102,8 @@ export function resizeCanvas(canvas, w, h, setStyle = true) { * @param {number} h */ export function resizeCanvasAndClear(canvas, context, w, h) { - const actualW = Math_ceil(w); - const actualH = Math_ceil(h); + const actualW = Math.ceil(w); + const actualH = Math.ceil(h); if (actualW !== canvas.width || actualH !== canvas.height) { canvas.width = actualW; canvas.height = actualH; diff --git a/src/js/core/draw_utils.js b/src/js/core/draw_utils.js index 8057a211..1b37b929 100644 --- a/src/js/core/draw_utils.js +++ b/src/js/core/draw_utils.js @@ -3,7 +3,6 @@ import { AtlasSprite } from "./sprites"; import { DrawParameters } from "./draw_parameters"; /* typehints:end */ -import { Math_PI, Math_round, Math_atan2, Math_hypot, Math_floor } from "./builtins"; import { Vector } from "./vector"; import { Rectangle } from "./rectangle"; import { createLogger } from "./logging"; @@ -40,7 +39,7 @@ export function initDrawUtils() { return; } this.beginPath(); - this.arc(x, y, r, 0, 2.0 * Math_PI); + this.arc(x, y, r, 0, 2.0 * Math.PI); }; } @@ -68,8 +67,8 @@ export function drawLineFast(context, { x1, x2, y1, y2, color = null, lineSize = const dX = x2 - x1; const dY = y2 - y1; - const angle = Math_atan2(dY, dX) + 0.0 * Math_PI; - const len = Math_hypot(dX, dY); + const angle = Math.atan2(dY, dX) + 0.0 * Math.PI; + const len = Math.hypot(dX, dY); context.translate(x1, y1); context.rotate(angle); @@ -247,7 +246,7 @@ export function hslToRgb(h, s, l) { b = hue2rgb(p, q, h - 1 / 3); } - return [Math_round(r * 255), Math_round(g * 255), Math_round(b * 255)]; + return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; } export function wrapText(context, text, x, y, maxWidth, lineHeight, stroke = false) { @@ -306,7 +305,7 @@ export function rotateTrapezRightFaced(x, y, w, h, leftHeight, angle) { */ export function mapClampedColorValueToHex(value) { const hex = "0123456789abcdef"; - return hex[Math_floor(value / 16)] + hex[value % 16]; + return hex[Math.floor(value / 16)] + hex[value % 16]; } /** diff --git a/src/js/core/logging.js b/src/js/core/logging.js index 20786241..f3e938e5 100644 --- a/src/js/core/logging.js +++ b/src/js/core/logging.js @@ -1,6 +1,4 @@ import { globalConfig } from "../core/config"; -import { Math_floor, performanceNow } from "./builtins"; - const circularJson = require("circular-json"); /* @@ -231,7 +229,7 @@ function logInternal(handle, consoleMethod, args) { const labelColor = handle && handle.LOG_LABEL_COLOR ? handle.LOG_LABEL_COLOR : "#aaa"; if (G_IS_DEV && globalConfig.debug.logTimestamps) { - const timestamp = "⏱ %c" + (Math_floor(performanceNow()) + "").padEnd(6, " ") + ""; + const timestamp = "⏱ %c" + (Math.floor(performance.now()) + "").padEnd(6, " ") + ""; consoleMethod.call( console, timestamp + " %c" + context, diff --git a/src/js/core/modal_dialog_forms.js b/src/js/core/modal_dialog_forms.js index 4d1c9f97..1ded9a8b 100644 --- a/src/js/core/modal_dialog_forms.js +++ b/src/js/core/modal_dialog_forms.js @@ -19,7 +19,7 @@ export class FormElement { abstract; } - focus(parent) {} + focus() {} isValid() { return true; diff --git a/src/js/core/polyfills.js b/src/js/core/polyfills.js index e5efca1d..8f6f8ffe 100644 --- a/src/js/core/polyfills.js +++ b/src/js/core/polyfills.js @@ -1,12 +1,12 @@ function mathPolyfills() { // Converts from degrees to radians. Math.radians = function (degrees) { - return (degrees * Math_PI) / 180.0; + return (degrees * Math.PI) / 180.0; }; // Converts from radians to degrees. Math.degrees = function (radians) { - return (radians * 180.0) / Math_PI; + return (radians * 180.0) / Math.PI; }; } @@ -72,6 +72,7 @@ function objectPolyfills() { } if (!Object.entries) { + // @ts-ignore Object.entries = function entries(O) { return reduce( keys(O), @@ -97,8 +98,6 @@ function initExtensions() { // Fetch polyfill import "whatwg-fetch"; -import { Math_PI } from "./builtins"; - // Other polyfills initPolyfills(); initExtensions(); diff --git a/src/js/core/read_write_proxy.js b/src/js/core/read_write_proxy.js index ee568dc6..5474467a 100644 --- a/src/js/core/read_write_proxy.js +++ b/src/js/core/read_write_proxy.js @@ -7,7 +7,6 @@ import { createLogger } from "./logging"; import { FILE_NOT_FOUND } from "../platform/storage"; import { accessNestedPropertyReverse } from "./utils"; import { IS_DEBUG, globalConfig } from "./config"; -import { JSON_stringify, JSON_parse } from "./builtins"; import { ExplainedResult } from "./explained_result"; import { decompressX64, compressX64 } from ".//lzstring"; import { asyncCompressor, compressionPrefix } from "./async_compression"; @@ -84,7 +83,7 @@ export class ReadWriteProxy { * @param {object} obj */ static serializeObject(obj) { - const jsonString = JSON_stringify(compressObject(obj)); + const jsonString = JSON.stringify(compressObject(obj)); const checksum = sha1(jsonString + salt); return compressionPrefix + compressX64(checksum + jsonString); } @@ -129,7 +128,7 @@ export class ReadWriteProxy { logger.error("Tried to write invalid data to", this.filename, "reason:", verifyResult.reason); return Promise.reject(verifyResult.reason); } - const jsonString = JSON_stringify(compressObject(this.currentData)); + const jsonString = JSON.stringify(compressObject(this.currentData)); // if (!this.app.pageVisible || this.app.unloaded) { // logger.log("Saving file sync because in unload handler"); @@ -189,7 +188,7 @@ export class ReadWriteProxy { .then(rawData => { if (rawData == null) { // So, the file has not been found, use default data - return JSON_stringify(compressObject(this.getDefaultData())); + return JSON.stringify(compressObject(this.getDefaultData())); } if (rawData.startsWith(compressionPrefix)) { @@ -223,7 +222,7 @@ export class ReadWriteProxy { // Parse JSON, this could throw but thats fine .then(res => { try { - return JSON_parse(res); + return JSON.parse(res); } catch (ex) { logger.error( "Failed to parse file content of", diff --git a/src/js/core/rectangle.js b/src/js/core/rectangle.js index 79136b9f..75279e58 100644 --- a/src/js/core/rectangle.js +++ b/src/js/core/rectangle.js @@ -1,5 +1,4 @@ import { globalConfig } from "./config"; -import { Math_ceil, Math_floor, Math_max, Math_min } from "./builtins"; import { clamp, epsilonCompare, round2Digits } from "./utils"; import { Vector } from "./vector"; @@ -38,10 +37,10 @@ export class Rectangle { * @param {Vector} p2 */ static fromTwoPoints(p1, p2) { - const left = Math_min(p1.x, p2.x); - const top = Math_min(p1.y, p2.y); - const right = Math_max(p1.x, p2.x); - const bottom = Math_max(p1.y, p2.y); + const left = Math.min(p1.x, p2.x); + const top = Math.min(p1.y, p2.y); + const right = Math.max(p1.x, p2.x); + const bottom = Math.max(p1.y, p2.y); return new Rectangle(left, top, right - left, bottom - top); } @@ -67,10 +66,10 @@ export class Rectangle { let maxY = -1e10; for (let i = 0; i < points.length; ++i) { const rotated = points[i].rotated(angle); - minX = Math_min(minX, rotated.x); - minY = Math_min(minY, rotated.y); - maxX = Math_max(maxX, rotated.x); - maxY = Math_max(maxY, rotated.y); + minX = Math.min(minX, rotated.x); + minY = Math.min(minY, rotated.y); + maxX = Math.max(maxX, rotated.x); + maxY = Math.max(maxY, rotated.y); } return new Rectangle(minX, minY, maxX - minX, maxY - minY); } @@ -98,10 +97,10 @@ export class Rectangle { this.w = halfWidth * 2; this.h = halfHeight * 2; } else { - this.setLeft(Math_min(this.x, centerX - halfWidth)); - this.setRight(Math_max(this.right(), centerX + halfWidth)); - this.setTop(Math_min(this.y, centerY - halfHeight)); - this.setBottom(Math_max(this.bottom(), centerY + halfHeight)); + this.setLeft(Math.min(this.x, centerX - halfWidth)); + this.setRight(Math.max(this.right(), centerX + halfWidth)); + this.setTop(Math.min(this.y, centerY - halfHeight)); + this.setBottom(Math.max(this.bottom(), centerY + halfHeight)); } } @@ -326,11 +325,11 @@ export class Rectangle { * @returns {Rectangle|null} */ getIntersection(rect) { - const left = Math_max(this.x, rect.x); - const top = Math_max(this.y, rect.y); + const left = Math.max(this.x, rect.x); + const top = Math.max(this.y, rect.y); - const right = Math_min(this.x + this.w, rect.x + rect.w); - const bottom = Math_min(this.y + this.h, rect.y + rect.h); + const right = Math.min(this.x + this.w, rect.x + rect.w); + const bottom = Math.min(this.y + this.h, rect.y + rect.h); if (right <= left || bottom <= top) { return null; @@ -354,10 +353,10 @@ export class Rectangle { } // Find contained area - const left = Math_min(this.x, rect.x); - const top = Math_min(this.y, rect.y); - const right = Math_max(this.right(), rect.right()); - const bottom = Math_max(this.bottom(), rect.bottom()); + const left = Math.min(this.x, rect.x); + const top = Math.min(this.y, rect.y); + const right = Math.max(this.right(), rect.right()); + const bottom = Math.max(this.bottom(), rect.bottom()); return Rectangle.fromTRBL(top, right, bottom, left); } @@ -388,10 +387,10 @@ export class Rectangle { if (includeHalfTiles) { // Increase rectangle size scaled = Rectangle.fromTRBL( - Math_floor(scaled.y), - Math_ceil(scaled.right()), - Math_ceil(scaled.bottom()), - Math_floor(scaled.x) + Math.floor(scaled.y), + Math.ceil(scaled.right()), + Math.ceil(scaled.bottom()), + Math.floor(scaled.x) ); } diff --git a/src/js/core/rng.js b/src/js/core/rng.js index 3322aafa..9c5c1c43 100644 --- a/src/js/core/rng.js +++ b/src/js/core/rng.js @@ -1,5 +1,3 @@ -import { Math_random } from "./builtins"; - // ALEA RNG function Mash() { @@ -72,7 +70,7 @@ export class RandomNumberGenerator { * @param {number|string=} seed */ constructor(seed) { - this.internalRng = makeNewRng(seed || Math_random()); + this.internalRng = makeNewRng(seed || Math.random()); } /** @@ -80,7 +78,7 @@ export class RandomNumberGenerator { * @param {number|string} seed */ reseed(seed) { - this.internalRng = makeNewRng(seed || Math_random()); + this.internalRng = makeNewRng(seed || Math.random()); } /** diff --git a/src/js/core/sprites.js b/src/js/core/sprites.js index 4251bee1..8c4c02ea 100644 --- a/src/js/core/sprites.js +++ b/src/js/core/sprites.js @@ -1,5 +1,4 @@ import { DrawParameters } from "./draw_parameters"; -import { Math_floor } from "./builtins"; import { Rectangle } from "./rectangle"; import { epsilonCompare, round3Digits } from "./utils"; @@ -144,9 +143,9 @@ export class AtlasSprite extends BaseSprite { */ drawCached(parameters, x, y, w = null, h = null, clipping = true) { if (G_IS_DEV) { - assertAlways(parameters instanceof DrawParameters, "Not a valid context"); - assertAlways(!!w && w > 0, "Not a valid width:" + w); - assertAlways(!!h && h > 0, "Not a valid height:" + h); + assert(parameters instanceof DrawParameters, "Not a valid context"); + assert(!!w && w > 0, "Not a valid width:" + w); + assert(!!h && h > 0, "Not a valid height:" + h); } const visibleRect = parameters.visibleRect; @@ -195,20 +194,20 @@ export class AtlasSprite extends BaseSprite { link.atlas, // atlas src pos - Math_floor(srcX), - Math_floor(srcY), + Math.floor(srcX), + Math.floor(srcY), // atlas src size - Math_floor(srcW), - Math_floor(srcH), + Math.floor(srcW), + Math.floor(srcH), // dest pos - Math_floor(destX), - Math_floor(destY), + Math.floor(destX), + Math.floor(destY), // dest size - Math_floor(destW), - Math_floor(destH) + Math.floor(destW), + Math.floor(destH) ); } else { parameters.context.drawImage( diff --git a/src/js/core/utils.js b/src/js/core/utils.js index 23368317..75bc2902 100644 --- a/src/js/core/utils.js +++ b/src/js/core/utils.js @@ -1,19 +1,4 @@ import { globalConfig, IS_DEBUG } from "./config"; -import { - Math_abs, - Math_atan2, - Math_ceil, - Math_floor, - Math_log10, - Math_max, - Math_min, - Math_PI, - Math_pow, - Math_random, - Math_round, - Math_sin, - performanceNow, -} from "./builtins"; import { Vector } from "./vector"; import { T } from "../translations"; @@ -212,7 +197,7 @@ export function newEmptyMap() { * @param {number} end */ export function randomInt(start, end) { - return start + Math_round(Math_random() * (end - start)); + return start + Math.round(Math.random() * (end - start)); } /** @@ -233,7 +218,7 @@ export function accessNestedPropertyReverse(obj, keys) { * @param {Array | string} arr */ export function randomChoice(arr) { - return arr[Math_floor(Math_random() * arr.length)]; + return arr[Math.floor(Math.random() * arr.length)]; } /** @@ -327,12 +312,12 @@ export function arrayDeleteValue(array, value) { * @returns {number} in range [0, 7] */ export function angleToSpriteIndex(offset, inverse = false) { - const twoPi = 2.0 * Math_PI; + 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 angle = (factor * Math.atan2(offset.y, offset.x) + offs * Math.PI) % twoPi; - const index = Math_round((angle / twoPi) * 8) % 8; + const index = Math.round((angle / twoPi) * 8) % 8; return index; } @@ -343,7 +328,7 @@ export function angleToSpriteIndex(offset, inverse = false) { * @returns {boolean} */ export function epsilonCompare(a, b, epsilon = 1e-5) { - return Math_abs(a - b) < epsilon; + return Math.abs(a - b) < epsilon; } /** @@ -394,26 +379,24 @@ export function findNiceValue(num) { roundAmount = 5; } - const niceValue = Math_floor(num / roundAmount) * roundAmount; + const niceValue = Math.floor(num / roundAmount) * roundAmount; if (num >= 10) { - return Math_round(niceValue); + return Math.round(niceValue); } if (num >= 1) { - return Math_round(niceValue * 10) / 10; + return Math.round(niceValue * 10) / 10; } - return Math_round(niceValue * 100) / 100; + return Math.round(niceValue * 100) / 100; } -window.fn = findNiceValue; - /** * Finds a nice integer value * @see findNiceValue * @param {number} num */ export function findNiceIntegerValue(num) { - return Math_ceil(findNiceValue(num)); + return Math.ceil(findNiceValue(num)); } /** @@ -424,7 +407,7 @@ function roundSmart(n) { if (n < 100) { return n.toFixed(1); } - return Math_round(n); + return Math.round(n); } /** @@ -435,7 +418,7 @@ function roundSmart(n) { */ export function formatBigNumber(num, divider = ".") { const sign = num < 0 ? "-" : ""; - num = Math_abs(num); + num = Math.abs(num); if (num > 1e54) { return sign + T.global.infinite; @@ -447,7 +430,7 @@ export function formatBigNumber(num, divider = ".") { if (num < 50 && !Number.isInteger(num)) { return sign + num.toFixed(1); } - num = Math_floor(num); + num = Math.floor(num); if (num < 1000) { return sign + "" + num; @@ -484,7 +467,7 @@ export function formatBigNumberFull(num, divider = T.global.thousandsDivider) { let out = ""; while (rest >= 1000) { out = (rest % 1000).toString().padStart(3, "0") + divider + out; - rest = Math_floor(rest / 1000); + rest = Math.floor(rest / 1000); } out = rest + divider + out; @@ -502,11 +485,11 @@ export function artificialDelayedPromise(promise, minTimeMs = 500) { return promise; } - const startTime = performanceNow(); + const startTime = performance.now(); return promise.then( result => { - const timeTaken = performanceNow() - startTime; - const waitTime = Math_floor(minTimeMs - timeTaken); + const timeTaken = performance.now() - startTime; + const waitTime = Math.floor(minTimeMs - timeTaken); if (waitTime > 0) { return new Promise(resolve => { setTimeout(() => { @@ -518,8 +501,8 @@ export function artificialDelayedPromise(promise, minTimeMs = 500) { } }, error => { - const timeTaken = performanceNow() - startTime; - const waitTime = Math_floor(minTimeMs - timeTaken); + const timeTaken = performance.now() - startTime; + const waitTime = Math.floor(minTimeMs - timeTaken); if (waitTime > 0) { // @ts-ignore return new Promise((resolve, reject) => { @@ -541,7 +524,7 @@ export function artificialDelayedPromise(promise, minTimeMs = 500) { * @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; + return Math.sin((time * Math.PI * 2.0) / duration + seed * 5642.86729349) * 0.5 + 0.5; } /** @@ -551,7 +534,7 @@ export function pulseAnimation(time, duration = 1.0, seed = 0.0) { * @returns {number} 0 .. 2 PI */ export function smallestAngle(a, b) { - return safeMod(a - b + Math_PI, 2.0 * Math_PI) - Math_PI; + return safeMod(a - b + Math.PI, 2.0 * Math.PI) - Math.PI; } /** @@ -568,7 +551,7 @@ export function safeMod(n, m) { * @param {number} angle */ export function wrapAngle(angle) { - return safeMod(angle, 2.0 * Math_PI); + return safeMod(angle, 2.0 * Math.PI); } /** @@ -591,7 +574,7 @@ export function waitNextFrame() { * @returns {number} */ export function round1Digit(n) { - return Math_floor(n * 10.0) / 10.0; + return Math.floor(n * 10.0) / 10.0; } /** @@ -600,7 +583,7 @@ export function round1Digit(n) { * @returns {number} */ export function round2Digits(n) { - return Math_floor(n * 100.0) / 100.0; + return Math.floor(n * 100.0) / 100.0; } /** @@ -609,7 +592,7 @@ export function round2Digits(n) { * @returns {number} */ export function round3Digits(n) { - return Math_floor(n * 1000.0) / 1000.0; + return Math.floor(n * 1000.0) / 1000.0; } /** @@ -618,7 +601,7 @@ export function round3Digits(n) { * @returns {number} */ export function round4Digits(n) { - return Math_floor(n * 10000.0) / 10000.0; + return Math.floor(n * 10000.0) / 10000.0; } /** @@ -628,7 +611,7 @@ export function round4Digits(n) { * @param {number=} maximum Default 1 */ export function clamp(v, minimum = 0, maximum = 1) { - return Math_max(minimum, Math_min(maximum, v)); + return Math.max(minimum, Math.min(maximum, v)); } /** @@ -637,11 +620,11 @@ export function clamp(v, minimum = 0, maximum = 1) { * @param {function():void} target */ export function measure(name, target) { - const now = performanceNow(); + const now = performance.now(); for (let i = 0; i < 25; ++i) { target(); } - const dur = (performanceNow() - now) / 25.0; + const dur = (performance.now() - now) / 25.0; console.warn("->", name, "took", dur.toFixed(2), "ms"); } @@ -891,10 +874,10 @@ export function fastRotateMultipleOf90(x, y, deg) { * @returns {string} */ export function formatSecondsToTimeAgo(secs) { - const seconds = Math_floor(secs); - const minutes = Math_floor(seconds / 60); - const hours = Math_floor(minutes / 60); - const days = Math_floor(hours / 24); + const seconds = Math.floor(secs); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); if (seconds <= 60) { if (seconds <= 1) { @@ -926,18 +909,18 @@ export function formatSecondsToTimeAgo(secs) { */ export function formatSeconds(secs) { const trans = T.global.time; - secs = Math_ceil(secs); + secs = Math.ceil(secs); if (secs < 60) { return trans.secondsShort.replace("", "" + secs); } else if (secs < 60 * 60) { - const minutes = Math_floor(secs / 60); + const minutes = Math.floor(secs / 60); const seconds = secs % 60; return trans.minutesAndSecondsShort .replace("", "" + seconds) .replace("", "" + minutes); } else { - const hours = Math_floor(secs / 3600); - const minutes = Math_floor(secs / 60) % 60; + const hours = Math.floor(secs / 3600); + const minutes = Math.floor(secs / 60) % 60; return trans.hoursAndMinutesShort.replace("", "" + minutes).replace("", "" + hours); } } diff --git a/src/js/core/vector.js b/src/js/core/vector.js index 635556d6..b0a2d3fa 100644 --- a/src/js/core/vector.js +++ b/src/js/core/vector.js @@ -1,17 +1,4 @@ import { globalConfig } from "./config"; -import { - Math_abs, - Math_floor, - Math_PI, - Math_max, - Math_min, - Math_round, - Math_hypot, - Math_atan2, - Math_sin, - Math_cos, - Math_ceil, -} from "./builtins"; const tileSize = globalConfig.tileSize; const halfTileSize = globalConfig.halfTileSize; @@ -158,7 +145,7 @@ export class Vector { * @returns {number} */ length() { - return Math_hypot(this.x, this.y); + return Math.hypot(this.x, this.y); } /** @@ -226,7 +213,7 @@ export class Vector { * @returns {Vector} */ maxScalar(f) { - return new Vector(Math_max(f, this.x), Math_max(f, this.y)); + return new Vector(Math.max(f, this.x), Math.max(f, this.y)); } /** @@ -244,7 +231,7 @@ export class Vector { * @returns {Vector} */ min(v) { - return new Vector(Math_min(v.x, this.x), Math_min(v.y, this.y)); + return new Vector(Math.min(v.x, this.x), Math.min(v.y, this.y)); } /** @@ -253,14 +240,14 @@ export class Vector { * @returns {Vector} */ max(v) { - return new Vector(Math_max(v.x, this.x), Math_max(v.y, this.y)); + return new Vector(Math.max(v.x, this.x), Math.max(v.y, this.y)); } /** * Computes the component wise absolute * @returns {Vector} */ abs() { - return new Vector(Math_abs(this.x), Math_abs(this.y)); + return new Vector(Math.abs(this.x), Math.abs(this.y)); } /** @@ -278,7 +265,7 @@ export class Vector { * @returns {number} */ distance(v) { - return Math_hypot(this.x - v.x, this.y - v.y); + return Math.hypot(this.x - v.x, this.y - v.y); } /** @@ -308,7 +295,7 @@ export class Vector { * @returns {Vector} */ floor() { - return new Vector(Math_floor(this.x), Math_floor(this.y)); + return new Vector(Math.floor(this.x), Math.floor(this.y)); } /** @@ -316,7 +303,7 @@ export class Vector { * @returns {Vector} */ ceil() { - return new Vector(Math_ceil(this.x), Math_ceil(this.y)); + return new Vector(Math.ceil(this.x), Math.ceil(this.y)); } /** @@ -324,7 +311,7 @@ export class Vector { * @returns {Vector} */ round() { - return new Vector(Math_round(this.x), Math_round(this.y)); + return new Vector(Math.round(this.x), Math.round(this.y)); } /** @@ -332,7 +319,7 @@ export class Vector { * @returns {Vector} */ toTileSpace() { - return new Vector(Math_floor(this.x / tileSize), Math_floor(this.y / tileSize)); + return new Vector(Math.floor(this.x / tileSize), Math.floor(this.y / tileSize)); } /** @@ -340,7 +327,7 @@ export class Vector { * @returns {Vector} */ toStreetSpace() { - return new Vector(Math_floor(this.x / halfTileSize + 0.25), Math_floor(this.y / halfTileSize + 0.25)); + return new Vector(Math.floor(this.x / halfTileSize + 0.25), Math.floor(this.y / halfTileSize + 0.25)); } /** @@ -364,7 +351,7 @@ export class Vector { * @returns {Vector} */ snapWorldToTile() { - return new Vector(Math_floor(this.x / tileSize) * tileSize, Math_floor(this.y / tileSize) * tileSize); + return new Vector(Math.floor(this.x / tileSize) * tileSize, Math.floor(this.y / tileSize) * tileSize); } /** @@ -372,7 +359,7 @@ export class Vector { * @returns {Vector} */ normalize() { - const len = Math_max(1e-5, Math_hypot(this.x, this.y)); + const len = Math.max(1e-5, Math.hypot(this.x, this.y)); return new Vector(this.x / len, this.y / len); } @@ -381,7 +368,7 @@ export class Vector { * @returns {Vector} */ normalizeIfGreaterOne() { - const len = Math_max(1, Math_hypot(this.x, this.y)); + const len = Math.max(1, Math.hypot(this.x, this.y)); return new Vector(this.x / len, this.y / len); } @@ -393,7 +380,7 @@ export class Vector { normalizedDirection(v) { const dx = v.x - this.x; const dy = v.y - this.y; - const len = Math_max(1e-5, Math_hypot(dx, dy)); + const len = Math.max(1e-5, Math.hypot(dx, dy)); return new Vector(dx / len, dy / len); } @@ -437,8 +424,8 @@ export class Vector { * @returns {Vector} new vector */ rotated(angle) { - const sin = Math_sin(angle); - const cos = Math_cos(angle); + const sin = Math.sin(angle); + const cos = Math.cos(angle); return new Vector(this.x * cos - this.y * sin, this.x * sin + this.y * cos); } @@ -448,8 +435,8 @@ export class Vector { * @returns {Vector} this vector */ rotateInplaceFastMultipleOf90(angle) { - // const sin = Math_sin(angle); - // const cos = Math_cos(angle); + // const sin = Math.sin(angle); + // const cos = Math.cos(angle); // let sin = 0, cos = 1; assert(angle >= 0 && angle <= 360, "Invalid angle, please clamp first: " + angle); @@ -598,7 +585,7 @@ export class Vector { * @returns {Boolean} */ equalsEpsilon(v, epsilon = 1e-5) { - return Math_abs(this.x - v.x) < 1e-5 && Math_abs(this.y - v.y) < epsilon; + return Math.abs(this.x - v.x) < 1e-5 && Math.abs(this.y - v.y) < epsilon; } /** @@ -606,7 +593,7 @@ export class Vector { * @returns {number} 0 .. 2 PI */ angle() { - return Math_atan2(this.y, this.x) + Math_PI / 2; + return Math.atan2(this.y, this.x) + Math.PI / 2; } /** @@ -638,7 +625,7 @@ export class Vector { */ static deserializeTileFromInt(i) { const x = i % 256; - const y = Math_floor(i / 256); + const y = Math.floor(i / 256); return new Vector(x, y); } diff --git a/src/js/game/automatic_save.js b/src/js/game/automatic_save.js index 1b3f13ca..e32b9b62 100644 --- a/src/js/game/automatic_save.js +++ b/src/js/game/automatic_save.js @@ -1,6 +1,5 @@ import { GameRoot } from "./root"; import { globalConfig, IS_DEBUG } from "../core/config"; -import { Math_max } from "../core/builtins"; import { createLogger } from "../core/logging"; // How important it is that a savegame is created @@ -29,7 +28,7 @@ export class AutomaticSave { } setSaveImportance(importance) { - this.saveImportance = Math_max(this.saveImportance, importance); + this.saveImportance = Math.max(this.saveImportance, importance); } doSave() { @@ -54,7 +53,7 @@ export class AutomaticSave { } // Check when the last save was, but make sure that if it fails, we don't spam - const lastSaveTime = Math_max(this.lastSaveAttempt, this.root.savegame.getRealLastUpdate()); + const lastSaveTime = Math.max(this.lastSaveAttempt, this.root.savegame.getRealLastUpdate()); const secondsSinceLastSave = (Date.now() - lastSaveTime) / 1000.0; let shouldSave = false; diff --git a/src/js/game/base_item.js b/src/js/game/base_item.js index e70263df..7f01d6ce 100644 --- a/src/js/game/base_item.js +++ b/src/js/game/base_item.js @@ -1,6 +1,11 @@ import { DrawParameters } from "../core/draw_parameters"; -import { BasicSerializableObject, types } from "../savegame/serialization"; -import { THEME } from "./theme"; +import { BasicSerializableObject } from "../savegame/serialization"; + +/** @enum {string} */ +export const enumItemType = { + shape: "shape", + color: "color", +}; /** * Class for items on belts etc. Not an entity for performance reasons @@ -19,6 +24,12 @@ export class BaseItem extends BasicSerializableObject { return {}; } + /** @returns {enumItemType} */ + getItemType() { + abstract; + return ""; + } + /** * Draws the item at the given position * @param {number} x diff --git a/src/js/game/belt_path.js b/src/js/game/belt_path.js new file mode 100644 index 00000000..e6d25185 --- /dev/null +++ b/src/js/game/belt_path.js @@ -0,0 +1,1109 @@ +import { globalConfig } from "../core/config"; +import { DrawParameters } from "../core/draw_parameters"; +import { createLogger } from "../core/logging"; +import { epsilonCompare, round4Digits } from "../core/utils"; +import { Vector } from "../core/vector"; +import { BaseItem } from "./base_item"; +import { Entity } from "./entity"; +import { GameRoot } from "./root"; +import { Rectangle } from "../core/rectangle"; +import { BasicSerializableObject, types } from "../savegame/serialization"; +import { gItemRegistry } from "../core/global_registries"; + +const logger = createLogger("belt_path"); + +// Helpers for more semantic access into interleaved arrays +const _nextDistance = 0; +const _item = 1; + +const DEBUG = G_IS_DEV && false; + +/** + * Stores a path of belts, used for optimizing performance + */ +export class BeltPath extends BasicSerializableObject { + static getId() { + return "BeltPath"; + } + + static getSchema() { + return { + entityPath: types.array(types.entity), + items: types.array(types.pair(types.ufloat, types.obj(gItemRegistry))), + spacingToFirstItem: types.ufloat, + }; + } + + /** + * Creates a path from a serialized object + * @param {GameRoot} root + * @param {Object} data + * @returns {BeltPath|string} + */ + static fromSerialized(root, data) { + // Create fake object which looks like a belt path but skips the constructor + const fakeObject = /** @type {BeltPath} */ (Object.create(BeltPath.prototype)); + fakeObject.root = root; + + // Deserialize the data + const errorCodeDeserialize = fakeObject.deserialize(data); + if (errorCodeDeserialize) { + return errorCodeDeserialize; + } + + // Compute other properties + fakeObject.init(false); + + return fakeObject; + } + + /** + * @param {GameRoot} root + * @param {Array} entityPath + */ + constructor(root, entityPath) { + super(); + this.root = root; + + assert(entityPath.length > 0, "invalid entity path"); + this.entityPath = entityPath; + + /** + * Stores the items sorted, and their distance to the previous item (or start) + * Layout: [distanceToNext, item] + * @type {Array<[number, BaseItem]>} + */ + this.items = []; + + /** + * Stores the spacing to the first item + */ + + this.init(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("constructor"); + } + } + /** + * Initializes the path by computing the properties which are not saved + * @param {boolean} computeSpacing Whether to also compute the spacing + */ + init(computeSpacing = true) { + // Find acceptor and ejector + + this.totalLength = this.computeTotalLength(); + + if (computeSpacing) { + this.spacingToFirstItem = this.totalLength; + } + + /** + * Current bounds of this path + * @type {Rectangle} + */ + this.worldBounds = this.computeBounds(); + + // Connect the belts + for (let i = 0; i < this.entityPath.length; ++i) { + this.entityPath[i].components.Belt.assignedPath = this; + } + + this.onPathChanged(); + } + + /** + * Returns whether this path can accept a new item + * @returns {boolean} + */ + canAcceptItem() { + return this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts; + } + + /** + * Tries to accept the item + * @param {BaseItem} item + */ + tryAcceptItem(item) { + if (this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts) { + // So, since we already need one tick to accept this item we will add this directly. + const beltProgressPerTick = + this.root.hubGoals.getBeltBaseSpeed() * + this.root.dynamicTickrate.deltaSeconds * + globalConfig.itemSpacingOnBelts; + + // First, compute how much progress we can make *at max* + const maxProgress = Math.max(0, this.spacingToFirstItem - globalConfig.itemSpacingOnBelts); + const initialProgress = Math.min(maxProgress, beltProgressPerTick); + + this.items.unshift([this.spacingToFirstItem - initialProgress, item]); + this.spacingToFirstItem = initialProgress; + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("accept-item"); + } + + return true; + } + return false; + } + + /** + * SLOW / Tries to find the item closest to the given tile + * @param {Vector} tile + * @returns {BaseItem|null} + */ + findItemAtTile(tile) { + // TODO: This breaks color blind mode otherwise + return null; + } + + /** + * Computes the tile bounds of the path + * @returns {Rectangle} + */ + computeBounds() { + let bounds = this.entityPath[0].components.StaticMapEntity.getTileSpaceBounds(); + for (let i = 1; i < this.entityPath.length; ++i) { + const staticComp = this.entityPath[i].components.StaticMapEntity; + const otherBounds = staticComp.getTileSpaceBounds(); + bounds = bounds.getUnion(otherBounds); + } + return bounds.allScaled(globalConfig.tileSize); + } + + /** + * Updates all ejectors on the path, so that only the last ejector + */ + onPathChanged() { + this.ejectorComp = this.entityPath[this.entityPath.length - 1].components.ItemEjector; + this.ejectorSlot = this.ejectorComp.slots[0]; + + for (let i = 0; i < this.entityPath.length; ++i) { + const ejectorComp = this.entityPath[i].components.ItemEjector; + const isLast = i === this.entityPath.length - 1; + ejectorComp.enabled = isLast; + + // Clear all slots of non-end entities + if (!isLast) { + for (let k = 0; k < ejectorComp.slots.length; ++k) { + ejectorComp.slots[k].item = null; + ejectorComp.slots[k].progress = 0.0; + } + } + } + } + + // Following code will be compiled out outside of dev versions + /* dev:start */ + + /** + * Helper to throw an error on mismatch + * @param {string} change + * @param {Array} reason + */ + debug_failIntegrity(change, ...reason) { + throw new Error("belt path invalid (" + change + "): " + reason.map(i => "" + i).join(" ")); + } + + /** + * Checks if this path is valid + */ + debug_checkIntegrity(currentChange = "change") { + const fail = (...args) => this.debug_failIntegrity(currentChange, ...args); + + // Check for empty path + if (this.entityPath.length === 0) { + return fail("Belt path is empty"); + } + + // Check for mismatching length + const totalLength = this.computeTotalLength(); + if (!epsilonCompare(this.totalLength, totalLength, 0.01)) { + return this.debug_failIntegrity( + currentChange, + "Total length mismatch, stored =", + this.totalLength, + "but correct is", + totalLength + ); + } + + // Check for misconnected entities + for (let i = 0; i < this.entityPath.length - 1; ++i) { + const entity = this.entityPath[i]; + if (entity.destroyed) { + return fail("Reference to destroyed entity " + entity.uid); + } + + const enabledState = i === this.entityPath.length - 1; + if (entity.components.ItemEjector.enabled !== enabledState) { + return fail("Item ejector enabled state is not synchronized (index =" + i + ")"); + } + + const followUp = this.root.systemMgr.systems.belt.findFollowUpEntity(entity); + if (!followUp) { + return fail( + "Follow up entity for the", + i, + "-th entity (total length", + this.entityPath.length, + ") was null!" + ); + } + if (followUp !== this.entityPath[i + 1]) { + return fail( + "Follow up entity mismatch, stored is", + this.entityPath[i + 1].uid, + "but real one is", + followUp.uid + ); + } + if (entity.components.Belt.assignedPath !== this) { + return fail( + "Entity with uid", + entity.uid, + "doesn't have this path assigned, but this path contains the entity." + ); + } + } + + // Check for right ejector component and slot + if (this.ejectorComp !== this.entityPath[this.entityPath.length - 1].components.ItemEjector) { + return fail("Stale ejectorComp handle"); + } + if (this.ejectorSlot !== this.ejectorComp.slots[0]) { + return fail("Stale ejector slot handle"); + } + if (!this.ejectorComp) { + return fail("Ejector comp not set"); + } + if (!this.ejectorSlot) { + return fail("Ejector slot not set"); + } + + // Check spacing + if (this.spacingToFirstItem > this.totalLength + 0.005) { + return fail( + currentChange, + "spacing to first item (", + this.spacingToFirstItem, + ") is greater than total length (", + this.totalLength, + ")" + ); + } + + // Check distance if empty + if (this.items.length === 0 && !epsilonCompare(this.spacingToFirstItem, this.totalLength, 0.01)) { + return fail( + currentChange, + "Path is empty but spacing to first item (", + this.spacingToFirstItem, + ") does not equal total length (", + this.totalLength, + ")" + ); + } + + // Check items etc + let currentPos = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + + if (item[_nextDistance] < 0 || item[_nextDistance] > this.totalLength + 0.02) { + return fail( + "Item has invalid offset to next item: ", + item[_nextDistance], + "(total length:", + this.totalLength, + ")" + ); + } + + currentPos += item[_nextDistance]; + } + + // Check the total sum matches + if (!epsilonCompare(currentPos, this.totalLength, 0.01)) { + return fail( + "total sum (", + currentPos, + ") of first item spacing (", + this.spacingToFirstItem, + ") and items does not match total length (", + this.totalLength, + ") -> items: " + this.items.map(i => i[_nextDistance]).join("|") + ); + } + + // Check bounds + const actualBounds = this.computeBounds(); + if (!actualBounds.equalsEpsilon(this.worldBounds, 0.01)) { + return fail("Bounds are stale"); + } + } + + /* dev:end */ + + /** + * Extends the belt path by the given belt + * @param {Entity} entity + */ + extendOnEnd(entity) { + DEBUG && logger.log("Extending belt path by entity at", entity.components.StaticMapEntity.origin); + + const beltComp = entity.components.Belt; + + // If the last belt has something on its ejector, put that into the path first + const pendingItem = this.ejectorComp.takeSlotItem(0); + if (pendingItem) { + // Ok, so we have a pending item + DEBUG && logger.log("Taking pending item and putting it back on the path"); + this.items.push([0, pendingItem]); + } + + // Append the entity + this.entityPath.push(entity); + this.onPathChanged(); + + // Extend the path length + const additionalLength = beltComp.getEffectiveLengthTiles(); + this.totalLength += additionalLength; + DEBUG && logger.log(" Extended total length by", additionalLength, "to", this.totalLength); + + // If we have no item, just update the distance to the first item + if (this.items.length === 0) { + this.spacingToFirstItem = this.totalLength; + DEBUG && logger.log(" Extended spacing to first to", this.totalLength, "(= total length)"); + } else { + // Otherwise, update the next-distance of the last item + const lastItem = this.items[this.items.length - 1]; + DEBUG && + logger.log( + " Extended spacing of last item from", + lastItem[_nextDistance], + "to", + lastItem[_nextDistance] + additionalLength + ); + lastItem[_nextDistance] += additionalLength; + } + + // Assign reference + beltComp.assignedPath = this; + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("extend-on-end"); + } + } + + /** + * Extends the path with the given entity on the beginning + * @param {Entity} entity + */ + extendOnBeginning(entity) { + const beltComp = entity.components.Belt; + + DEBUG && logger.log("Extending the path on the beginning"); + + // All items on that belt are simply lost (for now) + + const length = beltComp.getEffectiveLengthTiles(); + + // Extend the length of this path + this.totalLength += length; + + // Simply adjust the first item spacing cuz we have no items contained + this.spacingToFirstItem += length; + + // Set handles and append entity + beltComp.assignedPath = this; + this.entityPath.unshift(entity); + this.onPathChanged(); + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("extend-on-begin"); + } + } + + /** + * Returns if the given entity is the end entity of the path + * @param {Entity} entity + * @returns {boolean} + */ + isEndEntity(entity) { + return this.entityPath[this.entityPath.length - 1] === entity; + } + + /** + * Returns if the given entity is the start entity of the path + * @param {Entity} entity + * @returns {boolean} + */ + isStartEntity(entity) { + return this.entityPath[0] === entity; + } + + /** + * Splits this path at the given entity by removing it, and + * returning the new secondary paht + * @param {Entity} entity + * @returns {BeltPath} + */ + deleteEntityOnPathSplitIntoTwo(entity) { + DEBUG && logger.log("Splitting path at entity", entity.components.StaticMapEntity.origin); + + // First, find where the current path ends + const beltComp = entity.components.Belt; + beltComp.assignedPath = null; + + const entityLength = beltComp.getEffectiveLengthTiles(); + assert(this.entityPath.indexOf(entity) >= 0, "Entity not contained for split"); + assert(this.entityPath.indexOf(entity) !== 0, "Entity is first"); + assert(this.entityPath.indexOf(entity) !== this.entityPath.length - 1, "Entity is last"); + + let firstPathEntityCount = 0; + let firstPathLength = 0; + let firstPathEndEntity = null; + + for (let i = 0; i < this.entityPath.length; ++i) { + const otherEntity = this.entityPath[i]; + if (otherEntity === entity) { + DEBUG && logger.log("Found entity at", i, "of length", firstPathLength); + break; + } + + ++firstPathEntityCount; + firstPathEndEntity = otherEntity; + firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles(); + } + + DEBUG && + logger.log( + "First path ends at", + firstPathLength, + "and entity", + firstPathEndEntity.components.StaticMapEntity.origin, + "and has", + firstPathEntityCount, + "entities" + ); + + // Compute length of second path + const secondPathLength = this.totalLength - firstPathLength - entityLength; + const secondPathStart = firstPathLength + entityLength; + const secondEntities = this.entityPath.splice(firstPathEntityCount + 1); + DEBUG && + logger.log( + "Second path starts at", + secondPathStart, + "and has a length of ", + secondPathLength, + "with", + secondEntities.length, + "entities" + ); + + // Remove the last item + this.entityPath.pop(); + + DEBUG && logger.log("Splitting", this.items.length, "items"); + DEBUG && + logger.log( + "Old items are", + this.items.map(i => i[_nextDistance]) + ); + + // Create second path + const secondPath = new BeltPath(this.root, secondEntities); + + // Remove all items which are no longer relevant and transfer them to the second path + let itemPos = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + const distanceToNext = item[_nextDistance]; + + DEBUG && logger.log(" Checking item at", itemPos, "with distance of", distanceToNext, "to next"); + + // Check if this item is past the first path + if (itemPos >= firstPathLength) { + // Remove it from the first path + this.items.splice(i, 1); + i -= 1; + DEBUG && + logger.log(" Removed item from first path since its no longer contained @", itemPos); + + // Check if its on the second path (otherwise its on the removed belt and simply lost) + if (itemPos >= secondPathStart) { + // Put item on second path + secondPath.items.push([distanceToNext, item[_item]]); + DEBUG && + logger.log( + " Put item to second path @", + itemPos, + "with distance to next =", + distanceToNext + ); + + // If it was the first item, adjust the distance to the first item + if (secondPath.items.length === 1) { + DEBUG && logger.log(" Sinc it was the first, set sapcing of first to", itemPos); + secondPath.spacingToFirstItem = itemPos - secondPathStart; + } + } else { + DEBUG && logger.log(" Item was on the removed belt, so its gone - forever!"); + } + } else { + // Seems this item is on the first path (so all good), so just make sure it doesn't + // have a nextDistance which is bigger than the total path length + const clampedDistanceToNext = Math.min(itemPos + distanceToNext, firstPathLength) - itemPos; + if (clampedDistanceToNext < distanceToNext) { + DEBUG && + logger.log( + "Correcting next distance (first path) from", + distanceToNext, + "to", + clampedDistanceToNext + ); + item[_nextDistance] = clampedDistanceToNext; + } + } + + // Advance items + itemPos += distanceToNext; + } + + DEBUG && + logger.log( + "New items are", + this.items.map(i => i[_nextDistance]) + ); + + DEBUG && + logger.log( + "And second path items are", + secondPath.items.map(i => i[_nextDistance]) + ); + + // Adjust our total length + this.totalLength = firstPathLength; + + // Make sure that if we are empty, we set our first distance properly + if (this.items.length === 0) { + this.spacingToFirstItem = this.totalLength; + } + + this.onPathChanged(); + secondPath.onPathChanged(); + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("split-two-first"); + secondPath.debug_checkIntegrity("split-two-second"); + } + + return secondPath; + } + + /** + * Deletes the last entity + * @param {Entity} entity + */ + deleteEntityOnEnd(entity) { + assert( + this.entityPath[this.entityPath.length - 1] === entity, + "Not actually the last entity (instead " + this.entityPath.indexOf(entity) + ")" + ); + + // Ok, first remove the entity + const beltComp = entity.components.Belt; + const beltLength = beltComp.getEffectiveLengthTiles(); + + DEBUG && + logger.log( + "Deleting last entity on path with length", + this.entityPath.length, + "(reducing", + this.totalLength, + " by", + beltLength, + ")" + ); + this.totalLength -= beltLength; + this.entityPath.pop(); + this.onPathChanged(); + + DEBUG && + logger.log( + " New path has length of", + this.totalLength, + "with", + this.entityPath.length, + "entities" + ); + + // This is just for sanity + beltComp.assignedPath = null; + + // Clean up items + if (this.items.length === 0) { + // Simple case with no items, just update the first item spacing + this.spacingToFirstItem = this.totalLength; + } else { + // Ok, make sure we simply drop all items which are no longer contained + let itemOffset = this.spacingToFirstItem; + let lastItemOffset = itemOffset; + + DEBUG && logger.log(" Adjusting", this.items.length, "items"); + + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + + // Get rid of items past this path + if (itemOffset >= this.totalLength) { + DEBUG && logger.log("Dropping item (current index=", i, ")"); + this.items.splice(i, 1); + i -= 1; + continue; + } + + DEBUG && logger.log("Item", i, "is at", itemOffset, "with next offset", item[_nextDistance]); + lastItemOffset = itemOffset; + itemOffset += item[_nextDistance]; + } + + // If we still have an item, make sure the last item matches + if (this.items.length > 0) { + // We can easily compute the next distance since we know where the last item is now + const lastDistance = this.totalLength - lastItemOffset; + assert( + lastDistance >= 0.0, + "Last item distance mismatch: " + + lastDistance + + " -> Total length was " + + this.totalLength + + " and lastItemOffset was " + + lastItemOffset + ); + + DEBUG && + logger.log( + "Adjusted distance of last item: it is at", + lastItemOffset, + "so it has a distance of", + lastDistance, + "to the end (", + this.totalLength, + ")" + ); + this.items[this.items.length - 1][_nextDistance] = lastDistance; + } else { + DEBUG && logger.log(" Removed all items so we'll update spacing to total length"); + + // We removed all items so update our spacing + this.spacingToFirstItem = this.totalLength; + } + } + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("delete-on-end"); + } + } + + /** + * Deletes the entity of the start of the path + * @see deleteEntityOnEnd + * @param {Entity} entity + */ + deleteEntityOnStart(entity) { + assert( + entity === this.entityPath[0], + "Not actually the start entity (instead " + this.entityPath.indexOf(entity) + ")" + ); + + // Ok, first remove the entity + const beltComp = entity.components.Belt; + const beltLength = beltComp.getEffectiveLengthTiles(); + + DEBUG && + logger.log( + "Deleting first entity on path with length", + this.entityPath.length, + "(reducing", + this.totalLength, + " by", + beltLength, + ")" + ); + this.totalLength -= beltLength; + this.entityPath.shift(); + this.onPathChanged(); + + DEBUG && + logger.log( + " New path has length of", + this.totalLength, + "with", + this.entityPath.length, + "entities" + ); + + // This is just for sanity + beltComp.assignedPath = null; + + // Clean up items + if (this.items.length === 0) { + // Simple case with no items, just update the first item spacing + this.spacingToFirstItem = this.totalLength; + } else { + // Simple case, we had no item on the beginning -> all good + if (this.spacingToFirstItem >= beltLength) { + DEBUG && + logger.log( + " No item on the first place, so we can just adjust the spacing (spacing=", + this.spacingToFirstItem, + ") removed =", + beltLength + ); + this.spacingToFirstItem -= beltLength; + } else { + // Welp, okay we need to drop all items which are < beltLength and adjust + // the other item offsets as well + + DEBUG && + logger.log( + " We have at least one item in the beginning, drop those and adjust spacing (first item @", + this.spacingToFirstItem, + ") since we removed", + beltLength, + "length from path" + ); + DEBUG && + logger.log( + " Items:", + this.items.map(i => i[_nextDistance]) + ); + + // Find offset to first item + let itemOffset = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + if (itemOffset <= beltLength) { + DEBUG && + logger.log( + " -> Dropping item with index", + i, + "at", + itemOffset, + "since it was on the removed belt" + ); + // This item must be dropped + this.items.splice(i, 1); + i -= 1; + itemOffset += item[_nextDistance]; + continue; + } else { + // This item can be kept, thus its the first we know + break; + } + } + + if (this.items.length > 0) { + DEBUG && + logger.log( + " Offset of first non-dropped item was at:", + itemOffset, + "-> setting spacing to it (total length=", + this.totalLength, + ")" + ); + + this.spacingToFirstItem = itemOffset - beltLength; + assert( + this.spacingToFirstItem >= 0.0, + "Invalid spacing after delete on start: " + this.spacingToFirstItem + ); + } else { + DEBUG && logger.log(" We dropped all items, simply set spacing to total length"); + // We dropped all items, simple one + this.spacingToFirstItem = this.totalLength; + } + } + } + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("delete-on-start"); + } + } + + /** + * Extends the path by the given other path + * @param {BeltPath} otherPath + */ + extendByPath(otherPath) { + assert(otherPath !== this, "Circular path dependency"); + + const entities = otherPath.entityPath; + DEBUG && logger.log("Extending path by other path, starting to add entities"); + + const oldLength = this.totalLength; + + DEBUG && logger.log(" Adding", entities.length, "new entities, current length =", this.totalLength); + + // First, append entities + for (let i = 0; i < entities.length; ++i) { + const entity = entities[i]; + const beltComp = entity.components.Belt; + + // Add to path and update references + this.entityPath.push(entity); + beltComp.assignedPath = this; + + // Update our length + const additionalLength = beltComp.getEffectiveLengthTiles(); + this.totalLength += additionalLength; + } + + DEBUG && + logger.log( + " Path is now", + this.entityPath.length, + "entities and has a length of", + this.totalLength + ); + + // Now, update the distance of our last item + if (this.items.length !== 0) { + const lastItem = this.items[this.items.length - 1]; + lastItem[_nextDistance] += otherPath.spacingToFirstItem; + DEBUG && + logger.log(" Add distance to last item, effectively being", lastItem[_nextDistance], "now"); + } else { + // Seems we have no items, update our first item distance + this.spacingToFirstItem = oldLength + otherPath.spacingToFirstItem; + DEBUG && + logger.log( + " We had no items, so our new spacing to first is old length (", + oldLength, + ") plus others spacing to first (", + otherPath.spacingToFirstItem, + ") =", + this.spacingToFirstItem + ); + } + + DEBUG && logger.log(" Pushing", otherPath.items.length, "items from other path"); + + // Aaand push the other paths items + for (let i = 0; i < otherPath.items.length; ++i) { + const item = otherPath.items[i]; + this.items.push([item[_nextDistance], item[_item]]); + } + + // Update bounds + this.worldBounds = this.computeBounds(); + + this.onPathChanged(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("extend-by-path"); + } + } + + /** + * Computes the total length of the path + * @returns {number} + */ + computeTotalLength() { + let length = 0; + for (let i = 0; i < this.entityPath.length; ++i) { + length += this.entityPath[i].components.Belt.getEffectiveLengthTiles(); + } + return length; + } + + /** + * Performs one tick + */ + update() { + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("pre-update"); + } + + // Divide by item spacing on belts since we use throughput and not speed + let beltSpeed = + this.root.hubGoals.getBeltBaseSpeed() * + this.root.dynamicTickrate.deltaSeconds * + globalConfig.itemSpacingOnBelts; + + if (G_IS_DEV && globalConfig.debug.instantBelts) { + beltSpeed *= 100; + } + + let minimumDistance = this.ejectorSlot.item ? globalConfig.itemSpacingOnBelts : 0; + + // Try to reduce spacing + let remainingAmount = beltSpeed; + for (let i = this.items.length - 1; i >= 0; --i) { + const nextDistanceAndItem = this.items[i]; + const minimumSpacing = minimumDistance; + + const takeAway = Math.max( + 0, + Math.min(remainingAmount, nextDistanceAndItem[_nextDistance] - minimumSpacing) + ); + + remainingAmount -= takeAway; + nextDistanceAndItem[_nextDistance] -= takeAway; + + this.spacingToFirstItem += takeAway; + if (remainingAmount < 0.01) { + break; + } + + minimumDistance = globalConfig.itemSpacingOnBelts; + } + + const lastItem = this.items[this.items.length - 1]; + if (lastItem && lastItem[_nextDistance] === 0) { + // Take over + if (this.ejectorComp.tryEject(0, lastItem[_item])) { + this.items.pop(); + } + } + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("post-update"); + } + } + + /** + * Computes a world space position from the given progress + * @param {number} progress + * @returns {Vector} + */ + computePositionFromProgress(progress) { + let currentLength = 0; + + // floating point issuses .. + assert(progress <= this.totalLength + 0.02, "Progress too big: " + progress); + + for (let i = 0; i < this.entityPath.length; ++i) { + const beltComp = this.entityPath[i].components.Belt; + const localLength = beltComp.getEffectiveLengthTiles(); + + if (currentLength + localLength >= progress || i === this.entityPath.length - 1) { + // Min required here due to floating point issues + const localProgress = Math.min(1.0, progress - currentLength); + + assert(localProgress >= 0.0, "Invalid local progress: " + localProgress); + const localSpace = beltComp.transformBeltToLocalSpace(localProgress); + return this.entityPath[i].components.StaticMapEntity.localTileToWorld(localSpace); + } + currentLength += localLength; + } + + assert(false, "invalid progress: " + progress + " (max: " + this.totalLength + ")"); + } + + /** + * + * @param {DrawParameters} parameters + */ + drawDebug(parameters) { + if (!parameters.visibleRect.containsRect(this.worldBounds)) { + return; + } + + parameters.context.fillStyle = "#d79a25"; + parameters.context.strokeStyle = "#d79a25"; + parameters.context.beginPath(); + + for (let i = 0; i < this.entityPath.length; ++i) { + const entity = this.entityPath[i]; + const pos = entity.components.StaticMapEntity; + const worldPos = pos.origin.toWorldSpaceCenterOfTile(); + + if (i === 0) { + parameters.context.moveTo(worldPos.x, worldPos.y); + } else { + parameters.context.lineTo(worldPos.x, worldPos.y); + } + } + parameters.context.stroke(); + + // Items + let progress = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const nextDistanceAndItem = this.items[i]; + const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile(); + parameters.context.fillStyle = "#268e4d"; + parameters.context.beginRoundedRect(worldPos.x - 5, worldPos.y - 5, 10, 10, 3); + parameters.context.fill(); + parameters.context.font = "6px GameFont"; + parameters.context.fillStyle = "#111"; + parameters.context.fillText( + "" + round4Digits(nextDistanceAndItem[_nextDistance]), + worldPos.x + 5, + worldPos.y + 2 + ); + progress += nextDistanceAndItem[_nextDistance]; + } + + for (let i = 0; i < this.entityPath.length; ++i) { + const entity = this.entityPath[i]; + parameters.context.fillStyle = "#d79a25"; + const pos = entity.components.StaticMapEntity; + const worldPos = pos.origin.toWorldSpaceCenterOfTile(); + parameters.context.beginCircle(worldPos.x, worldPos.y, i === 0 ? 5 : 3); + parameters.context.fill(); + } + + for (let progress = 0; progress <= this.totalLength + 0.01; progress += 0.2) { + const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile(); + parameters.context.fillStyle = "red"; + parameters.context.beginCircle(worldPos.x, worldPos.y, 1); + parameters.context.fill(); + } + + const firstItemIndicator = this.computePositionFromProgress( + this.spacingToFirstItem + ).toWorldSpaceCenterOfTile(); + parameters.context.fillStyle = "purple"; + parameters.context.fillRect(firstItemIndicator.x - 3, firstItemIndicator.y - 1, 6, 2); + } + + /** + * Draws the path + * @param {DrawParameters} parameters + */ + draw(parameters) { + if (!parameters.visibleRect.containsRect(this.worldBounds)) { + return; + } + + let progress = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const nextDistanceAndItem = this.items[i]; + const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile(); + if (parameters.visibleRect.containsCircle(worldPos.x, worldPos.y, 10)) { + nextDistanceAndItem[_item].draw(worldPos.x, worldPos.y, parameters); + } + progress += nextDistanceAndItem[_nextDistance]; + } + } +} diff --git a/src/js/game/hud/parts/blueprint.js b/src/js/game/blueprint.js similarity index 82% rename from src/js/game/hud/parts/blueprint.js rename to src/js/game/blueprint.js index 8e75ed46..95a24e4a 100644 --- a/src/js/game/hud/parts/blueprint.js +++ b/src/js/game/blueprint.js @@ -1,14 +1,13 @@ -import { DrawParameters } from "../../../core/draw_parameters"; -import { Loader } from "../../../core/loader"; -import { createLogger } from "../../../core/logging"; -import { Vector } from "../../../core/vector"; -import { Entity } from "../../entity"; -import { GameRoot } from "../../root"; -import { findNiceIntegerValue } from "../../../core/utils"; -import { Math_pow } from "../../../core/builtins"; -import { blueprintShape } from "../../upgrades"; -import { globalConfig } from "../../../core/config"; -import { Rectangle } from "../../../core/rectangle"; +import { DrawParameters } from "../core/draw_parameters"; +import { Loader } from "../core/loader"; +import { createLogger } from "../core/logging"; +import { Vector } from "../core/vector"; +import { Entity } from "./entity"; +import { GameRoot } from "./root"; +import { findNiceIntegerValue } from "../core/utils"; +import { blueprintShape } from "./upgrades"; +import { globalConfig } from "../core/config"; +import { Rectangle } from "../core/rectangle"; const logger = createLogger("blueprint"); @@ -59,7 +58,7 @@ export class Blueprint { if (G_IS_DEV && globalConfig.debug.blueprintsNoCost) { return 0; } - return findNiceIntegerValue(4 * Math_pow(this.entities.length, 1.1)); + return findNiceIntegerValue(4 * Math.pow(this.entities.length, 1.1)); } /** @@ -178,14 +177,6 @@ export class Blueprint { return root.logic.performBulkOperation(() => { let anyPlaced = false; - /** - * To avoid recomputing belt direction when belts are placed, we manually - * recompute the belt cache without redirecting the belts by calculating the - * area that needs to update. - * @type {Rectangle?} - */ - let rectCoveredByBlueprint = null; - for (let i = 0; i < this.entities.length; ++i) { let placeable = true; const entity = this.entities[i]; @@ -226,20 +217,8 @@ export class Blueprint { root.map.placeStaticEntity(clone); root.entityMgr.registerEntity(clone); - if (rectCoveredByBlueprint === null) { - rectCoveredByBlueprint = rect.clone(); - } else { - rectCoveredByBlueprint = rectCoveredByBlueprint.getUnion(rect); - } - anyPlaced = true; } } - if (anyPlaced) { - logger.log(rectCoveredByBlueprint); - root.signals.blueprintPlacedUpdateBeltCache.dispatch( - rectCoveredByBlueprint.expandedInAllDirections(1) - ); - } return anyPlaced; }); diff --git a/src/js/game/buildings/cutter.js b/src/js/game/buildings/cutter.js index a9433390..bfe142d5 100644 --- a/src/js/game/buildings/cutter.js +++ b/src/js/game/buildings/cutter.js @@ -1,14 +1,14 @@ -import { globalConfig } from "../../core/config"; +import { formatItemsPerSecond } from "../../core/utils"; import { enumDirection, Vector } from "../../core/vector"; -import { enumItemAcceptorItemFilter, ItemAcceptorComponent } from "../components/item_acceptor"; +import { T } from "../../translations"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemEjectorComponent } from "../components/item_ejector"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { Entity } from "../entity"; -import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; import { enumHubGoalRewards } from "../tutorial_goals"; -import { T } from "../../translations"; -import { formatItemsPerSecond } from "../../core/utils"; +import { enumItemType } from "../base_item"; /** @enum {string} */ export const enumCutterVariants = { quad: "quad" }; @@ -82,7 +82,7 @@ export class MetaCutterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, ], }) diff --git a/src/js/game/buildings/energy_generator.js b/src/js/game/buildings/energy_generator.js new file mode 100644 index 00000000..24c6f90c --- /dev/null +++ b/src/js/game/buildings/energy_generator.js @@ -0,0 +1,108 @@ +import { enumDirection, Vector } from "../../core/vector"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { Entity } from "../entity"; +import { MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { EnergyGeneratorComponent } from "../components/energy_generator"; +import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins"; +import { enumItemType } from "../base_item"; + +export class MetaEnergyGenerator extends MetaBuilding { + constructor() { + super("energy_generator"); + } + + isRotateable(variant) { + return false; + } + + 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 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, 0), + directions: [enumDirection.top], + filter: enumItemType.shape, + }, + + { + pos: new Vector(1, 0), + directions: [enumDirection.top], + filter: enumItemType.shape, + }, + { + pos: new Vector(0, 1), + directions: [enumDirection.bottom], + filter: enumItemType.shape, + }, + { + pos: new Vector(1, 1), + directions: [enumDirection.bottom], + filter: enumItemType.shape, + }, + ], + }) + ); + + entity.addComponent( + new EnergyGeneratorComponent({ + // Set by the energy generator system later + requiredKey: null, + }) + ); + + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + type: enumPinSlotType.energyEjector, + }, + { + pos: new Vector(1, 0), + type: enumPinSlotType.energyEjector, + }, + { + pos: new Vector(0, 1), + type: enumPinSlotType.energyEjector, + }, + { + pos: new Vector(1, 1), + type: enumPinSlotType.energyEjector, + }, + ], + }) + ); + } +} diff --git a/src/js/game/buildings/hub.js b/src/js/game/buildings/hub.js index 49d95005..61a61f27 100644 --- a/src/js/game/buildings/hub.js +++ b/src/js/game/buildings/hub.js @@ -1,11 +1,11 @@ import { enumDirection, Vector } from "../../core/vector"; -import { enumItemAcceptorItemFilter, ItemAcceptorComponent } from "../components/item_acceptor"; +import { enumItemType } from "../base_item"; +import { HubComponent } from "../components/hub"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; +import { UnremovableComponent } from "../components/unremovable"; import { Entity } from "../entity"; import { MetaBuilding } from "../meta_building"; -import { ItemProcessorComponent, enumItemProcessorTypes } from "../components/item_processor"; -import { globalConfig } from "../../core/config"; -import { UnremovableComponent } from "../components/unremovable"; -import { HubComponent } from "../components/hub"; export class MetaHubBuilding extends MetaBuilding { constructor() { @@ -51,72 +51,72 @@ export class MetaHubBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.top, enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(1, 0), directions: [enumDirection.top], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(2, 0), directions: [enumDirection.top], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(3, 0), directions: [enumDirection.top, enumDirection.right], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(0, 3), directions: [enumDirection.bottom, enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(1, 3), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(2, 3), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(3, 3), directions: [enumDirection.bottom, enumDirection.right], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(0, 1), directions: [enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(0, 2), directions: [enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(0, 3), directions: [enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(3, 1), directions: [enumDirection.right], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(3, 2), directions: [enumDirection.right], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(3, 3), directions: [enumDirection.right], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, ], }) diff --git a/src/js/game/buildings/mixer.js b/src/js/game/buildings/mixer.js index a20bff82..a2b35280 100644 --- a/src/js/game/buildings/mixer.js +++ b/src/js/game/buildings/mixer.js @@ -1,14 +1,14 @@ -import { globalConfig } from "../../core/config"; +import { formatItemsPerSecond } from "../../core/utils"; import { enumDirection, Vector } from "../../core/vector"; -import { ItemAcceptorComponent, enumItemAcceptorItemFilter } from "../components/item_acceptor"; +import { T } from "../../translations"; +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 { MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; import { enumHubGoalRewards } from "../tutorial_goals"; -import { T } from "../../translations"; -import { formatItemsPerSecond } from "../../core/utils"; export class MetaMixerBuilding extends MetaBuilding { constructor() { @@ -63,12 +63,12 @@ export class MetaMixerBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, { pos: new Vector(1, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, ], }) diff --git a/src/js/game/buildings/painter.js b/src/js/game/buildings/painter.js index 835dde1e..61e77a7c 100644 --- a/src/js/game/buildings/painter.js +++ b/src/js/game/buildings/painter.js @@ -1,14 +1,14 @@ -import { globalConfig } from "../../core/config"; +import { formatItemsPerSecond } from "../../core/utils"; import { enumDirection, Vector } from "../../core/vector"; -import { enumItemAcceptorItemFilter, ItemAcceptorComponent } from "../components/item_acceptor"; +import { T } from "../../translations"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemEjectorComponent } from "../components/item_ejector"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { Entity } from "../entity"; -import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; -import { enumHubGoalRewards } from "../tutorial_goals"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; -import { T } from "../../translations"; -import { formatItemsPerSecond } from "../../core/utils"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { enumItemType } from "../base_item"; /** @enum {string} */ export const enumPainterVariants = { mirrored: "mirrored", double: "double", quad: "quad" }; @@ -98,12 +98,12 @@ export class MetaPainterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(1, 0), directions: [enumDirection.top], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, ], }) @@ -124,14 +124,14 @@ export class MetaPainterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(1, 0), directions: [ variant === defaultBuildingVariant ? enumDirection.top : enumDirection.bottom, ], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, ]); @@ -147,17 +147,17 @@ export class MetaPainterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(0, 1), directions: [enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(1, 0), directions: [enumDirection.top], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, ]); @@ -174,27 +174,27 @@ export class MetaPainterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.left], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, { pos: new Vector(1, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, { pos: new Vector(2, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, { pos: new Vector(3, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.color, + filter: enumItemType.color, }, ]); diff --git a/src/js/game/buildings/rotater.js b/src/js/game/buildings/rotater.js index abd3f2c2..56c0c87d 100644 --- a/src/js/game/buildings/rotater.js +++ b/src/js/game/buildings/rotater.js @@ -1,14 +1,14 @@ -import { globalConfig } from "../../core/config"; +import { formatItemsPerSecond } from "../../core/utils"; import { enumDirection, Vector } from "../../core/vector"; -import { ItemAcceptorComponent, enumItemAcceptorItemFilter } from "../components/item_acceptor"; +import { T } from "../../translations"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemEjectorComponent } from "../components/item_ejector"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { Entity } from "../entity"; -import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; -import { enumHubGoalRewards } from "../tutorial_goals"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; -import { T } from "../../translations"; -import { formatItemsPerSecond } from "../../core/utils"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { enumItemType } from "../base_item"; /** @enum {string} */ export const enumRotaterVariants = { ccw: "ccw" }; @@ -77,7 +77,7 @@ export class MetaRotaterBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, ], }) diff --git a/src/js/game/buildings/stacker.js b/src/js/game/buildings/stacker.js index 9b6f5ef3..91ac3f62 100644 --- a/src/js/game/buildings/stacker.js +++ b/src/js/game/buildings/stacker.js @@ -1,14 +1,14 @@ -import { globalConfig } from "../../core/config"; +import { formatItemsPerSecond } from "../../core/utils"; import { enumDirection, Vector } from "../../core/vector"; -import { ItemAcceptorComponent, enumItemAcceptorItemFilter } from "../components/item_acceptor"; +import { T } from "../../translations"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; import { ItemEjectorComponent } from "../components/item_ejector"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { Entity } from "../entity"; import { MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; import { enumHubGoalRewards } from "../tutorial_goals"; -import { formatItemsPerSecond } from "../../core/utils"; -import { T } from "../../translations"; +import { enumItemType } from "../base_item"; export class MetaStackerBuilding extends MetaBuilding { constructor() { @@ -63,12 +63,12 @@ export class MetaStackerBuilding extends MetaBuilding { { pos: new Vector(0, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, { pos: new Vector(1, 0), directions: [enumDirection.bottom], - filter: enumItemAcceptorItemFilter.shape, + filter: enumItemType.shape, }, ], }) diff --git a/src/js/game/camera.js b/src/js/game/camera.js index d2c468c9..2caeb863 100644 --- a/src/js/game/camera.js +++ b/src/js/game/camera.js @@ -1,12 +1,3 @@ -import { - Math_abs, - Math_ceil, - Math_floor, - Math_max, - Math_min, - Math_random, - performanceNow, -} from "../core/builtins"; import { clickDetectorGlobals } from "../core/click_detector"; import { globalConfig, SUPPORT_TOUCH } from "../core/config"; import { createLogger } from "../core/logging"; @@ -137,8 +128,8 @@ export class Camera extends BasicSerializableObject { addScreenShake(amount) { const currentShakeAmount = this.currentShake.length(); const scale = 1 / (1 + 3 * currentShakeAmount); - this.currentShake.x = this.currentShake.x + 2 * (Math_random() - 0.5) * scale * amount; - this.currentShake.y = this.currentShake.y + 2 * (Math_random() - 0.5) * scale * amount; + this.currentShake.x = this.currentShake.x + 2 * (Math.random() - 0.5) * scale * amount; + this.currentShake.y = this.currentShake.y + 2 * (Math.random() - 0.5) * scale * amount; } /** @@ -181,7 +172,7 @@ export class Camera extends BasicSerializableObject { const zoomLevelX = this.root.gameWidth / desiredWorldSpaceWidth; const zoomLevelY = this.root.gameHeight / desiredWorldSpaceWidth; - const finalLevel = Math_min(zoomLevelX, zoomLevelY); + const finalLevel = Math.min(zoomLevelX, zoomLevelY); assert( Number.isFinite(finalLevel) && finalLevel > 0, "Invalid zoom level computed for initial zoom: " + finalLevel @@ -292,10 +283,10 @@ export class Camera extends BasicSerializableObject { */ getVisibleRect() { return Rectangle.fromTRBL( - Math_floor(this.getViewportTop()), - Math_ceil(this.getViewportRight()), - Math_ceil(this.getViewportBottom()), - Math_floor(this.getViewportLeft()) + Math.floor(this.getViewportTop()), + Math.ceil(this.getViewportRight()), + Math.ceil(this.getViewportBottom()), + Math.floor(this.getViewportLeft()) ); } @@ -426,7 +417,7 @@ export class Camera extends BasicSerializableObject { * should get ignored */ checkPreventDoubleMouse() { - if (performanceNow() - clickDetectorGlobals.lastTouchTime < 1000.0) { + if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) { return false; } return true; @@ -531,7 +522,7 @@ export class Camera extends BasicSerializableObject { // event.stopPropagation(); } - clickDetectorGlobals.lastTouchTime = performanceNow(); + clickDetectorGlobals.lastTouchTime = performance.now(); this.touchPostMoveVelocity = new Vector(0, 0); if (event.touches.length === 1) { @@ -565,7 +556,7 @@ export class Camera extends BasicSerializableObject { // event.stopPropagation(); } - clickDetectorGlobals.lastTouchTime = performanceNow(); + clickDetectorGlobals.lastTouchTime = performance.now(); if (event.touches.length === 1) { const touch = event.touches[0]; @@ -585,7 +576,7 @@ export class Camera extends BasicSerializableObject { const thisDistance = newPinchPositions[0].distance(newPinchPositions[1]); // IMPORTANT to do math max here to avoid NaN and causing an invalid zoom level - const difference = thisDistance / Math_max(0.001, lastDistance); + const difference = thisDistance / Math.max(0.001, lastDistance); // Find old center of zoom let oldCenter = this.lastPinchPositions[0].centerPoint(this.lastPinchPositions[1]); @@ -645,7 +636,7 @@ export class Camera extends BasicSerializableObject { } } - clickDetectorGlobals.lastTouchTime = performanceNow(); + clickDetectorGlobals.lastTouchTime = performance.now(); if (event.changedTouches.length === 0) { logger.warn("Touch end without changed touches"); } @@ -753,7 +744,7 @@ export class Camera extends BasicSerializableObject { * @param {number} dt Delta time in milliseconds */ update(dt) { - dt = Math_min(dt, 33); + dt = Math.min(dt, 33); this.cameraUpdateTimeBucket += dt; // Simulate movement of N FPS @@ -861,7 +852,7 @@ export class Camera extends BasicSerializableObject { internalUpdateZooming(now, dt) { if (!this.currentlyPinching && this.desiredZoom !== null) { const diff = this.zoomLevel - this.desiredZoom; - if (Math_abs(diff) > 0.0001) { + if (Math.abs(diff) > 0.0001) { let fade = 0.94; if (diff > 0) { // Zoom out faster than in @@ -889,7 +880,7 @@ export class Camera extends BasicSerializableObject { const length = diff.length(); const tolerance = 1 / this.zoomLevel; if (length > tolerance) { - const movement = diff.multiplyScalar(Math_min(1, dt * 0.008)); + const movement = diff.multiplyScalar(Math.min(1, dt * 0.008)); this.center.x += movement.x; this.center.y += movement.y; } else { @@ -905,7 +896,7 @@ export class Camera extends BasicSerializableObject { */ internalUpdateKeyboardForce(now, dt) { if (!this.currentlyMoving && this.desiredCenter == null) { - const limitingDimension = Math_min(this.root.gameWidth, this.root.gameHeight); + const limitingDimension = Math.min(this.root.gameWidth, this.root.gameHeight); const moveAmount = ((limitingDimension / 2048) * dt) / this.zoomLevel; diff --git a/src/js/game/component_registry.js b/src/js/game/component_registry.js index 76458d43..c0077a5a 100644 --- a/src/js/game/component_registry.js +++ b/src/js/game/component_registry.js @@ -10,6 +10,8 @@ import { UndergroundBeltComponent } from "./components/underground_belt"; import { UnremovableComponent } from "./components/unremovable"; import { HubComponent } from "./components/hub"; import { StorageComponent } from "./components/storage"; +import { EnergyGeneratorComponent } from "./components/energy_generator"; +import { WiredPinsComponent } from "./components/wired_pins"; export function initComponentRegistry() { gComponentRegistry.register(StaticMapEntityComponent); @@ -23,6 +25,8 @@ export function initComponentRegistry() { gComponentRegistry.register(UnremovableComponent); gComponentRegistry.register(HubComponent); gComponentRegistry.register(StorageComponent); + gComponentRegistry.register(EnergyGeneratorComponent); + gComponentRegistry.register(WiredPinsComponent); // IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS diff --git a/src/js/game/components/belt.js b/src/js/game/components/belt.js index a9be5c99..5689376e 100644 --- a/src/js/game/components/belt.js +++ b/src/js/game/components/belt.js @@ -1,12 +1,11 @@ -import { Component } from "../component"; +import { enumDirection, Vector } from "../../core/vector"; import { types } from "../../savegame/serialization"; -import { gItemRegistry } from "../../core/global_registries"; -import { BaseItem } from "../base_item"; -import { Vector, enumDirection } from "../../core/vector"; -import { Math_PI, Math_sin, Math_cos } from "../../core/builtins"; -import { globalConfig } from "../../core/config"; +import { BeltPath } from "../belt_path"; +import { Component } from "../component"; import { Entity } from "../entity"; +export const curvedBeltLength = /* Math.PI / 4 */ 0.78; + export class BeltComponent extends Component { static getId() { return "Belt"; @@ -16,7 +15,6 @@ export class BeltComponent extends Component { // The followUpCache field is not serialized. return { direction: types.string, - sortedItems: types.array(types.pair(types.float, types.obj(gItemRegistry))), }; } @@ -34,11 +32,22 @@ export class BeltComponent extends Component { this.direction = direction; - /** @type {Array<[number, BaseItem]>} */ - this.sortedItems = []; - /** @type {Entity} */ this.followUpCache = null; + + /** + * The path this belt is contained in, not serialized + * @type {BeltPath} + */ + this.assignedPath = null; + } + + /** + * Returns the effective length of this belt in tile space + * @returns {number} + */ + getEffectiveLengthTiles() { + return this.direction === enumDirection.top ? 1.0 : curvedBeltLength; } /** @@ -50,61 +59,22 @@ export class BeltComponent extends Component { transformBeltToLocalSpace(progress) { switch (this.direction) { case enumDirection.top: + assert(progress <= 1.02, "Invalid progress: " + progress); return new Vector(0, 0.5 - progress); case enumDirection.right: { - const arcProgress = progress * 0.5 * Math_PI; - return new Vector(0.5 - 0.5 * Math_cos(arcProgress), 0.5 - 0.5 * Math_sin(arcProgress)); + 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: { - const arcProgress = progress * 0.5 * Math_PI; - return new Vector(-0.5 + 0.5 * Math_cos(arcProgress), 0.5 - 0.5 * Math_sin(arcProgress)); + 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); } } - - /** - * Returns if the belt can currently accept an item from the given direction - */ - canAcceptItem() { - const firstItem = this.sortedItems[0]; - if (!firstItem) { - return true; - } - - return firstItem[0] > globalConfig.itemSpacingOnBelts; - } - - /** - * Pushes a new item to the belt - * @param {BaseItem} item - */ - takeItem(item, leftoverProgress = 0.0) { - if (G_IS_DEV) { - assert( - this.sortedItems.length === 0 || - leftoverProgress <= this.sortedItems[0][0] - globalConfig.itemSpacingOnBelts + 0.001, - "Invalid leftover: " + - leftoverProgress + - " items are " + - this.sortedItems.map(item => item[0]) - ); - assert(leftoverProgress < 1.0, "Invalid leftover: " + leftoverProgress); - } - this.sortedItems.unshift([leftoverProgress, item]); - } - - /** - * Returns how much space there is to the first item - */ - getDistanceToFirstItemCenter() { - const firstItem = this.sortedItems[0]; - if (!firstItem) { - return 1; - } - return firstItem[0]; - } } diff --git a/src/js/game/components/energy_generator.js b/src/js/game/components/energy_generator.js new file mode 100644 index 00000000..41b6ec35 --- /dev/null +++ b/src/js/game/components/energy_generator.js @@ -0,0 +1,54 @@ +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { Component } from "../component"; +import { ShapeItem } from "../items/shape_item"; + +const maxQueueSize = 10; + +export class EnergyGeneratorComponent extends Component { + static getId() { + return "EnergyGenerator"; + } + + static getSchema() { + return { + requiredKey: types.string, + }; + } + + /** + * + * @param {object} param0 + * @param {string} param0.requiredKey Which shape this generator needs, can be null if not computed yet + */ + constructor({ requiredKey }) { + super(); + this.requiredKey = requiredKey; + + /** + * Stores how many items are ready to be converted to energy + * @type {number} + */ + this.itemsInQueue = 0; + } + + /** + * + * @param {BaseItem} item + */ + tryTakeItem(item) { + 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; + } +} diff --git a/src/js/game/components/item_acceptor.js b/src/js/game/components/item_acceptor.js index 6f3895aa..cc7e68e2 100644 --- a/src/js/game/components/item_acceptor.js +++ b/src/js/game/components/item_acceptor.js @@ -1,23 +1,12 @@ -import { Component } from "../component"; -import { Vector, enumDirection, enumInvertedDirections } from "../../core/vector"; -import { BaseItem } from "../base_item"; -import { ShapeItem } from "../items/shape_item"; -import { ColorItem } from "../items/color_item"; +import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector"; import { types } from "../../savegame/serialization"; - -/** - * @enum {string?} - */ -export const enumItemAcceptorItemFilter = { - shape: "shape", - color: "color", - none: null, -}; +import { BaseItem, enumItemType } from "../base_item"; +import { Component } from "../component"; /** @typedef {{ * pos: Vector, * directions: enumDirection[], - * filter?: enumItemAcceptorItemFilter + * filter?: enumItemType * }} ItemAcceptorSlot */ /** @@ -39,7 +28,7 @@ export class ItemAcceptorComponent extends Component { types.structured({ pos: types.vector, directions: types.array(types.enum(enumDirection)), - filter: types.nullable(types.enum(enumItemAcceptorItemFilter)), + filter: types.nullable(types.enum(enumItemType)), }) ), animated: types.bool, @@ -49,16 +38,6 @@ export class ItemAcceptorComponent extends Component { direction: types.enum(enumDirection), }) ), - - // We don't actually need to store the animations - // itemConsumptionAnimations: types.array( - // types.structured({ - // item: types.obj(gItemRegistry), - // slotIndex: types.uint, - // animProgress: types.float, - // direction: types.enum(enumDirection), - // }) - // ), }; } @@ -92,7 +71,7 @@ export class ItemAcceptorComponent extends Component { /** * * @param {object} param0 - * @param {Array<{pos: Vector, directions: enumDirection[], filter?: enumItemAcceptorItemFilter}>} param0.slots The slots from which we accept items + * @param {Array<{pos: Vector, directions: enumDirection[], filter?: enumItemType}>} param0.slots The slots from which we accept items * @param {boolean=} param0.animated Whether to animate item consumption * @param {Array<{pos: Vector, direction: enumDirection}>=} param0.beltUnderlays Where to render belt underlays */ @@ -115,10 +94,10 @@ export class ItemAcceptorComponent extends Component { /** * - * @param {Array<{pos: Vector, directions: enumDirection[], filter?: enumItemAcceptorItemFilter}>} slots + * @param {Array<{pos: Vector, directions: enumDirection[], filter?: enumItemType}>} slots */ setSlots(slots) { - /** @type {Array<{pos: Vector, directions: enumDirection[], filter?: enumItemAcceptorItemFilter}>} */ + /** @type {Array<{pos: Vector, directions: enumDirection[], filter?: enumItemType}>} */ this.slots = []; for (let i = 0; i < slots.length; ++i) { const slot = slots[i]; @@ -126,7 +105,7 @@ export class ItemAcceptorComponent extends Component { pos: slot.pos, directions: slot.directions, - // Which type of item to accept (shape | color | all) @see enumItemAcceptorItemFilter + // Which type of item to accept (shape | color | all) @see enumItemType filter: slot.filter, }); } @@ -139,16 +118,7 @@ export class ItemAcceptorComponent extends Component { */ canAcceptItem(slotIndex, item) { const slot = this.slots[slotIndex]; - switch (slot.filter) { - case enumItemAcceptorItemFilter.shape: { - return item instanceof ShapeItem; - } - case enumItemAcceptorItemFilter.color: { - return item instanceof ColorItem; - } - default: - return true; - } + return !slot.filter || slot.filter === item.getItemType(); } /** diff --git a/src/js/game/components/item_ejector.js b/src/js/game/components/item_ejector.js index e78faa36..739a0b9f 100644 --- a/src/js/game/components/item_ejector.js +++ b/src/js/game/components/item_ejector.js @@ -69,6 +69,11 @@ export class ItemEjectorComponent extends Component { /** @type {ItemEjectorSlot[]} */ this.cachedConnectedSlots = null; + + /** + * Whether this ejector slot is enabled + */ + this.enabled = true; } /** @@ -194,4 +199,17 @@ export class ItemEjectorComponent extends Component { this.slots[slotIndex].progress = this.instantEject ? 1 : 0; return true; } + + /** + * Clears the given slot and returns the item it had + * @param {number} slotIndex + * @returns {BaseItem|null} + */ + takeSlotItem(slotIndex) { + const slot = this.slots[slotIndex]; + const item = slot.item; + slot.item = null; + slot.progress = 0.0; + return item; + } } diff --git a/src/js/game/components/item_processor.js b/src/js/game/components/item_processor.js index eab51ae2..7b81ab16 100644 --- a/src/js/game/components/item_processor.js +++ b/src/js/game/components/item_processor.js @@ -1,8 +1,7 @@ +import { gItemRegistry } from "../../core/global_registries"; +import { types } from "../../savegame/serialization"; import { BaseItem } from "../base_item"; import { Component } from "../component"; -import { enumDirection, Vector } from "../../core/vector"; -import { types } from "../../savegame/serialization"; -import { gItemRegistry } from "../../core/global_registries"; /** @enum {string} */ export const enumItemProcessorTypes = { @@ -102,6 +101,12 @@ export class ItemProcessorComponent extends Component { * @param {number} sourceSlot */ tryTakeItem(item, sourceSlot) { + if (this.type === enumItemProcessorTypes.hub || this.type === enumItemProcessorTypes.trash) { + // Hub has special logic .. not really nice but efficient. + this.inputSlots.push({ item, sourceSlot }); + return true; + } + // Check that we only take one item per slot for (let i = 0; i < this.inputSlots.length; ++i) { const slot = this.inputSlots[i]; diff --git a/src/js/game/components/static_map_entity.js b/src/js/game/components/static_map_entity.js index ed616213..3f0794a4 100644 --- a/src/js/game/components/static_map_entity.js +++ b/src/js/game/components/static_map_entity.js @@ -1,4 +1,3 @@ -import { Math_radians } from "../../core/builtins"; import { globalConfig } from "../../core/config"; import { DrawParameters } from "../../core/draw_parameters"; import { Rectangle } from "../../core/rectangle"; @@ -253,7 +252,7 @@ export class StaticMapEntityComponent extends Component { const rotationCenterY = worldY + globalConfig.halfTileSize; parameters.context.translate(rotationCenterX, rotationCenterY); - parameters.context.rotate(Math_radians(this.rotation)); + parameters.context.rotate(Math.radians(this.rotation)); sprite.drawCached( parameters, @@ -264,7 +263,7 @@ export class StaticMapEntityComponent extends Component { false ); - parameters.context.rotate(-Math_radians(this.rotation)); + parameters.context.rotate(-Math.radians(this.rotation)); parameters.context.translate(-rotationCenterX, -rotationCenterY); } } diff --git a/src/js/game/components/storage.js b/src/js/game/components/storage.js index 69f4e367..e7b40a77 100644 --- a/src/js/game/components/storage.js +++ b/src/js/game/components/storage.js @@ -1,7 +1,7 @@ import { Component } from "../component"; import { types } from "../../savegame/serialization"; import { gItemRegistry } from "../../core/global_registries"; -import { BaseItem } from "../base_item"; +import { BaseItem, enumItemType } from "../base_item"; import { ColorItem } from "../items/color_item"; import { ShapeItem } from "../items/shape_item"; @@ -60,17 +60,23 @@ export class StorageComponent extends Component { return true; } - if (item instanceof ColorItem) { - return this.storedItem instanceof ColorItem && this.storedItem.color === item.color; + const itemType = item.getItemType(); + + // Check type matches + if (itemType !== this.storedItem.getItemType()) { + return false; } - if (item instanceof ShapeItem) { + if (itemType === enumItemType.color) { + return /** @type {ColorItem} */ (this.storedItem).color === /** @type {ColorItem} */ (item).color; + } + + if (itemType === enumItemType.shape) { return ( - this.storedItem instanceof ShapeItem && - this.storedItem.definition.getHash() === item.definition.getHash() + /** @type {ShapeItem} */ (this.storedItem).definition.getHash() === + /** @type {ShapeItem} */ (item).definition.getHash() ); } - return false; } diff --git a/src/js/game/components/underground_belt.js b/src/js/game/components/underground_belt.js index 4fcbbb48..f873e6e9 100644 --- a/src/js/game/components/underground_belt.js +++ b/src/js/game/components/underground_belt.js @@ -3,6 +3,7 @@ import { Component } from "../component"; import { globalConfig } from "../../core/config"; import { types } from "../../savegame/serialization"; import { gItemRegistry } from "../../core/global_registries"; +import { Entity } from "../entity"; /** @enum {string} */ export const enumUndergroundBeltMode = { @@ -10,6 +11,13 @@ export const enumUndergroundBeltMode = { receiver: "receiver", }; +/** + * @typedef {{ + * entity: Entity, + * distance: number + * }} LinkedUndergroundBelt + */ + export class UndergroundBeltComponent extends Component { static getId() { return "UndergroundBelt"; @@ -52,6 +60,13 @@ export class UndergroundBeltComponent extends Component { * @type {Array<[BaseItem, number]>} Format is [Item, remaining seconds until transfer/ejection] */ this.pendingItems = []; + + /** + * The linked entity, used to speed up performance. This contains either + * the entrance or exit depending on the tunnel type + * @type {LinkedUndergroundBelt} + */ + this.cachedLinkedEntity = null; } /** diff --git a/src/js/game/components/wired_pins.js b/src/js/game/components/wired_pins.js new file mode 100644 index 00000000..8157de5a --- /dev/null +++ b/src/js/game/components/wired_pins.js @@ -0,0 +1,65 @@ +import { Component } from "../component"; +import { Vector } from "../../core/vector"; +import { types } from "../../savegame/serialization"; + +/** @enum {string} */ +export const enumPinSlotType = { + energyEjector: "energyEjector", +}; + +/** @typedef {{ + * pos: Vector, + * type: enumPinSlotType + * }} WirePinSlotDefinition */ + +/** @typedef {{ + * pos: Vector, + * type: enumPinSlotType, + * value: number + * }} WirePinSlot */ + +export class WiredPinsComponent extends Component { + static getId() { + return "WiredPins"; + } + + static getSchema() { + return { + slots: types.array( + types.structured({ + pos: types.vector, + type: types.enum(enumPinSlotType), + value: types.float, + }) + ), + }; + } + + /** + * + * @param {object} param0 + * @param {Array} param0.slots + */ + constructor({ slots }) { + super(); + this.setSlots(slots); + } + + /** + * Sets the slots of this building + * @param {Array} slots + */ + setSlots(slots) { + /** @type {Array} */ + this.slots = []; + + for (let i = 0; i < slots.length; ++i) { + const slotData = slots[i]; + this.slots.push({ + pos: slotData.pos, + type: slotData.type, + value: 0.0, + }); + } + } +} diff --git a/src/js/game/core.js b/src/js/game/core.js index 3fc02397..cd7b4e20 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -5,7 +5,6 @@ import { Application } from "../application"; import { BufferMaintainer } from "../core/buffer_maintainer"; import { disableImageSmoothing, enableImageSmoothing, registerCanvas } from "../core/buffer_utils"; -import { Math_random } from "../core/builtins"; import { globalConfig } from "../core/config"; import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager"; import { DrawParameters } from "../core/draw_parameters"; @@ -24,7 +23,7 @@ import { GameHUD } from "./hud/hud"; import { KeyActionMapper } from "./key_action_mapper"; import { GameLogic } from "./logic"; import { MapView } from "./map_view"; -import { GameRoot } from "./root"; +import { GameRoot, enumEditMode } from "./root"; import { ShapeDefinitionManager } from "./shape_definition_manager"; import { SoundProxy } from "./sound_proxy"; import { GameTime } from "./time/game_time"; @@ -403,13 +402,25 @@ export class GameCore { root.map.drawForeground(params); if (!this.root.camera.getIsMapOverlayActive()) { systems.hub.draw(params); + systems.energyGenerator.draw(params); systems.storage.draw(params); } + // WIRES + // root.hud.parts.wiresOverlay.draw(params); + + if (this.root.editMode === enumEditMode.wires) { + systems.wiredPins.drawWiresLayer(params); + } + if (G_IS_DEV) { root.map.drawStaticEntityDebugOverlays(params); } + if (G_IS_DEV && globalConfig.debug.renderBeltPaths) { + systems.belt.drawBeltPathDebug(params); + } + // END OF GAME CONTENT // ----- @@ -431,7 +442,7 @@ export class GameCore { for (let i = 0; i < 1e8; ++i) { sum += i; } - if (Math_random() > 0.95) { + if (Math.random() > 0.95) { console.log(sum); } } diff --git a/src/js/game/dynamic_tickrate.js b/src/js/game/dynamic_tickrate.js index 076532d5..f289e2c1 100644 --- a/src/js/game/dynamic_tickrate.js +++ b/src/js/game/dynamic_tickrate.js @@ -1,7 +1,6 @@ import { GameRoot } from "./root"; import { createLogger } from "../core/logging"; import { globalConfig } from "../core/config"; -import { performanceNow, Math_min, Math_round, Math_max } from "../core/builtins"; import { round3Digits } from "../core/utils"; const logger = createLogger("dynamic_tickrate"); @@ -35,7 +34,7 @@ export class DynamicTickrate { onFrameRendered() { ++this.accumulatedFps; - const now = performanceNow(); + const now = performance.now(); const timeDuration = now - this.accumulatedFpsLastUpdate; if (timeDuration > fpsAccumulationTime) { const avgFps = (this.accumulatedFps / fpsAccumulationTime) * 1000; @@ -65,7 +64,7 @@ export class DynamicTickrate { } const desiredFps = this.root.app.settings.getDesiredFps(); - this.setTickRate(Math_round(Math_min(desiredFps, this.currentTickRate * 1.2))); + this.setTickRate(Math.round(Math.min(desiredFps, this.currentTickRate * 1.2))); } /** @@ -77,7 +76,7 @@ export class DynamicTickrate { } const desiredFps = this.root.app.settings.getDesiredFps(); - this.setTickRate(Math_round(Math_max(desiredFps / 2, this.currentTickRate * 0.8))); + this.setTickRate(Math.round(Math.max(desiredFps / 2, this.currentTickRate * 0.8))); } /** @@ -85,7 +84,7 @@ export class DynamicTickrate { */ beginTick() { assert(this.currentTickStart === null, "BeginTick called twice"); - this.currentTickStart = performanceNow(); + this.currentTickStart = performance.now(); if (this.capturedTicks.length > this.currentTickRate * 2) { // Take only a portion of the ticks @@ -119,7 +118,7 @@ export class DynamicTickrate { */ endTick() { assert(this.currentTickStart !== null, "EndTick called without BeginTick"); - const duration = performanceNow() - this.currentTickStart; + const duration = performance.now() - this.currentTickStart; this.capturedTicks.push(duration); this.currentTickStart = null; } diff --git a/src/js/game/entity.js b/src/js/game/entity.js index 9dea1c2b..ece7d644 100644 --- a/src/js/game/entity.js +++ b/src/js/game/entity.js @@ -10,7 +10,6 @@ import { BasicSerializableObject, types } from "../savegame/serialization"; import { EntityComponentStorage } from "./entity_components"; import { Loader } from "../core/loader"; import { drawRotatedSprite } from "../core/draw_utils"; -import { Math_radians } from "../core/builtins"; import { gComponentRegistry } from "../core/global_registries"; export class Entity extends BasicSerializableObject { @@ -166,7 +165,7 @@ export class Entity extends BasicSerializableObject { const slotTile = staticComp.localTileToWorld(slot.pos); const direction = staticComp.localDirectionToWorld(slot.direction); const directionVector = enumDirectionToVector[direction]; - const angle = Math_radians(enumDirectionToAngle[direction]); + const angle = Math.radians(enumDirectionToAngle[direction]); context.globalAlpha = slot.item ? 1 : 0.2; drawRotatedSprite({ @@ -189,7 +188,7 @@ export class Entity extends BasicSerializableObject { for (let k = 0; k < slot.directions.length; ++k) { const direction = staticComp.localDirectionToWorld(slot.directions[k]); const directionVector = enumDirectionToVector[direction]; - const angle = Math_radians(enumDirectionToAngle[direction] + 180); + const angle = Math.radians(enumDirectionToAngle[direction] + 180); context.globalAlpha = 0.4; drawRotatedSprite({ parameters, diff --git a/src/js/game/entity_components.js b/src/js/game/entity_components.js index bcc6e9d0..fc29ce80 100644 --- a/src/js/game/entity_components.js +++ b/src/js/game/entity_components.js @@ -10,6 +10,8 @@ import { UndergroundBeltComponent } from "./components/underground_belt"; import { UnremovableComponent } from "./components/unremovable"; import { HubComponent } from "./components/hub"; import { StorageComponent } from "./components/storage"; +import { EnergyGeneratorComponent } from "./components/energy_generator"; +import { WiredPinsComponent } from "./components/wired_pins"; /* typehints:end */ /** @@ -56,6 +58,12 @@ export class EntityComponentStorage { /** @type {StorageComponent} */ this.Storage; + /** @type {EnergyGeneratorComponent} */ + this.EnergyGenerator; + + /** @type {WiredPinsComponent} */ + this.WiredPins; + /* typehints:end */ } } diff --git a/src/js/game/game_system_manager.js b/src/js/game/game_system_manager.js index 20d74dda..144380b2 100644 --- a/src/js/game/game_system_manager.js +++ b/src/js/game/game_system_manager.js @@ -13,6 +13,8 @@ import { HubSystem } from "./systems/hub"; import { StaticMapEntitySystem } from "./systems/static_map_entity"; import { ItemAcceptorSystem } from "./systems/item_acceptor"; import { StorageSystem } from "./systems/storage"; +import { EnergyGeneratorSystem } from "./systems/energy_generator"; +import { WiredPinsSystem } from "./systems/wired_pins"; const logger = createLogger("game_system_manager"); @@ -56,6 +58,12 @@ export class GameSystemManager { /** @type {StorageSystem} */ storage: null, + /** @type {EnergyGeneratorSystem} */ + energyGenerator: null, + + /** @type {WiredPinsSystem} */ + wiredPins: null, + /* typehints:end */ }; this.systemUpdateOrder = []; @@ -90,8 +98,12 @@ export class GameSystemManager { add("hub", HubSystem); + add("energyGenerator", EnergyGeneratorSystem); + add("staticMapEntities", StaticMapEntitySystem); + add("wiredPins", WiredPinsSystem); + // IMPORTANT: Must be after belt system since belt system can change the // orientation of an entity after it is placed -> the item acceptor cache // then would be invalid diff --git a/src/js/game/game_system_with_filter.js b/src/js/game/game_system_with_filter.js index d1fddc7f..d2b116df 100644 --- a/src/js/game/game_system_with_filter.js +++ b/src/js/game/game_system_with_filter.js @@ -8,8 +8,6 @@ import { GameSystem } from "./game_system"; import { arrayDelete, arrayDeleteValue } from "../core/utils"; import { DrawParameters } from "../core/draw_parameters"; import { globalConfig } from "../core/config"; -import { Math_floor, Math_ceil } from "../core/builtins"; - export class GameSystemWithFilter extends GameSystem { /** * Constructs a new game system with the given component filter. It will process @@ -71,11 +69,11 @@ export class GameSystemWithFilter extends GameSystem { let seenUids = new Set(); - const chunkStartX = Math_floor(minX / globalConfig.mapChunkSize); - const chunkStartY = Math_floor(minY / globalConfig.mapChunkSize); + const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); + const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); - const chunkEndX = Math_ceil(maxX / globalConfig.mapChunkSize); - const chunkEndY = Math_ceil(maxY / globalConfig.mapChunkSize); + const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); + const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); const requiredComponents = this.requiredComponentIds; diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index 9f65dc4c..3f4f1cf3 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -1,4 +1,3 @@ -import { Math_random } from "../core/builtins"; import { globalConfig } from "../core/config"; import { queryParamOptions } from "../core/query_parameters"; import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils"; @@ -53,6 +52,17 @@ export class HubGoals extends BasicSerializableObject { } this.upgradeImprovements[upgradeId] = totalImprovement; } + + // Compute current goal + const goal = tutorialGoals[this.level - 1]; + if (goal) { + this.currentGoal = { + /** @type {ShapeDefinition} */ + definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(goal.shape), + required: goal.required, + reward: goal.reward, + }; + } } /** @@ -202,7 +212,7 @@ export class HubGoals extends BasicSerializableObject { this.currentGoal = { /** @type {ShapeDefinition} */ definition: this.createRandomShape(), - required: 1000 + findNiceIntegerValue(this.level * 47.5), + required: 10000 + findNiceIntegerValue(this.level * 2000), reward: enumHubGoalRewards.no_reward_freeplay, }; } @@ -313,7 +323,7 @@ export class HubGoals extends BasicSerializableObject { * @returns {ShapeDefinition} */ createRandomShape() { - const layerCount = clamp(this.level / 50, 2, 4); + const layerCount = clamp(this.level / 25, 2, 4); /** @type {Array} */ let layers = []; @@ -336,14 +346,14 @@ export class HubGoals extends BasicSerializableObject { } // Sometimes shapes are missing - if (Math_random() > 0.85) { + if (Math.random() > 0.85) { layer[randomInt(0, 3)] = null; } // Sometimes they actually are missing *two* ones! // Make sure at max only one layer is missing it though, otherwise we could // create an uncreateable shape - if (Math_random() > 0.95 && !anyIsMissingTwo) { + if (Math.random() > 0.95 && !anyIsMissingTwo) { layer[randomInt(0, 3)] = null; anyIsMissingTwo = true; } diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index efbde2ab..1bbc2041 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -36,6 +36,8 @@ import { HUDInteractiveTutorial } from "./parts/interactive_tutorial"; import { HUDScreenshotExporter } from "./parts/screenshot_exporter"; import { HUDColorBlindHelper } from "./parts/color_blind_helper"; import { HUDShapeViewer } from "./parts/shape_viewer"; +import { HUDWiresOverlay } from "./parts/wires_overlay"; +import { HUDChangesDebugger } from "./parts/debug_changes"; export class GameHUD { /** @@ -70,6 +72,15 @@ export class GameHUD { dialogs: new HUDModalDialogs(this.root), screenshotExporter: new HUDScreenshotExporter(this.root), shapeViewer: new HUDShapeViewer(this.root), + + // WIRES + // wiresOverlay: new HUDWiresOverlay(this.root), + + // Typing hints + /* typehints:start */ + /** @type {HUDChangesDebugger} */ + changesDebugger: null, + /* typehints:end */ }; this.signals = { @@ -94,6 +105,10 @@ export class GameHUD { this.parts.watermark = new HUDWatermark(this.root); } + if (G_IS_DEV && globalConfig.debug.renderChanges) { + this.parts.changesDebugger = new HUDChangesDebugger(this.root); + } + if (this.root.app.settings.getAllSettings().offerHints) { this.parts.tutorialHints = new HUDPartTutorialHints(this.root); this.parts.interactiveTutorial = new HUDInteractiveTutorial(this.root); @@ -221,6 +236,7 @@ export class GameHUD { "buildingPlacer", "blueprintPlacer", "colorBlindHelper", + "changesDebugger", ]; for (let i = 0; i < partsOrder.length; ++i) { diff --git a/src/js/game/hud/parts/base_toolbar.js b/src/js/game/hud/parts/base_toolbar.js new file mode 100644 index 00000000..ca24f376 --- /dev/null +++ b/src/js/game/hud/parts/base_toolbar.js @@ -0,0 +1,172 @@ +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { Signal, STOP_PROPAGATION } from "../../../core/signal"; +import { TrackedState } from "../../../core/tracked_state"; +import { makeDiv } from "../../../core/utils"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { MetaBuilding } from "../../meta_building"; +import { BaseHUDPart } from "../base_hud_part"; +import { GameRoot } from "../../root"; + +export class HUDBaseToolbar extends BaseHUDPart { + /** + * @param {GameRoot} root + * @param {Array} supportedBuildings + * @param {function} visibilityCondition + */ + constructor(root, supportedBuildings, visibilityCondition) { + super(root); + + this.supportedBuildings = supportedBuildings; + this.visibilityCondition = visibilityCondition; + + /** @type {Object.} */ + this.buildingHandles = {}; + + this.sigBuildingSelected = new Signal(); + this.trackedIsVisisible = new TrackedState(this.onVisibilityChanged, this); + } + + /** + * Called when the visibility of the toolbar changed + * @param {boolean} visible + */ + onVisibilityChanged(visible) { + this.element.classList.toggle("visible", visible); + } + + /** + * Should create all require elements + * @param {HTMLElement} parent + */ + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_buildings_toolbar", ["ingame_buildingsToolbar"], ""); + } + + initialize() { + const actionMapper = this.root.keyMapper; + + const items = makeDiv(this.element, null, ["buildings"]); + + for (let i = 0; i < this.supportedBuildings.length; ++i) { + const metaBuilding = gMetaBuildingRegistry.findByClass(this.supportedBuildings[i]); + const binding = actionMapper.getBinding(KEYMAPPINGS.buildings[metaBuilding.getId()]); + + const itemContainer = makeDiv(items, null, ["building"]); + itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png"); + + binding.add(() => this.selectBuildingForPlacement(metaBuilding)); + + this.trackClicks(itemContainer, () => this.selectBuildingForPlacement(metaBuilding), { + clickSound: null, + }); + + this.buildingHandles[metaBuilding.id] = { + metaBuilding, + element: itemContainer, + unlocked: false, + selected: false, + index: i, + }; + } + + this.root.hud.signals.selectedPlacementBuildingChanged.add( + this.onSelectedPlacementBuildingChanged, + this + ); + + this.lastSelectedIndex = 0; + actionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildings).add(this.cycleBuildings, this); + } + + /** + * Updates the toolbar + */ + update() { + this.trackedIsVisisible.set(this.visibilityCondition()); + + if (!this.trackedIsVisisible.get()) { + // Currently not active + } else { + for (const buildingId in this.buildingHandles) { + const handle = this.buildingHandles[buildingId]; + const newStatus = handle.metaBuilding.getIsUnlocked(this.root); + if (handle.unlocked !== newStatus) { + handle.unlocked = newStatus; + handle.element.classList.toggle("unlocked", newStatus); + } + } + } + } + + /** + * Cycles through all buildings + */ + cycleBuildings() { + let newIndex = this.lastSelectedIndex; + for (let i = 0; i < this.supportedBuildings.length; ++i, ++newIndex) { + newIndex %= this.supportedBuildings.length; + const metaBuilding = gMetaBuildingRegistry.findByClass(this.supportedBuildings[newIndex]); + const handle = this.buildingHandles[metaBuilding.id]; + if (!handle.selected && handle.unlocked) { + break; + } + } + const metaBuildingClass = this.supportedBuildings[newIndex]; + const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass); + this.selectBuildingForPlacement(metaBuilding); + } + + /** + * Called when the selected building got changed + * @param {MetaBuilding} metaBuilding + */ + onSelectedPlacementBuildingChanged(metaBuilding) { + for (const buildingId in this.buildingHandles) { + const handle = this.buildingHandles[buildingId]; + const newStatus = handle.metaBuilding === metaBuilding; + if (handle.selected !== newStatus) { + handle.selected = newStatus; + handle.element.classList.toggle("selected", newStatus); + } + if (handle.selected) { + this.lastSelectedIndex = handle.index; + } + } + + this.element.classList.toggle("buildingSelected", !!metaBuilding); + } + + /** + * @param {MetaBuilding} metaBuilding + */ + selectBuildingForPlacement(metaBuilding) { + if (!this.visibilityCondition()) { + // Not active + return; + } + + if (!metaBuilding.getIsUnlocked(this.root)) { + this.root.soundProxy.playUiError(); + return STOP_PROPAGATION; + } + + // Allow clicking an item again to deselect it + for (const buildingId in this.buildingHandles) { + const handle = this.buildingHandles[buildingId]; + if (handle.selected && handle.metaBuilding === metaBuilding) { + metaBuilding = null; + break; + } + } + + this.root.soundProxy.playUiClick(); + this.sigBuildingSelected.dispatch(metaBuilding); + this.onSelectedPlacementBuildingChanged(metaBuilding); + } +} diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index c98fbf2d..6b2af42e 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -9,7 +9,7 @@ import { KEYMAPPINGS } from "../../key_action_mapper"; import { blueprintShape } from "../../upgrades"; import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { Blueprint } from "./blueprint"; +import { Blueprint } from "../../blueprint"; import { SOUNDS } from "../../../platform/sound"; export class HUDBlueprintPlacer extends BaseHUDPart { diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index f0bbcdae..95239a84 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -1,4 +1,3 @@ -import { Math_radians } from "../../../core/builtins"; import { globalConfig } from "../../../core/config"; import { DrawParameters } from "../../../core/draw_parameters"; import { drawRotatedSprite } from "../../../core/draw_utils"; @@ -368,11 +367,11 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { sprite: this.lockIndicatorSprite, x: worldPos.x, y: worldPos.y, - angle: Math_radians(rotation), + angle: Math.radians(rotation), size: 12, offsetY: -globalConfig.halfTileSize - - clamp((this.root.time.now() * 1.5) % 1.0, 0, 1) * 1 * globalConfig.tileSize + + clamp((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) * 1 * globalConfig.tileSize + globalConfig.halfTileSize, }); } @@ -432,7 +431,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { sprite, x: acceptorSlotWsPos.x, y: acceptorSlotWsPos.y, - angle: Math_radians(enumDirectionToAngle[enumInvertedDirections[worldDirection]]), + angle: Math.radians(enumDirectionToAngle[enumInvertedDirections[worldDirection]]), size: 13, offsetY: offsetShift + 13, }); @@ -478,7 +477,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { sprite, x: ejectorSLotWsPos.x, y: ejectorSLotWsPos.y, - angle: Math_radians(enumDirectionToAngle[ejectorSlotWsDirection]), + angle: Math.radians(enumDirectionToAngle[ejectorSlotWsDirection]), size: 13, offsetY: offsetShift, }); diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index c8c83acc..7d2cbb71 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -1,4 +1,3 @@ -import { Math_abs, Math_degrees, Math_round } from "../../../core/builtins"; import { globalConfig } from "../../../core/config"; import { gMetaBuildingRegistry } from "../../../core/global_registries"; import { Signal, STOP_PROPAGATION } from "../../../core/signal"; @@ -13,6 +12,7 @@ import { BaseHUDPart } from "../base_hud_part"; import { SOUNDS } from "../../../platform/sound"; import { MetaMinerBuilding, enumMinerVariants } from "../../buildings/miner"; import { enumHubGoalRewards } from "../../tutorial_goals"; +import { enumEditMode } from "../../root"; /** * Contains all logic for the building placer - this doesn't include the rendering @@ -115,6 +115,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this); this.root.signals.storyGoalCompleted.add(() => this.signals.variantChanged.dispatch()); this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch()); + this.root.signals.editModeChanged.add(this.onEditModeChanged, this); // MOUSE BINDINGS this.root.camera.downPreHandler.add(this.onMouseDown, this); @@ -122,6 +123,20 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { this.root.camera.upPostHandler.add(this.onMouseUp, this); } + /** + * Called when the edit mode got changed + * @param {enumEditMode} editMode + */ + onEditModeChanged(editMode) { + const metaBuilding = this.currentMetaBuilding.get(); + if (metaBuilding) { + if (metaBuilding.getEditLayer() !== editMode) { + // This layer doesn't fit the edit mode anymore + this.currentMetaBuilding.set(null); + } + } + } + /** * Returns the current base rotation for the current meta-building. * @returns {number} @@ -500,15 +515,32 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { * releasing the mouse */ executeDirectionLockedPlacement() { + const metaBuilding = this.currentMetaBuilding.get(); + if (!metaBuilding) { + // No active building + return; + } + + // Get path to place const path = this.computeDirectionLockPath(); + + // Store if we placed anything + let anythingPlaced = false; + + // Perform this in bulk to avoid recalculations this.root.logic.performBulkOperation(() => { for (let i = 0; i < path.length; ++i) { const { rotation, tile } = path[i]; - this.currentBaseRotation = rotation; - this.tryPlaceCurrentBuildingAt(tile); + if (this.tryPlaceCurrentBuildingAt(tile)) { + anythingPlaced = true; + } } }); + + if (anythingPlaced) { + this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); + } } /** @@ -532,10 +564,10 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { // Place from start to corner const pathToCorner = this.currentDirectionLockCorner.sub(startTile); const deltaToCorner = pathToCorner.normalize().round(); - const lengthToCorner = Math_round(pathToCorner.length()); + const lengthToCorner = Math.round(pathToCorner.length()); let currentPos = startTile.copy(); - let rotation = (Math.round(Math_degrees(deltaToCorner.angle()) / 90) * 90 + 360) % 360; + let rotation = (Math.round(Math.degrees(deltaToCorner.angle()) / 90) * 90 + 360) % 360; if (lengthToCorner > 0) { for (let i = 0; i < lengthToCorner; ++i) { @@ -550,10 +582,10 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { // Place from corner to end const pathFromCorner = mouseTile.sub(this.currentDirectionLockCorner); const deltaFromCorner = pathFromCorner.normalize().round(); - const lengthFromCorner = Math_round(pathFromCorner.length()); + const lengthFromCorner = Math.round(pathFromCorner.length()); if (lengthFromCorner > 0) { - rotation = (Math.round(Math_degrees(deltaFromCorner.angle()) / 90) * 90 + 360) % 360; + rotation = (Math.round(Math.degrees(deltaFromCorner.angle()) / 90) * 90 + 360) % 360; for (let i = 0; i < lengthFromCorner + 1; ++i) { result.push({ tile: currentPos.copy(), @@ -631,7 +663,9 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { // Place initial building, but only if direction lock is not active if (!this.isDirectionLockActive) { - this.tryPlaceCurrentBuildingAt(this.lastDragTile); + if (this.tryPlaceCurrentBuildingAt(this.lastDragTile)) { + this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); + } } return STOP_PROPAGATION; } @@ -687,7 +721,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { ).pressed ) { const delta = newPos.sub(oldPos); - const angleDeg = Math_degrees(delta.angle()); + const angleDeg = Math.degrees(delta.angle()); this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360; // Holding alt inverts the placement @@ -702,22 +736,31 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { let x1 = newPos.x; let y1 = newPos.y; - var dx = Math_abs(x1 - x0); - var dy = Math_abs(y1 - y0); + var dx = Math.abs(x1 - x0); + var dy = Math.abs(y1 - y0); var sx = x0 < x1 ? 1 : -1; var sy = y0 < y1 ? 1 : -1; var err = dx - dy; + let anythingPlaced = false; + let anythingDeleted = false; + while (this.currentlyDeleting || this.currentMetaBuilding.get()) { if (this.currentlyDeleting) { + // Deletion const contents = this.root.map.getTileContentXY(x0, y0); if (contents && !contents.queuedForDestroy && !contents.destroyed) { - this.root.logic.tryDeleteBuilding(contents); - this.root.soundProxy.playUi(SOUNDS.destroyBuilding); + if (this.root.logic.tryDeleteBuilding(contents)) { + anythingDeleted = true; + } } } else { - this.tryPlaceCurrentBuildingAt(new Vector(x0, y0)); + // Placement + if (this.tryPlaceCurrentBuildingAt(new Vector(x0, y0))) { + anythingPlaced = true; + } } + if (x0 === x1 && y0 === y1) break; var e2 = 2 * err; if (e2 > -dy) { @@ -729,6 +772,13 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { y0 += sy; } } + + if (anythingPlaced) { + this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); + } + if (anythingDeleted) { + this.root.soundProxy.playUi(SOUNDS.destroyBuilding); + } } this.lastDragTile = newPos; diff --git a/src/js/game/hud/parts/buildings_toolbar.js b/src/js/game/hud/parts/buildings_toolbar.js index 305d3eee..d9c34ffb 100644 --- a/src/js/game/hud/parts/buildings_toolbar.js +++ b/src/js/game/hud/parts/buildings_toolbar.js @@ -1,9 +1,6 @@ -import { gMetaBuildingRegistry } from "../../../core/global_registries"; -import { Signal } from "../../../core/signal"; -import { TrackedState } from "../../../core/tracked_state"; -import { makeDiv } from "../../../core/utils"; import { MetaBeltBaseBuilding } from "../../buildings/belt_base"; import { MetaCutterBuilding } from "../../buildings/cutter"; +import { MetaEnergyGenerator } from "../../buildings/energy_generator"; import { MetaMinerBuilding } from "../../buildings/miner"; import { MetaMixerBuilding } from "../../buildings/mixer"; import { MetaPainterBuilding } from "../../buildings/painter"; @@ -12,9 +9,8 @@ import { MetaSplitterBuilding } from "../../buildings/splitter"; import { MetaStackerBuilding } from "../../buildings/stacker"; import { MetaTrashBuilding } from "../../buildings/trash"; import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt"; -import { MetaBuilding } from "../../meta_building"; -import { BaseHUDPart } from "../base_hud_part"; -import { KEYMAPPINGS } from "../../key_action_mapper"; +import { enumEditMode } from "../../root"; +import { HUDBaseToolbar } from "./base_toolbar"; const toolbarBuildings = [ MetaBeltBaseBuilding, @@ -27,146 +23,17 @@ const toolbarBuildings = [ MetaMixerBuilding, MetaPainterBuilding, MetaTrashBuilding, + + // WIRES + // MetaEnergyGenerator, ]; -export class HUDBuildingsToolbar extends BaseHUDPart { +export class HUDBuildingsToolbar extends HUDBaseToolbar { constructor(root) { - super(root); - - /** @type {Object.} */ - this.buildingHandles = {}; - - this.sigBuildingSelected = new Signal(); - - this.trackedIsVisisible = new TrackedState(this.onVisibilityChanged, this); - } - - onVisibilityChanged(visible) { - this.element.classList.toggle("visible", visible); - } - - /** - * Should create all require elements - * @param {HTMLElement} parent - */ - createElements(parent) { - this.element = makeDiv(parent, "ingame_HUD_buildings_toolbar", [], ""); - } - - initialize() { - const actionMapper = this.root.keyMapper; - - const items = makeDiv(this.element, null, ["buildings"]); - - for (let i = 0; i < toolbarBuildings.length; ++i) { - const metaBuilding = gMetaBuildingRegistry.findByClass(toolbarBuildings[i]); - const binding = actionMapper.getBinding(KEYMAPPINGS.buildings[metaBuilding.getId()]); - - const itemContainer = makeDiv(items, null, ["building"]); - itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png"); - - binding.add(() => this.selectBuildingForPlacement(metaBuilding)); - - this.trackClicks(itemContainer, () => this.selectBuildingForPlacement(metaBuilding), { - clickSound: null, - }); - - this.buildingHandles[metaBuilding.id] = { - metaBuilding, - element: itemContainer, - unlocked: false, - selected: false, - index: i, - }; - } - - this.root.hud.signals.selectedPlacementBuildingChanged.add( - this.onSelectedPlacementBuildingChanged, - this + super( + root, + toolbarBuildings, + () => !this.root.camera.getIsMapOverlayActive() && this.root.editMode === enumEditMode.regular ); - - this.lastSelectedIndex = 0; - actionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildings).add(this.cycleBuildings, this); - } - - update() { - this.trackedIsVisisible.set(!this.root.camera.getIsMapOverlayActive()); - - for (const buildingId in this.buildingHandles) { - const handle = this.buildingHandles[buildingId]; - const newStatus = handle.metaBuilding.getIsUnlocked(this.root); - if (handle.unlocked !== newStatus) { - handle.unlocked = newStatus; - handle.element.classList.toggle("unlocked", newStatus); - } - } - } - - cycleBuildings() { - let newIndex = this.lastSelectedIndex; - for (let i = 0; i < toolbarBuildings.length; ++i, ++newIndex) { - newIndex %= toolbarBuildings.length; - const metaBuilding = gMetaBuildingRegistry.findByClass(toolbarBuildings[newIndex]); - const handle = this.buildingHandles[metaBuilding.id]; - if (!handle.selected && handle.unlocked) { - break; - } - } - const metaBuildingClass = toolbarBuildings[newIndex]; - const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass); - this.selectBuildingForPlacement(metaBuilding); - } - - /** - * @param {MetaBuilding} metaBuilding - */ - onSelectedPlacementBuildingChanged(metaBuilding) { - for (const buildingId in this.buildingHandles) { - const handle = this.buildingHandles[buildingId]; - const newStatus = handle.metaBuilding === metaBuilding; - if (handle.selected !== newStatus) { - handle.selected = newStatus; - handle.element.classList.toggle("selected", newStatus); - } - if (handle.selected) { - this.lastSelectedIndex = handle.index; - } - } - - this.element.classList.toggle("buildingSelected", !!metaBuilding); - } - - /** - * @param {MetaBuilding} metaBuilding - */ - selectBuildingForPlacement(metaBuilding) { - if (!metaBuilding.getIsUnlocked(this.root)) { - this.root.soundProxy.playUiError(); - return; - } - - if (this.root.camera.getIsMapOverlayActive()) { - this.root.soundProxy.playUiError(); - return; - } - - // Allow clicking an item again to deselect it - for (const buildingId in this.buildingHandles) { - const handle = this.buildingHandles[buildingId]; - if (handle.selected && handle.metaBuilding === metaBuilding) { - metaBuilding = null; - break; - } - } - - this.root.soundProxy.playUiClick(); - this.sigBuildingSelected.dispatch(metaBuilding); - this.onSelectedPlacementBuildingChanged(metaBuilding); } } diff --git a/src/js/game/hud/parts/color_blind_helper.js b/src/js/game/hud/parts/color_blind_helper.js index 4e6a0229..2eaca6a9 100644 --- a/src/js/game/hud/parts/color_blind_helper.js +++ b/src/js/game/hud/parts/color_blind_helper.js @@ -7,6 +7,7 @@ import { DrawParameters } from "../../../core/draw_parameters"; import { THEME } from "../../theme"; import { globalConfig } from "../../../core/config"; import { T } from "../../../translations"; +import { enumItemType } from "../../base_item"; export class HUDColorBlindHelper extends BaseHUDPart { createElements(parent) { @@ -48,9 +49,9 @@ export class HUDColorBlindHelper extends BaseHUDPart { // Check if the belt has a color item if (beltComp) { - const firstItem = beltComp.sortedItems[0]; - if (firstItem && firstItem[1] instanceof ColorItem) { - return firstItem[1].color; + const item = beltComp.assignedPath.findItemAtTile(tile); + if (item && item.getItemType() === enumItemType.color) { + return /** @type {ColorItem} */ (item).color; } } @@ -59,16 +60,16 @@ export class HUDColorBlindHelper extends BaseHUDPart { if (ejectorComp) { for (let i = 0; i < ejectorComp.slots.length; ++i) { const slot = ejectorComp.slots[i]; - if (slot.item && slot.item instanceof ColorItem) { - return slot.item.color; + if (slot.item && slot.item.getItemType() === enumItemType.color) { + return /** @type {ColorItem} */ (slot.item).color; } } } } else { // We hovered a lower layer, show the color there const lowerLayer = this.root.map.getLowerLayerContentXY(tile.x, tile.y); - if (lowerLayer && lowerLayer instanceof ColorItem) { - return lowerLayer.color; + if (lowerLayer && lowerLayer.getItemType() === enumItemType.color) { + return /** @type {ColorItem} */ (lowerLayer).color; } } diff --git a/src/js/game/hud/parts/debug_changes.js b/src/js/game/hud/parts/debug_changes.js new file mode 100644 index 00000000..1502afa2 --- /dev/null +++ b/src/js/game/hud/parts/debug_changes.js @@ -0,0 +1,78 @@ +import { globalConfig } from "../../../core/config"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { Rectangle } from "../../../core/rectangle"; +import { BaseHUDPart } from "../base_hud_part"; + +/** + * @typedef {{ + * label: string, + * area: Rectangle, + * hideAt: number, + * fillColor: string + * }} DebugChange + */ + +export class HUDChangesDebugger extends BaseHUDPart { + createElements(parent) {} + + initialize() { + /** @type {Array} */ + this.changes = []; + } + + /** + * Renders a new change + * @param {string} label Text to display + * @param {Rectangle} area Affected area world space + * @param {string} fillColor Color to display (Hex) + * @param {number=} timeToDisplay How long to display the change + */ + renderChange(label, area, fillColor, timeToDisplay = 0.3) { + this.changes.push({ + label, + area: area.clone(), + fillColor, + hideAt: this.root.time.realtimeNow() + timeToDisplay, + }); + } + + update() { + const now = this.root.time.realtimeNow(); + // Detect outdated changes + for (let i = 0; i < this.changes.length; ++i) { + const change = this.changes[i]; + if (change.hideAt <= now) { + this.changes.splice(i, 1); + i -= 1; + continue; + } + } + } + + /** + * + * @param {DrawParameters} parameters + */ + draw(parameters) { + for (let i = 0; i < this.changes.length; ++i) { + const change = this.changes[i]; + parameters.context.fillStyle = change.fillColor; + parameters.context.globalAlpha = 0.5; + parameters.context.fillRect( + change.area.x * globalConfig.tileSize, + change.area.y * globalConfig.tileSize, + change.area.w * globalConfig.tileSize, + change.area.h * globalConfig.tileSize + ); + + parameters.context.fillStyle = "#222"; + parameters.context.globalAlpha = 1; + parameters.context.font = "bold 8px GameFont"; + parameters.context.fillText( + change.label, + change.area.x * globalConfig.tileSize + 2, + change.area.y * globalConfig.tileSize + 12 + ); + } + } +} diff --git a/src/js/game/hud/parts/debug_info.js b/src/js/game/hud/parts/debug_info.js index 4f4c052e..46ec2dcf 100644 --- a/src/js/game/hud/parts/debug_info.js +++ b/src/js/game/hud/parts/debug_info.js @@ -1,6 +1,5 @@ import { BaseHUDPart } from "../base_hud_part"; import { makeDiv, round3Digits, round2Digits } from "../../../core/utils"; -import { Math_round } from "../../../core/builtins"; import { DynamicDomAttach } from "../dynamic_dom_attach"; import { KEYMAPPINGS } from "../../key_action_mapper"; @@ -34,7 +33,7 @@ export class HUDDebugInfo extends BaseHUDPart { this.tickRateElement.innerText = "Tickrate: " + this.root.dynamicTickrate.currentTickRate; this.fpsElement.innerText = "FPS: " + - Math_round(this.root.dynamicTickrate.averageFps) + + Math.round(this.root.dynamicTickrate.averageFps) + " (" + round2Digits(1000 / this.root.dynamicTickrate.averageFps) + " ms)"; diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index 63208b19..2603e011 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -254,6 +254,14 @@ export class HUDKeybindingOverlay extends BaseHUDPart { keys: [k.massSelect.massSelectCopy], condition: () => this.anythingSelectedOnMap, }, + + // WIRES + // { + // // Switch layers + // label: T.ingame.keybindingsOverlay.switchLayers, + // keys: [k.ingame.switchLayers], + // condition: () => true, + // }, ]; if (!this.root.app.settings.getAllSettings().alwaysMultiplace) { diff --git a/src/js/game/hud/parts/pinned_shapes.js b/src/js/game/hud/parts/pinned_shapes.js index 3f935a0b..bda49f1e 100644 --- a/src/js/game/hud/parts/pinned_shapes.js +++ b/src/js/game/hud/parts/pinned_shapes.js @@ -1,4 +1,3 @@ -import { Math_max } from "../../../core/builtins"; import { ClickDetector } from "../../../core/click_detector"; import { formatBigNumber, makeDiv, arrayDelete, arrayDeleteValue } from "../../../core/utils"; import { ShapeDefinition } from "../../shape_definition"; diff --git a/src/js/game/hud/parts/processing_overlay.js b/src/js/game/hud/parts/processing_overlay.js index 3354966a..95383dfd 100644 --- a/src/js/game/hud/parts/processing_overlay.js +++ b/src/js/game/hud/parts/processing_overlay.js @@ -1,6 +1,5 @@ import { DynamicDomAttach } from "../dynamic_dom_attach"; import { BaseHUDPart } from "../base_hud_part"; -import { performanceNow } from "../../../core/builtins"; import { makeDiv } from "../../../core/utils"; import { Signal } from "../../../core/signal"; import { InputReceiver } from "../../../core/input_receiver"; @@ -62,15 +61,15 @@ export class HUDProcessingOverlay extends BaseHUDPart { } processSync() { - const now = performanceNow(); + const now = performance.now(); while (this.tasks.length > 0) { const workload = this.tasks[0]; workload.call(); this.tasks.shift(); } - const duration = performanceNow() - now; + const duration = performance.now() - now; if (duration > 100) { - logger.log("Tasks done slow (SYNC!) within", (performanceNow() - now).toFixed(2), "ms"); + logger.log("Tasks done slow (SYNC!) within", (performance.now() - now).toFixed(2), "ms"); } } @@ -89,15 +88,15 @@ export class HUDProcessingOverlay extends BaseHUDPart { } this.computeTimeout = setTimeout(() => { - const now = performanceNow(); + const now = performance.now(); while (this.tasks.length > 0) { const workload = this.tasks[0]; workload.call(); this.tasks.shift(); } - const duration = performanceNow() - now; + const duration = performance.now() - now; if (duration > 100) { - logger.log("Tasks done slow within", (performanceNow() - now).toFixed(2), "ms"); + logger.log("Tasks done slow within", (performance.now() - now).toFixed(2), "ms"); } this.domWatcher.update(false); diff --git a/src/js/game/hud/parts/screenshot_exporter.js b/src/js/game/hud/parts/screenshot_exporter.js index 8623f902..19c644e9 100644 --- a/src/js/game/hud/parts/screenshot_exporter.js +++ b/src/js/game/hud/parts/screenshot_exporter.js @@ -5,7 +5,6 @@ import { T } from "../../../translations"; import { createLogger } from "../../../core/logging"; import { StaticMapEntityComponent } from "../../components/static_map_entity"; import { Vector } from "../../../core/vector"; -import { Math_max, Math_min, Math_floor } from "../../../core/builtins"; import { makeOffscreenBuffer } from "../../../core/buffer_utils"; import { DrawParameters } from "../../../core/draw_parameters"; import { Rectangle } from "../../../core/rectangle"; @@ -43,11 +42,11 @@ export class HUDScreenshotExporter extends BaseHUDPart { const maxTile = new Vector(0, 0); for (let i = 0; i < staticEntities.length; ++i) { const bounds = staticEntities[i].components.StaticMapEntity.getTileSpaceBounds(); - minTile.x = Math_min(minTile.x, bounds.x); - minTile.y = Math_min(minTile.y, bounds.y); + minTile.x = Math.min(minTile.x, bounds.x); + minTile.y = Math.min(minTile.y, bounds.y); - maxTile.x = Math_max(maxTile.x, bounds.x + bounds.w); - maxTile.y = Math_max(maxTile.y, bounds.y + bounds.h); + maxTile.x = Math.max(maxTile.x, bounds.x + bounds.w); + maxTile.y = Math.max(maxTile.y, bounds.y + bounds.h); } const minChunk = minTile.divideScalar(globalConfig.mapChunkSize).floor(); @@ -57,10 +56,10 @@ export class HUDScreenshotExporter extends BaseHUDPart { logger.log("Dimensions:", dimensions); let chunkSizePixels = 128; - const maxDimensions = Math_max(dimensions.x, dimensions.y); + const maxDimensions = Math.max(dimensions.x, dimensions.y); if (maxDimensions > 128) { - chunkSizePixels = Math_max(1, Math_floor(128 * (128 / maxDimensions))); + chunkSizePixels = Math.max(1, Math.floor(128 * (128 / maxDimensions))); } logger.log("ChunkSizePixels:", chunkSizePixels); diff --git a/src/js/game/hud/parts/shop.js b/src/js/game/hud/parts/shop.js index f83cb89b..f0f16cfe 100644 --- a/src/js/game/hud/parts/shop.js +++ b/src/js/game/hud/parts/shop.js @@ -1,4 +1,3 @@ -import { Math_min } from "../../../core/builtins"; import { ClickDetector } from "../../../core/click_detector"; import { InputReceiver } from "../../../core/input_receiver"; import { formatBigNumber, makeDiv } from "../../../core/utils"; @@ -178,7 +177,7 @@ export class HUDShop extends BaseHUDPart { const { progressLabel, progressBar, definition, required } = handle.requireIndexToElement[i]; const haveAmount = this.root.hubGoals.getShapesStored(definition); - const progress = Math_min(haveAmount / required, 1.0); + const progress = Math.min(haveAmount / required, 1.0); progressLabel.innerText = formatBigNumber(haveAmount) + " / " + formatBigNumber(required); progressBar.style.width = progress * 100.0 + "%"; diff --git a/src/js/game/hud/parts/statistics.js b/src/js/game/hud/parts/statistics.js index fc19b3fd..4a3b75f5 100644 --- a/src/js/game/hud/parts/statistics.js +++ b/src/js/game/hud/parts/statistics.js @@ -1,4 +1,3 @@ -import { Math_min } from "../../../core/builtins"; import { InputReceiver } from "../../../core/input_receiver"; import { makeButton, makeDiv, removeAllChildren, capitalizeFirstLetter } from "../../../core/utils"; import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; @@ -179,7 +178,7 @@ export class HUDStatistics extends BaseHUDPart { let rendered = new Set(); - for (let i = 0; i < Math_min(entries.length, 200); ++i) { + for (let i = 0; i < Math.min(entries.length, 200); ++i) { const entry = entries[i]; const shapeKey = entry[0]; diff --git a/src/js/game/hud/parts/statistics_handle.js b/src/js/game/hud/parts/statistics_handle.js index d3612d92..d5c60d3b 100644 --- a/src/js/game/hud/parts/statistics_handle.js +++ b/src/js/game/hud/parts/statistics_handle.js @@ -1,10 +1,10 @@ +import { makeOffscreenBuffer } from "../../../core/buffer_utils"; +import { globalConfig } from "../../../core/config"; +import { clamp, formatBigNumber, round2Digits } from "../../../core/utils"; +import { T } from "../../../translations"; +import { enumAnalyticsDataSource } from "../../production_analytics"; import { GameRoot } from "../../root"; import { ShapeDefinition } from "../../shape_definition"; -import { enumAnalyticsDataSource } from "../../production_analytics"; -import { formatBigNumber, clamp } from "../../../core/utils"; -import { globalConfig } from "../../../core/config"; -import { makeOffscreenBuffer } from "../../../core/buffer_utils"; -import { T } from "../../../translations"; /** @enum {string} */ export const enumDisplayMode = { @@ -91,6 +91,11 @@ export class HUDShapeStatisticsHandle { "", formatBigNumber(rate) ); + + if (G_IS_DEV && globalConfig.debug.detailedStatistics) { + this.counter.innerText = "" + round2Digits(rate / 60) + " /s"; + } + break; } } diff --git a/src/js/game/hud/parts/waypoints.js b/src/js/game/hud/parts/waypoints.js index 2472e57a..c7837d37 100644 --- a/src/js/game/hud/parts/waypoints.js +++ b/src/js/game/hud/parts/waypoints.js @@ -1,5 +1,4 @@ import { makeOffscreenBuffer } from "../../../core/buffer_utils"; -import { Math_max, Math_PI, Math_radians } from "../../../core/builtins"; import { globalConfig, IS_DEMO } from "../../../core/config"; import { DrawParameters } from "../../../core/draw_parameters"; import { Loader } from "../../../core/loader"; @@ -153,7 +152,16 @@ export class HUDWaypoints extends BaseHUDPart { if (ShapeDefinition.isValidShortKey(label)) { const canvas = this.getWaypointCanvas(waypoint); - element.appendChild(canvas); + /** + * Create a clone of the cached canvas, as calling appendElement when a canvas is + * already in the document will move the existing canvas to the new position. + */ + const [newCanvas, context] = makeOffscreenBuffer(48, 48, { + smooth: true, + label: label + "-waypoint-" + i, + }); + context.drawImage(canvas, 0, 0); + element.appendChild(newCanvas); element.classList.add("shapeIcon"); } else { element.innerText = label; @@ -261,7 +269,7 @@ export class HUDWaypoints extends BaseHUDPart { center: { x: position.x, y: position.y }, // Make sure the zoom is *just* a bit above the zoom level where the map overview // starts, so you always see all buildings - zoomLevel: Math_max(this.root.camera.zoomLevel, globalConfig.mapChunkOverviewMinZoom + 0.05), + zoomLevel: Math.max(this.root.camera.zoomLevel, globalConfig.mapChunkOverviewMinZoom + 0.05), }); // Sort waypoints by name @@ -410,7 +418,7 @@ export class HUDWaypoints extends BaseHUDPart { if (this.currentCompassOpacity > 0.01) { context.globalAlpha = this.currentCompassOpacity; - const angle = cameraPos.angle() + Math_radians(45) + Math_PI / 2; + const angle = cameraPos.angle() + Math.radians(45) + Math.PI / 2; context.translate(dims / 2, dims / 2); context.rotate(angle); this.directionIndicatorSprite.drawCentered(context, 0, 0, indicatorSize); diff --git a/src/js/game/hud/parts/wires_overlay.js b/src/js/game/hud/parts/wires_overlay.js new file mode 100644 index 00000000..0d228509 --- /dev/null +++ b/src/js/game/hud/parts/wires_overlay.js @@ -0,0 +1,83 @@ +import { makeOffscreenBuffer } from "../../../core/buffer_utils"; +import { globalConfig } from "../../../core/config"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { enumEditMode } from "../../root"; +import { THEME } from "../../theme"; +import { BaseHUDPart } from "../base_hud_part"; + +const wiresBackgroundDpi = 3; + +export class HUDWiresOverlay extends BaseHUDPart { + createElements(parent) {} + + initialize() { + // Probably not the best location, but the one which makes most sense + this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this); + + this.generateTilePattern(); + } + + /** + * Switches between layers + */ + switchLayers() { + if (this.root.editMode === enumEditMode.regular) { + this.root.editMode = enumEditMode.wires; + } else { + this.root.editMode = enumEditMode.regular; + } + this.root.signals.editModeChanged.dispatch(this.root.editMode); + } + + /** + * Generates the background pattern for the wires overlay + */ + generateTilePattern() { + const dims = globalConfig.tileSize * wiresBackgroundDpi; + const [canvas, context] = makeOffscreenBuffer(dims, dims, { + smooth: false, + reusable: false, + label: "wires-tile-pattern", + }); + + context.scale(wiresBackgroundDpi, wiresBackgroundDpi); + context.fillStyle = THEME.map.wires.overlay; + context.fillRect(0, 0, globalConfig.tileSize, globalConfig.tileSize); + + const lineWidth = 1; + + context.fillRect(0, 0, globalConfig.tileSize, lineWidth); + context.fillRect(0, lineWidth, lineWidth, globalConfig.tileSize); + + this.tilePatternCanvas = canvas; + } + + /** + * + * @param {DrawParameters} parameters + */ + draw(parameters) { + if (this.root.editMode !== enumEditMode.wires) { + return; + } + + if (!this.cachedPatternBackground) { + this.cachedPatternBackground = parameters.context.createPattern(this.tilePatternCanvas, "repeat"); + } + + const bounds = parameters.visibleRect; + + const scaleFactor = 1 / wiresBackgroundDpi; + + parameters.context.scale(scaleFactor, scaleFactor); + parameters.context.fillStyle = this.cachedPatternBackground; + parameters.context.fillRect( + bounds.x / scaleFactor, + bounds.y / scaleFactor, + bounds.w / scaleFactor, + bounds.h / scaleFactor + ); + parameters.context.scale(1 / scaleFactor, 1 / scaleFactor); + } +} diff --git a/src/js/game/hud/trailer_maker.js b/src/js/game/hud/trailer_maker.js index 72509b93..8655def4 100644 --- a/src/js/game/hud/trailer_maker.js +++ b/src/js/game/hud/trailer_maker.js @@ -1,7 +1,6 @@ import { GameRoot } from "../root"; import { globalConfig } from "../../core/config"; import { Vector, mixVector } from "../../core/vector"; -import { performanceNow } from "../../core/builtins"; import { lerp } from "../../core/utils"; /* dev:start */ @@ -87,7 +86,7 @@ export class TrailerMaker { if (!nextMarker.startTime) { console.log("Starting to approach", nextMarker.pos); - nextMarker.startTime = performanceNow() / 1000.0; + nextMarker.startTime = performance.now() / 1000.0; } const speed = @@ -98,7 +97,7 @@ export class TrailerMaker { // this.currentPlaybackOrigin.distance(Vector.fromSerializedObject(nextMarker.pos)) / speed; const time = nextMarker.time; - const progress = (performanceNow() / 1000.0 - nextMarker.startTime) / time; + const progress = (performance.now() / 1000.0 - nextMarker.startTime) / time; if (progress > 1.0) { if (nextMarker.wait > 0) { diff --git a/src/js/game/items/color_item.js b/src/js/game/items/color_item.js index b2a3cd74..d1b9a217 100644 --- a/src/js/game/items/color_item.js +++ b/src/js/game/items/color_item.js @@ -2,7 +2,7 @@ import { globalConfig } from "../../core/config"; import { smoothenDpi } from "../../core/dpi_manager"; import { DrawParameters } from "../../core/draw_parameters"; import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; +import { BaseItem, enumItemType } from "../base_item"; import { enumColors, enumColorsToHexCode } from "../colors"; import { THEME } from "../theme"; @@ -23,6 +23,10 @@ export class ColorItem extends BaseItem { this.color = data; } + getItemType() { + return enumItemType.color; + } + /** * @param {enumColors} color */ diff --git a/src/js/game/items/shape_item.js b/src/js/game/items/shape_item.js index cfdb9830..2aca63f6 100644 --- a/src/js/game/items/shape_item.js +++ b/src/js/game/items/shape_item.js @@ -1,6 +1,6 @@ import { DrawParameters } from "../../core/draw_parameters"; import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; +import { BaseItem, enumItemType } from "../base_item"; import { ShapeDefinition } from "../shape_definition"; import { THEME } from "../theme"; @@ -21,6 +21,10 @@ export class ShapeItem extends BaseItem { this.definition = ShapeDefinition.fromShortKey(data); } + getItemType() { + return enumItemType.shape; + } + /** * @param {ShapeDefinition} definition */ diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index c514446d..75a0ef6e 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -7,8 +7,6 @@ import { Application } from "../application"; import { Signal, STOP_PROPAGATION } from "../core/signal"; import { IS_MOBILE } from "../core/config"; import { T } from "../translations"; -import { JSON_stringify } from "../core/builtins"; - function key(str) { return str.toUpperCase().charCodeAt(0); } @@ -26,6 +24,9 @@ export const KEYMAPPINGS = { toggleHud: { keyCode: 113 }, // F2 exportScreenshot: { keyCode: 114 }, // F3PS toggleFPSInfo: { keyCode: 115 }, // F4 + + // WIRES + // switchLayers: { keyCode: key("Y") }, }, navigation: { @@ -53,6 +54,9 @@ export const KEYMAPPINGS = { mixer: { keyCode: key("8") }, painter: { keyCode: key("9") }, trash: { keyCode: key("0") }, + + // WIRES + // energy_generator: { keyCode: key("O") }, }, placement: { @@ -440,7 +444,7 @@ export class KeyActionMapper { getBinding(binding) { // @ts-ignore const id = binding.id; - assert(id, "Not a valid keybinding: " + JSON_stringify(binding)); + assert(id, "Not a valid keybinding: " + JSON.stringify(binding)); assert(this.keybindings[id], "Keybinding " + id + " not known!"); return this.keybindings[id]; } diff --git a/src/js/game/logic.js b/src/js/game/logic.js index 408bf89d..5277f608 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -3,7 +3,6 @@ import { Entity } from "./entity"; import { Vector, enumDirectionToVector, enumDirection } from "../core/vector"; import { MetaBuilding } from "./meta_building"; import { StaticMapEntityComponent } from "./components/static_map_entity"; -import { Math_abs, performanceNow } from "../core/builtins"; import { createLogger } from "../core/logging"; import { MetaBeltBaseBuilding, arrayBeltVariantToRotation } from "./buildings/belt_base"; import { SOUNDS } from "../platform/sound"; @@ -186,7 +185,6 @@ export class GameLogic { variant, }); - this.root.soundProxy.playUi(building.getPlacementSound()); return entity; } return null; @@ -197,12 +195,12 @@ export class GameLogic { * @param {function} operation */ performBulkOperation(operation) { - logger.log("Running bulk operation ..."); + logger.warn("Running bulk operation ..."); assert(!this.root.bulkOperationRunning, "Can not run two bulk operations twice"); this.root.bulkOperationRunning = true; - const now = performanceNow(); + const now = performance.now(); const returnValue = operation(); - const duration = performanceNow() - now; + const duration = performance.now() - now; logger.log("Done in", round2Digits(duration), "ms"); assert(this.root.bulkOperationRunning, "Bulk operation = false while bulk operation was running"); this.root.bulkOperationRunning = false; @@ -228,6 +226,7 @@ export class GameLogic { } this.root.map.removeStaticEntity(building); this.root.entityMgr.destroyEntity(building); + this.root.entityMgr.processDestroyList(); return true; } @@ -244,7 +243,7 @@ export class GameLogic { for (let dx = -1; dx <= 1; ++dx) { for (let dy = -1; dy <= 1; ++dy) { - if (Math_abs(dx) + Math_abs(dy) !== 1) { + if (Math.abs(dx) + Math.abs(dy) !== 1) { continue; } diff --git a/src/js/game/map.js b/src/js/game/map.js index ef745c6d..5bdd22e4 100644 --- a/src/js/game/map.js +++ b/src/js/game/map.js @@ -5,7 +5,6 @@ import { GameRoot } from "./root"; import { globalConfig } from "../core/config"; import { Vector } from "../core/vector"; import { Entity } from "./entity"; -import { Math_floor } from "../core/builtins"; import { createLogger } from "../core/logging"; import { BaseItem } from "./base_item"; import { MapChunkView } from "./map_chunk_view"; @@ -71,8 +70,8 @@ export class BaseMap extends BasicSerializableObject { * @returns {MapChunkView} */ getOrCreateChunkAtTile(tileX, tileY) { - const chunkX = Math_floor(tileX / globalConfig.mapChunkSize); - const chunkY = Math_floor(tileY / globalConfig.mapChunkSize); + const chunkX = Math.floor(tileX / globalConfig.mapChunkSize); + const chunkY = Math.floor(tileY / globalConfig.mapChunkSize); return this.getChunk(chunkX, chunkY, true); } @@ -83,8 +82,8 @@ export class BaseMap extends BasicSerializableObject { * @returns {MapChunkView?} */ getChunkAtTileOrNull(tileX, tileY) { - const chunkX = Math_floor(tileX / globalConfig.mapChunkSize); - const chunkY = Math_floor(tileY / globalConfig.mapChunkSize); + const chunkX = Math.floor(tileX / globalConfig.mapChunkSize); + const chunkY = Math.floor(tileY / globalConfig.mapChunkSize); return this.getChunk(chunkX, chunkY, false); } diff --git a/src/js/game/map_chunk.js b/src/js/game/map_chunk.js index bffaf9e8..2ec781a2 100644 --- a/src/js/game/map_chunk.js +++ b/src/js/game/map_chunk.js @@ -2,7 +2,6 @@ import { GameRoot } from "./root"; /* typehints:end */ -import { Math_ceil, Math_max, Math_min, Math_round } from "../core/builtins"; import { globalConfig } from "../core/config"; import { createLogger } from "../core/logging"; import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "../core/utils"; @@ -66,7 +65,7 @@ export class MapChunk { * @param {number=} overrideY Override the Y position of the patch */ internalGeneratePatch(rng, patchSize, item, overrideX = null, overrideY = null) { - const border = Math_ceil(patchSize / 2 + 3); + const border = Math.ceil(patchSize / 2 + 3); // Find a position within the chunk which is not blocked let patchX = rng.nextIntRange(border, globalConfig.mapChunkSize - border - 1); @@ -88,7 +87,7 @@ export class MapChunk { for (let i = 0; i <= numCircles; ++i) { // Determine circle parameters - const circleRadius = Math_min(1 + i, patchSize); + const circleRadius = Math.min(1 + i, patchSize); const circleRadiusSquare = circleRadius * circleRadius; const circleOffsetRadius = (numCircles - i) / 2 + 2; @@ -101,8 +100,8 @@ export class MapChunk { for (let dx = -circleRadius * circleScaleX - 2; dx <= circleRadius * circleScaleX + 2; ++dx) { for (let dy = -circleRadius * circleScaleY - 2; dy <= circleRadius * circleScaleY + 2; ++dy) { - const x = Math_round(circleX + dx); - const y = Math_round(circleY + dy); + const x = Math.round(circleX + dx); + const y = Math.round(circleY + dy); if (x >= 0 && x < globalConfig.mapChunkSize && y >= 0 && y <= globalConfig.mapChunkSize) { const originalDx = dx / circleScaleX; const originalDy = dy / circleScaleY; @@ -158,9 +157,9 @@ export class MapChunk { // Later there is a mix of everything weights = { [enumSubShape.rect]: 100, - [enumSubShape.circle]: Math_round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)), - [enumSubShape.star]: Math_round(20 + clamp(distanceToOriginInChunks, 0, 30)), - [enumSubShape.windmill]: Math_round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)), + [enumSubShape.circle]: Math.round(50 + clamp(distanceToOriginInChunks * 2, 0, 50)), + [enumSubShape.star]: Math.round(20 + clamp(distanceToOriginInChunks, 0, 30)), + [enumSubShape.windmill]: Math.round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)), }; if (distanceToOriginInChunks < 7) { @@ -239,20 +238,20 @@ export class MapChunk { } const chunkCenter = new Vector(this.x, this.y).addScalar(0.5); - const distanceToOriginInChunks = Math_round(chunkCenter.length()); + const distanceToOriginInChunks = Math.round(chunkCenter.length()); // Determine how likely it is that there is a color patch const colorPatchChance = 0.9 - clamp(distanceToOriginInChunks / 25, 0, 1) * 0.5; if (rng.next() < colorPatchChance / 4) { - const colorPatchSize = Math_max(2, Math_round(1 + clamp(distanceToOriginInChunks / 8, 0, 4))); + const colorPatchSize = Math.max(2, Math.round(1 + clamp(distanceToOriginInChunks / 8, 0, 4))); this.internalGenerateColorPatch(rng, colorPatchSize, distanceToOriginInChunks); } // Determine how likely it is that there is a shape patch const shapePatchChance = 0.9 - clamp(distanceToOriginInChunks / 25, 0, 1) * 0.5; if (rng.next() < shapePatchChance / 4) { - const shapePatchSize = Math_max(2, Math_round(1 + clamp(distanceToOriginInChunks / 8, 0, 4))); + const shapePatchSize = Math.max(2, Math.round(1 + clamp(distanceToOriginInChunks / 8, 0, 4))); this.internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks); } } diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index cc0734d8..6bdbd100 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -3,7 +3,6 @@ import { GameRoot } from "./root"; import { globalConfig } from "../core/config"; import { DrawParameters } from "../core/draw_parameters"; import { round1Digit } from "../core/utils"; -import { Math_max, Math_round } from "../core/builtins"; import { Rectangle } from "../core/rectangle"; import { createLogger } from "../core/logging"; import { smoothenDpi } from "../core/dpi_manager"; diff --git a/src/js/game/map_view.js b/src/js/game/map_view.js index 5c4bf88a..48c1c502 100644 --- a/src/js/game/map_view.js +++ b/src/js/game/map_view.js @@ -1,4 +1,3 @@ -import { Math_max, Math_min, Math_floor, Math_ceil } from "../core/builtins"; import { globalConfig } from "../core/config"; import { DrawParameters } from "../core/draw_parameters"; import { BaseMap } from "./map"; @@ -142,11 +141,11 @@ export class MapView extends BaseMap { const minX = left - border; const maxX = right + border - 1; - const chunkStartX = Math_floor(minX / globalConfig.mapChunkSize); - const chunkStartY = Math_floor(minY / globalConfig.mapChunkSize); + const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); + const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); - const chunkEndX = Math_ceil(maxX / globalConfig.mapChunkSize); - const chunkEndY = Math_ceil(maxY / globalConfig.mapChunkSize); + const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); + const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); // Render y from top down for proper blending for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { @@ -196,11 +195,11 @@ export class MapView extends BaseMap { const minX = left - border; const maxX = right + border - 1; - const chunkStartX = Math_floor(minX / globalConfig.mapChunkSize); - const chunkStartY = Math_floor(minY / globalConfig.mapChunkSize); + const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); + const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); - const chunkEndX = Math_ceil(maxX / globalConfig.mapChunkSize); - const chunkEndY = Math_ceil(maxY / globalConfig.mapChunkSize); + const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); + const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); // Render y from top down for proper blending for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { @@ -223,11 +222,11 @@ export class MapView extends BaseMap { const minX = left - border; const maxX = right + border - 1; - const chunkStartX = Math_floor(minX / globalConfig.mapChunkSize); - const chunkStartY = Math_floor(minY / globalConfig.mapChunkSize); + const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); + const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); - const chunkEndX = Math_ceil(maxX / globalConfig.mapChunkSize); - const chunkEndY = Math_ceil(maxY / globalConfig.mapChunkSize); + const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize); + const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize); // Render y from top down for proper blending for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { diff --git a/src/js/game/meta_building.js b/src/js/game/meta_building.js index 11da28f3..798e4d4c 100644 --- a/src/js/game/meta_building.js +++ b/src/js/game/meta_building.js @@ -1,10 +1,10 @@ -import { Vector, enumDirection, enumAngleToDirection } from "../core/vector"; import { Loader } from "../core/loader"; -import { GameRoot } from "./root"; import { AtlasSprite } from "../core/sprites"; -import { Entity } from "./entity"; -import { StaticMapEntityComponent } from "./components/static_map_entity"; +import { Vector } from "../core/vector"; import { SOUNDS } from "../platform/sound"; +import { StaticMapEntityComponent } from "./components/static_map_entity"; +import { Entity } from "./entity"; +import { enumEditMode, GameRoot } from "./root"; export const defaultBuildingVariant = "default"; @@ -24,6 +24,14 @@ export class MetaBuilding { return this.id; } + /** + * Returns the edit layer of the building + * @returns {enumEditMode} + */ + getEditLayer() { + return enumEditMode.regular; + } + /** * Should return the dimensions of the building */ diff --git a/src/js/game/meta_building_registry.js b/src/js/game/meta_building_registry.js index 450a9743..0f37762d 100644 --- a/src/js/game/meta_building_registry.js +++ b/src/js/game/meta_building_registry.js @@ -10,6 +10,7 @@ import { MetaStackerBuilding } from "./buildings/stacker"; import { MetaTrashBuilding } from "./buildings/trash"; import { MetaUndergroundBeltBuilding } from "./buildings/underground_belt"; import { MetaHubBuilding } from "./buildings/hub"; +import { MetaEnergyGenerator } from "./buildings/energy_generator"; export function initMetaBuildingRegistry() { gMetaBuildingRegistry.register(MetaSplitterBuilding); @@ -23,4 +24,5 @@ export function initMetaBuildingRegistry() { gMetaBuildingRegistry.register(MetaBeltBaseBuilding); gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding); gMetaBuildingRegistry.register(MetaHubBuilding); + gMetaBuildingRegistry.register(MetaEnergyGenerator); } diff --git a/src/js/game/production_analytics.js b/src/js/game/production_analytics.js index 1abac940..41ec31ae 100644 --- a/src/js/game/production_analytics.js +++ b/src/js/game/production_analytics.js @@ -1,7 +1,7 @@ import { GameRoot } from "./root"; import { ShapeDefinition } from "./shape_definition"; import { globalConfig } from "../core/config"; -import { BaseItem } from "./base_item"; +import { BaseItem, enumItemType } from "./base_item"; import { ShapeItem } from "./items/shape_item"; import { BasicSerializableObject } from "../savegame/serialization"; @@ -53,8 +53,8 @@ export class ProductionAnalytics extends BasicSerializableObject { * @param {BaseItem} item */ onItemProduced(item) { - if (item instanceof ShapeItem) { - const definition = item.definition; + if (item.getItemType() === enumItemType.shape) { + const definition = /** @type {ShapeItem} */ (item).definition; const key = definition.getHash(); const entry = this.history[enumAnalyticsDataSource.produced]; entry[entry.length - 1][key] = (entry[entry.length - 1][key] || 0) + 1; diff --git a/src/js/game/root.js b/src/js/game/root.js index 061790c0..def0a034 100644 --- a/src/js/game/root.js +++ b/src/js/game/root.js @@ -32,6 +32,12 @@ import { KeyActionMapper } from "./key_action_mapper"; const logger = createLogger("game/root"); +/** @enum {string} */ +export const enumEditMode = { + regular: "regular", + wires: "wires", +}; + /** * The game root is basically the whole game state at a given point, * combining all important classes. We don't have globals, but this @@ -125,6 +131,9 @@ export class GameRoot { /** @type {DynamicTickrate} */ this.dynamicTickrate = null; + /** @type {enumEditMode} */ + this.editMode = enumEditMode.regular; + this.signals = { // Entities entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new Signal()), @@ -134,9 +143,6 @@ export class GameRoot { entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()), entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - // Special case for updating when a blueprint is placed. - blueprintPlacedUpdateBeltCache: /** @type {TypedSignal<[Rectangle]>} */ (new Signal()), - // Global resized: /** @type {TypedSignal<[number, number]>} */ (new Signal()), readyToRender: /** @type {TypedSignal<[]>} */ (new Signal()), @@ -159,6 +165,8 @@ export class GameRoot { itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()), bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), + + editModeChanged: /** @type {TypedSignal<[enumEditMode]>} */ (new Signal()), }; // RNG's diff --git a/src/js/game/shape_definition.js b/src/js/game/shape_definition.js index 8644e353..fca5f730 100644 --- a/src/js/game/shape_definition.js +++ b/src/js/game/shape_definition.js @@ -1,5 +1,4 @@ import { makeOffscreenBuffer } from "../core/buffer_utils"; -import { JSON_parse, JSON_stringify, Math_max, Math_PI, Math_radians } from "../core/builtins"; import { globalConfig } from "../core/config"; import { smoothenDpi } from "../core/dpi_manager"; import { DrawParameters } from "../core/draw_parameters"; @@ -235,7 +234,7 @@ export class ShapeDefinition extends BasicSerializableObject { * @returns {Array} */ internalCloneLayers() { - return JSON_parse(JSON_stringify(this.layers)); + return JSON.parse(JSON.stringify(this.layers)); } /** @@ -340,7 +339,7 @@ export class ShapeDefinition extends BasicSerializableObject { for (let layerIndex = 0; layerIndex < this.layers.length; ++layerIndex) { const quadrants = this.layers[layerIndex]; - const layerScale = Math_max(0.1, 0.9 - layerIndex * 0.22); + const layerScale = Math.max(0.1, 0.9 - layerIndex * 0.22); for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) { if (!quadrants[quadrantIndex]) { @@ -352,7 +351,7 @@ export class ShapeDefinition extends BasicSerializableObject { const centerQuadrantX = quadrantPos.x * quadrantHalfSize; const centerQuadrantY = quadrantPos.y * quadrantHalfSize; - const rotation = Math_radians(quadrantIndex * 90); + const rotation = Math.radians(quadrantIndex * 90); context.translate(centerQuadrantX, centerQuadrantY); context.rotate(rotation); @@ -414,7 +413,7 @@ export class ShapeDefinition extends BasicSerializableObject { insetPadding + -quadrantHalfSize, -insetPadding + quadrantHalfSize, quadrantSize * layerScale, - -Math_PI * 0.5, + -Math.PI * 0.5, 0 ); context.closePath(); diff --git a/src/js/game/sound_proxy.js b/src/js/game/sound_proxy.js index 0408586d..162f0bc0 100644 --- a/src/js/game/sound_proxy.js +++ b/src/js/game/sound_proxy.js @@ -5,10 +5,9 @@ import { GameRoot } from "./root"; import { Vector } from "../core/vector"; import { SOUNDS } from "../platform/sound"; -const avgSoundDurationSeconds = 0.25; +const avgSoundDurationSeconds = 0.1; const maxOngoingSounds = 2; - -const maxOngoingUiSounds = 10; +const maxOngoingUiSounds = 5; // Proxy to the application sound instance export class SoundProxy { diff --git a/src/js/game/systems/belt.js b/src/js/game/systems/belt.js index 108f099b..a9a3efd9 100644 --- a/src/js/game/systems/belt.js +++ b/src/js/game/systems/belt.js @@ -1,25 +1,26 @@ -import { Math_radians, Math_min, Math_max, Math_sqrt } from "../../core/builtins"; import { globalConfig } from "../../core/config"; import { DrawParameters } from "../../core/draw_parameters"; +import { gMetaBuildingRegistry } from "../../core/global_registries"; import { Loader } from "../../core/loader"; +import { createLogger } from "../../core/logging"; import { AtlasSprite } from "../../core/sprites"; +import { fastArrayDeleteValue } from "../../core/utils"; +import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../../core/vector"; +import { BeltPath } from "../belt_path"; +import { arrayBeltVariantToRotation, MetaBeltBaseBuilding } from "../buildings/belt_base"; import { BeltComponent } from "../components/belt"; import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; -import { enumDirection, enumDirectionToVector, Vector, enumInvertedDirections } from "../../core/vector"; import { MapChunkView } from "../map_chunk_view"; -import { gMetaBuildingRegistry } from "../../core/global_registries"; -import { MetaBeltBaseBuilding } from "../buildings/belt_base"; import { defaultBuildingVariant } from "../meta_building"; -import { GameRoot } from "../root"; -import { createLogger } from "../../core/logging"; -import { Rectangle } from "../../core/rectangle"; -const BELT_ANIM_COUNT = 6; -const SQRT_2 = Math_sqrt(2); +export const BELT_ANIM_COUNT = 28; const logger = createLogger("belt"); +/** + * Manages all belts + */ export class BeltSystem extends GameSystemWithFilter { constructor(root) { super(root, [BeltComponent]); @@ -31,43 +32,78 @@ export class BeltSystem extends GameSystemWithFilter { [enumDirection.left]: Loader.getSprite("sprites/belt/left_0.png"), [enumDirection.right]: Loader.getSprite("sprites/belt/right_0.png"), }; - /**b + + /** * @type {Object.>} */ this.beltAnimations = { - [enumDirection.top]: [ - Loader.getSprite("sprites/belt/forward_0.png"), - Loader.getSprite("sprites/belt/forward_1.png"), - Loader.getSprite("sprites/belt/forward_2.png"), - Loader.getSprite("sprites/belt/forward_3.png"), - Loader.getSprite("sprites/belt/forward_4.png"), - Loader.getSprite("sprites/belt/forward_5.png"), - ], - [enumDirection.left]: [ - Loader.getSprite("sprites/belt/left_0.png"), - Loader.getSprite("sprites/belt/left_1.png"), - Loader.getSprite("sprites/belt/left_2.png"), - Loader.getSprite("sprites/belt/left_3.png"), - Loader.getSprite("sprites/belt/left_4.png"), - Loader.getSprite("sprites/belt/left_5.png"), - ], - [enumDirection.right]: [ - Loader.getSprite("sprites/belt/right_0.png"), - Loader.getSprite("sprites/belt/right_1.png"), - Loader.getSprite("sprites/belt/right_2.png"), - Loader.getSprite("sprites/belt/right_3.png"), - Loader.getSprite("sprites/belt/right_4.png"), - Loader.getSprite("sprites/belt/right_5.png"), - ], + [enumDirection.top]: [], + [enumDirection.left]: [], + [enumDirection.right]: [], }; - this.root.signals.entityManuallyPlaced.add(this.updateSurroundingBeltPlacement, this); - this.root.signals.blueprintPlacedUpdateBeltCache.add(this.updateAreaToRecompute, this); - this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this); - this.root.signals.postLoadHook.add(this.computeBeltCache, this); + for (let i = 0; i < BELT_ANIM_COUNT; ++i) { + this.beltAnimations[enumDirection.top].push( + Loader.getSprite("sprites/belt/forward_" + i + ".png") + ); + this.beltAnimations[enumDirection.left].push(Loader.getSprite("sprites/belt/left_" + i + ".png")); + this.beltAnimations[enumDirection.right].push( + Loader.getSprite("sprites/belt/right_" + i + ".png") + ); + } - /** @type {Rectangle} */ - this.areaToRecompute = null; + this.root.signals.entityDestroyed.add(this.onEntityDestroyed, this); + this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this); + + // Notice: These must come *after* the entity destroyed signals + this.root.signals.entityManuallyPlaced.add(this.updateSurroundingBeltPlacement, this); + this.root.signals.entityAdded.add(this.onEntityAdded, this); + + /** @type {Array} */ + this.beltPaths = []; + } + + /** + * Serializes all belt paths + * @returns {Array} + */ + serializePaths() { + let data = []; + for (let i = 0; i < this.beltPaths.length; ++i) { + data.push(this.beltPaths[i].serialize()); + } + return data; + } + + /** + * Deserializes all belt paths + * @param {Array} data + */ + deserializePaths(data) { + if (!Array.isArray(data)) { + return "Belt paths are not an array: " + typeof data; + } + + for (let i = 0; i < data.length; ++i) { + const path = BeltPath.fromSerialized(this.root, data[i]); + // If path is a string, that means its an error + if (!(path instanceof BeltPath)) { + return "Failed to create path from belt data: " + path; + } + this.beltPaths.push(path); + } + + if (this.beltPaths.length === 0) { + // Old savegames might not have paths yet + logger.warn("Recomputing belt paths (most likely the savegame is old or empty)"); + this.recomputeAllBeltPaths(); + } else { + logger.warn("Restored", this.beltPaths.length, "belt paths"); + } + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } } /** @@ -97,57 +133,213 @@ export class BeltSystem extends GameSystemWithFilter { return; } - if (entity.components.Belt) { - this.cacheNeedsUpdate = true; - } - const metaBelt = gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding); // Compute affected area const originalRect = staticComp.getTileSpaceBounds(); const affectedArea = originalRect.expandedInAllDirections(1); - // Store if anything got changed, if so we need to queue a recompute - let anythingChanged = false; - for (let x = affectedArea.x; x < affectedArea.right(); ++x) { for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) { - if (!originalRect.containsPoint(x, y)) { - const targetEntity = this.root.map.getTileContentXY(x, y); - if (targetEntity) { - const targetBeltComp = targetEntity.components.Belt; - if (targetBeltComp) { - const targetStaticComp = targetEntity.components.StaticMapEntity; - const { - rotation, - rotationVariant, - } = metaBelt.computeOptimalDirectionAndRotationVariantAtTile( - this.root, - new Vector(x, y), - targetStaticComp.originalRotation, - defaultBuildingVariant - ); - targetStaticComp.rotation = rotation; - metaBelt.updateVariants(targetEntity, rotationVariant, defaultBuildingVariant); - anythingChanged = true; - } + if (originalRect.containsPoint(x, y)) { + // Make sure we don't update the original entity + continue; + } + + const targetEntity = this.root.map.getTileContentXY(x, y); + if (!targetEntity) { + // Empty tile + continue; + } + + const targetBeltComp = targetEntity.components.Belt; + const targetStaticComp = targetEntity.components.StaticMapEntity; + + if (!targetBeltComp) { + // Not a belt + continue; + } + + const { + rotation, + rotationVariant, + } = metaBelt.computeOptimalDirectionAndRotationVariantAtTile( + this.root, + new Vector(x, y), + targetStaticComp.originalRotation, + defaultBuildingVariant + ); + + // Compute delta to see if anything changed + const newDirection = arrayBeltVariantToRotation[rotationVariant]; + + if (targetStaticComp.rotation !== rotation || newDirection !== targetBeltComp.direction) { + // Ok, first remove it from its current path + this.deleteEntityFromPath(targetBeltComp.assignedPath, targetEntity); + + // Change stuff + targetStaticComp.rotation = rotation; + metaBelt.updateVariants(targetEntity, rotationVariant, defaultBuildingVariant); + + // Now add it again + this.addEntityToPaths(targetEntity); + + // Sanity + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); } } } } - if (anythingChanged) { - this.updateAreaToRecompute(affectedArea); + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); } } + /** + * Called when an entity got destroyed + * @param {Entity} entity + */ + onEntityDestroyed(entity) { + if (!this.root.gameInitialized) { + return; + } + + if (!entity.components.Belt) { + return; + } + + const assignedPath = entity.components.Belt.assignedPath; + assert(assignedPath, "Entity has no belt path assigned"); + this.deleteEntityFromPath(assignedPath, entity); + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + } + + /** + * Attempts to delete the belt from its current path + * @param {BeltPath} path + * @param {Entity} entity + */ + deleteEntityFromPath(path, entity) { + if (path.entityPath.length === 1) { + // This is a single entity path, easy to do, simply erase whole path + fastArrayDeleteValue(this.beltPaths, path); + return; + } + + // Notice: Since there might be circular references, it is important to check + // which role the entity has + if (path.isStartEntity(entity)) { + // We tried to delete the start + path.deleteEntityOnStart(entity); + } else if (path.isEndEntity(entity)) { + // We tried to delete the end + path.deleteEntityOnEnd(entity); + } else { + // We tried to delete something inbetween + const newPath = path.deleteEntityOnPathSplitIntoTwo(entity); + this.beltPaths.push(newPath); + } + + // Sanity + entity.components.Belt.assignedPath = null; + } + + /** + * Adds the given entity to the appropriate paths + * @param {Entity} entity + */ + addEntityToPaths(entity) { + const fromEntity = this.findSupplyingEntity(entity); + const toEntity = this.findFollowUpEntity(entity); + + // Check if we can add the entity to the previous path + if (fromEntity) { + const fromPath = fromEntity.components.Belt.assignedPath; + fromPath.extendOnEnd(entity); + + // Check if we now can extend the current path by the next path + if (toEntity) { + const toPath = toEntity.components.Belt.assignedPath; + + if (fromPath === toPath) { + // This is a circular dependency -> Ignore + } else { + fromPath.extendByPath(toPath); + + // Delete now obsolete path + fastArrayDeleteValue(this.beltPaths, toPath); + } + } + } else { + if (toEntity) { + // Prepend it to the other path + const toPath = toEntity.components.Belt.assignedPath; + toPath.extendOnBeginning(entity); + } else { + // This is an empty belt path + const path = new BeltPath(this.root, [entity]); + this.beltPaths.push(path); + } + } + } + + /** + * Called when an entity got added + * @param {Entity} entity + */ + onEntityAdded(entity) { + if (!this.root.gameInitialized) { + return; + } + + if (!entity.components.Belt) { + return; + } + + this.addEntityToPaths(entity); + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + } + + /** + * Draws all belt paths + * @param {DrawParameters} parameters + */ draw(parameters) { - this.forEachMatchingEntityOnScreen(parameters, this.drawEntityItems.bind(this)); + for (let i = 0; i < this.beltPaths.length; ++i) { + this.beltPaths[i].draw(parameters); + } + } + + /** + * Verifies all belt paths + */ + debug_verifyBeltPaths() { + for (let i = 0; i < this.beltPaths.length; ++i) { + this.beltPaths[i].debug_checkIntegrity("general-verify"); + } + + const belts = this.root.entityMgr.getAllWithComponent(BeltComponent); + for (let i = 0; i < belts.length; ++i) { + const path = belts[i].components.Belt.assignedPath; + if (!path) { + throw new Error("Belt has no path: " + belts[i].uid); + } + if (this.beltPaths.indexOf(path) < 0) { + throw new Error("Path of entity not contained: " + belts[i].uid); + } + } } /** * Finds the follow up entity for a given belt. Used for building the dependencies * @param {Entity} entity + * @returns {Entity|null} */ findFollowUpEntity(entity) { const staticComp = entity.components.StaticMapEntity; @@ -184,141 +376,115 @@ export class BeltSystem extends GameSystemWithFilter { } /** - * Recomputes the belt cache + * Finds the supplying belt for a given belt. Used for building the dependencies + * @param {Entity} entity + * @returns {Entity|null} */ - computeBeltCache() { - if (this.areaToRecompute) { - logger.log("Updating belt cache by updating area:", this.areaToRecompute); - for (let x = this.areaToRecompute.x; x < this.areaToRecompute.right(); ++x) { - for (let y = this.areaToRecompute.y; y < this.areaToRecompute.bottom(); ++y) { - const tile = this.root.map.getTileContentXY(x, y); - if (tile && tile.components.Belt) { - tile.components.Belt.followUpCache = this.findFollowUpEntity(tile); + findSupplyingEntity(entity) { + const staticComp = entity.components.StaticMapEntity; + + const supplyDirection = staticComp.localDirectionToWorld(enumDirection.bottom); + const supplyVector = enumDirectionToVector[supplyDirection]; + + const supplyTile = staticComp.origin.add(supplyVector); + const supplyEntity = this.root.map.getTileContent(supplyTile); + + // Check if theres a belt at the tile we point to + if (supplyEntity) { + const supplyBeltComp = supplyEntity.components.Belt; + if (supplyBeltComp) { + const supplyStatic = supplyEntity.components.StaticMapEntity; + const supplyEjector = supplyEntity.components.ItemEjector; + + // Check if the belt accepts items from our direction + const ejectorSlots = supplyEjector.slots; + for (let i = 0; i < ejectorSlots.length; ++i) { + const slot = ejectorSlots[i]; + const localDirection = supplyStatic.localDirectionToWorld(slot.direction); + if (enumInvertedDirections[localDirection] === supplyDirection) { + return supplyEntity; } } } - - // Reset stale areas afterwards - this.areaToRecompute = null; - } else { - logger.log("Doing full belt recompute"); - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - entity.components.Belt.followUpCache = this.findFollowUpEntity(entity); - } } + + return null; } - update() { - if (this.areaToRecompute) { - this.computeBeltCache(); - } + /** + * Recomputes the belt path network. Only required for old savegames + */ + recomputeAllBeltPaths() { + logger.warn("Recomputing all belt paths"); + const visitedUids = new Set(); - // Divide by item spacing on belts since we use throughput and not speed - let beltSpeed = - this.root.hubGoals.getBeltBaseSpeed() * - this.root.dynamicTickrate.deltaSeconds * - globalConfig.itemSpacingOnBelts; - - if (G_IS_DEV && globalConfig.debug.instantBelts) { - beltSpeed *= 100; - } + const result = []; for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; - - const beltComp = entity.components.Belt; - const items = beltComp.sortedItems; - - if (items.length === 0) { - // Fast out for performance + if (visitedUids.has(entity.uid)) { continue; } - const ejectorComp = entity.components.ItemEjector; - let maxProgress = 1; + // Mark entity as visited + visitedUids.add(entity.uid); - /* PERFORMANCE OPTIMIZATION */ - // Original: - // const isCurrentlyEjecting = ejectorComp.isAnySlotEjecting(); - // Replaced (Since belts always have just one slot): - const ejectorSlot = ejectorComp.slots[0]; - const isCurrentlyEjecting = ejectorSlot.item; + // Compute path, start with entity and find precedors / successors + const path = [entity]; - // When ejecting, we can not go further than the item spacing since it - // will be on the corner - if (isCurrentlyEjecting) { - maxProgress = 1 - globalConfig.itemSpacingOnBelts; - } else { - // Otherwise our progress depends on the follow up - if (beltComp.followUpCache) { - const spacingOnBelt = beltComp.followUpCache.components.Belt.getDistanceToFirstItemCenter(); - maxProgress = Math.min(2, 1 - globalConfig.itemSpacingOnBelts + spacingOnBelt); + // Prevent infinite loops + let maxIter = 99999; - // Useful check, but hurts performance - // assert(maxProgress >= 0.0, "max progress < 0 (I)"); + // Find precedors + let prevEntity = this.findSupplyingEntity(entity); + while (prevEntity && --maxIter > 0) { + if (visitedUids.has(prevEntity.uid)) { + break; } + path.unshift(prevEntity); + visitedUids.add(prevEntity.uid); + prevEntity = this.findSupplyingEntity(prevEntity); } - let speedMultiplier = 1; - if (beltComp.direction !== enumDirection.top) { - // Curved belts are shorter, thus being quicker (Looks weird otherwise) - speedMultiplier = SQRT_2; - } - - // Not really nice. haven't found the reason for this yet. - if (items.length > 2 / globalConfig.itemSpacingOnBelts) { - beltComp.sortedItems = []; - } - - for (let itemIndex = items.length - 1; itemIndex >= 0; --itemIndex) { - const progressAndItem = items[itemIndex]; - - progressAndItem[0] = Math.min(maxProgress, progressAndItem[0] + speedMultiplier * beltSpeed); - - if (progressAndItem[0] >= 1.0) { - if (beltComp.followUpCache) { - const followUpBelt = beltComp.followUpCache.components.Belt; - if (followUpBelt.canAcceptItem()) { - followUpBelt.takeItem(progressAndItem[1], progressAndItem[0] - 1.0); - items.splice(itemIndex, 1); - } else { - // Well, we couldn't really take it to a follow up belt, keep it at - // max progress - progressAndItem[0] = 1.0; - maxProgress = 1 - globalConfig.itemSpacingOnBelts; - } - } else { - // Try to give this item to a new belt - - /* PERFORMANCE OPTIMIZATION */ - // Original: - // const freeSlot = ejectorComp.getFirstFreeSlot(); - // Replaced - if (ejectorSlot.item) { - // So, we don't have a free slot - damned! - progressAndItem[0] = 1.0; - maxProgress = 1 - globalConfig.itemSpacingOnBelts; - } else { - // We got a free slot, remove this item and keep it on the ejector slot - if (!ejectorComp.tryEject(0, progressAndItem[1])) { - assert(false, "Ejection failed"); - } - items.splice(itemIndex, 1); - - // NOTICE: Do not override max progress here at all, this leads to issues - } - } - } else { - // We just moved this item forward, so determine the maximum progress of other items - maxProgress = Math.max(0, progressAndItem[0] - globalConfig.itemSpacingOnBelts); + // Find succedors + let nextEntity = this.findFollowUpEntity(entity); + while (nextEntity && --maxIter > 0) { + if (visitedUids.has(nextEntity.uid)) { + break; } + + path.push(nextEntity); + visitedUids.add(nextEntity.uid); + nextEntity = this.findFollowUpEntity(nextEntity); } + + assert(maxIter > 1, "Ran out of iterations"); + result.push(new BeltPath(this.root, path)); + } + + logger.log("Found", this.beltPaths.length, "belt paths"); + this.beltPaths = result; + } + + /** + * Updates all belts + */ + update() { + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + + for (let i = 0; i < this.beltPaths.length; ++i) { + this.beltPaths[i].update(); + } + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); } } /** - * + * Draws a given chunk * @param {DrawParameters} parameters * @param {MapChunkView} chunk */ @@ -327,12 +493,13 @@ export class BeltSystem extends GameSystemWithFilter { return; } - const speedMultiplier = this.root.hubGoals.getBeltBaseSpeed(); + // Limit speed to avoid belts going backwards + const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10); // SYNC with systems/item_acceptor.js:drawEntityUnderlays! // 126 / 42 is the exact animation speed of the png animation const animationIndex = Math.floor( - ((this.root.time.now() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * + ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * globalConfig.itemSpacingOnBelts ); const contents = chunk.contents; @@ -357,38 +524,12 @@ export class BeltSystem extends GameSystemWithFilter { } /** + * Draws the belt path debug overlays * @param {DrawParameters} parameters - * @param {Entity} entity */ - drawEntityItems(parameters, entity) { - const beltComp = entity.components.Belt; - const staticComp = entity.components.StaticMapEntity; - - const items = beltComp.sortedItems; - - if (items.length === 0) { - // Fast out for performance - return; - } - - if (!staticComp.shouldBeDrawn(parameters)) { - return; - } - - for (let i = 0; i < items.length; ++i) { - const itemAndProgress = items[i]; - - // Nice would be const [pos, item] = itemAndPos; but that gets polyfilled and is super slow then - const progress = itemAndProgress[0]; - const item = itemAndProgress[1]; - - const position = staticComp.applyRotationToVector(beltComp.transformBeltToLocalSpace(progress)); - - item.draw( - (staticComp.origin.x + position.x + 0.5) * globalConfig.tileSize, - (staticComp.origin.y + position.y + 0.5) * globalConfig.tileSize, - parameters - ); + drawBeltPathDebug(parameters) { + for (let i = 0; i < this.beltPaths.length; ++i) { + this.beltPaths[i].drawDebug(parameters); } } } diff --git a/src/js/game/systems/energy_generator.js b/src/js/game/systems/energy_generator.js new file mode 100644 index 00000000..733ea90a --- /dev/null +++ b/src/js/game/systems/energy_generator.js @@ -0,0 +1,76 @@ +import { DrawParameters } from "../../core/draw_parameters"; +import { T } from "../../translations"; +import { EnergyGeneratorComponent } from "../components/energy_generator"; +import { Entity } from "../entity"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { ShapeDefinition } from "../shape_definition"; + +export class EnergyGeneratorSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [EnergyGeneratorComponent]); + } + + draw(parameters) { + this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this)); + } + + /** + * Returns which shape is required for a given generator + * @param {Entity} entity + */ + getShapeRequiredForGenerator(entity) { + return "CuCuCuCu"; + } + + update() { + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const energyGenComp = entity.components.EnergyGenerator; + + if (!energyGenComp.requiredKey) { + // Compute required key for this generator + energyGenComp.requiredKey = this.getShapeRequiredForGenerator(entity); + } + } + } + + /** + * @param {DrawParameters} parameters + * @param {Entity} entity + */ + drawEntity(parameters, entity) { + const context = parameters.context; + const staticComp = entity.components.StaticMapEntity; + + if (!staticComp.shouldBeDrawn(parameters)) { + return; + } + + const energyGenComp = entity.components.EnergyGenerator; + if (!energyGenComp.requiredKey) { + // Not initialized yet + return; + } + + const pos = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); + + // TESTING + const definition = ShapeDefinition.fromShortKey(energyGenComp.requiredKey); + definition.draw(pos.x, pos.y, parameters, 30); + + const energyGenerated = 5; + + // deliver: Deliver + // toGenerateEnergy: For energy + context.font = "bold 7px GameFont"; + context.fillStyle = "#64666e"; + context.textAlign = "left"; + context.fillText(T.buildings.energy_generator.deliver.toUpperCase(), pos.x - 25, pos.y - 18); + + context.fillText( + T.buildings.energy_generator.toGenerateEnergy.replace("", "" + energyGenerated).toUpperCase(), + pos.x - 25, + pos.y + 28 + ); + } +} diff --git a/src/js/game/systems/item_acceptor.js b/src/js/game/systems/item_acceptor.js index 69209ca9..8b4983e6 100644 --- a/src/js/game/systems/item_acceptor.js +++ b/src/js/game/systems/item_acceptor.js @@ -6,37 +6,43 @@ import { enumDirectionToVector, enumDirectionToAngle } from "../../core/vector"; import { ItemAcceptorComponent } from "../components/item_acceptor"; import { Loader } from "../../core/loader"; import { drawRotatedSprite } from "../../core/draw_utils"; -import { Math_radians } from "../../core/builtins"; +import { BELT_ANIM_COUNT } from "./belt"; +import { fastArrayDelete } from "../../core/utils"; export class ItemAcceptorSystem extends GameSystemWithFilter { constructor(root) { super(root, [ItemAcceptorComponent]); - this.underlayBeltSprites = [ - Loader.getSprite("sprites/belt/forward_0.png"), - Loader.getSprite("sprites/belt/forward_1.png"), - Loader.getSprite("sprites/belt/forward_2.png"), - Loader.getSprite("sprites/belt/forward_3.png"), - Loader.getSprite("sprites/belt/forward_4.png"), - Loader.getSprite("sprites/belt/forward_5.png"), - ]; + this.underlayBeltSprites = []; + + for (let i = 0; i < BELT_ANIM_COUNT; ++i) { + this.underlayBeltSprites.push(Loader.getSprite("sprites/belt/forward_" + i + ".png")); + } } update() { + const progress = + this.root.dynamicTickrate.deltaSeconds * + this.root.hubGoals.getBeltBaseSpeed() * + 2 * // * 2 because its only a half tile + globalConfig.itemSpacingOnBelts; + for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; const aceptorComp = entity.components.ItemAcceptor; + const animations = aceptorComp.itemConsumptionAnimations; // Process item consumption animations to avoid items popping from the belts - for (let animIndex = 0; animIndex < aceptorComp.itemConsumptionAnimations.length; ++animIndex) { - const anim = aceptorComp.itemConsumptionAnimations[animIndex]; - anim.animProgress += - this.root.dynamicTickrate.deltaSeconds * - this.root.hubGoals.getBeltBaseSpeed() * - 2 * - globalConfig.itemSpacingOnBelts; + for (let animIndex = 0; animIndex < animations.length; ++animIndex) { + const anim = animations[animIndex]; + anim.animProgress += progress; if (anim.animProgress > 1) { - aceptorComp.itemConsumptionAnimations.splice(animIndex, 1); + // Original + // animations.splice(animIndex, 1); + + // Faster variant + fastArrayDelete(animations, animIndex); + animIndex -= 1; } } @@ -96,6 +102,9 @@ export class ItemAcceptorSystem extends GameSystemWithFilter { return; } + // Limit speed to avoid belts going backwards + const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10); + const underlays = acceptorComp.beltUnderlays; for (let i = 0; i < underlays.length; ++i) { const { pos, direction } = underlays[i]; @@ -105,11 +114,7 @@ export class ItemAcceptorSystem extends GameSystemWithFilter { // SYNC with systems/belt.js:drawSingleEntity! const animationIndex = Math.floor( - ((this.root.time.now() * - this.root.hubGoals.getBeltBaseSpeed() * - this.underlayBeltSprites.length * - 126) / - 42) * + ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * globalConfig.itemSpacingOnBelts ); @@ -118,7 +123,7 @@ export class ItemAcceptorSystem extends GameSystemWithFilter { sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length], x: (transformedPos.x + 0.5) * globalConfig.tileSize, y: (transformedPos.y + 0.5) * globalConfig.tileSize, - angle: Math_radians(angle), + angle: Math.radians(angle), size: globalConfig.tileSize, }); } diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index 444cb8c4..c18ce2e9 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -1,4 +1,3 @@ -import { Math_min } from "../../core/builtins"; import { globalConfig } from "../../core/config"; import { DrawParameters } from "../../core/draw_parameters"; import { createLogger } from "../../core/logging"; @@ -56,10 +55,25 @@ export class ItemEjectorSystem extends GameSystemWithFilter { recomputeCache() { if (this.areaToRecompute) { logger.log("Recomputing cache using rectangle"); - this.recomputeAreaCache(this.areaToRecompute); + if (G_IS_DEV && globalConfig.debug.renderChanges) { + this.root.hud.parts.changesDebugger.renderChange( + "ejector-area", + this.areaToRecompute, + "#fe50a6" + ); + } + this.recomputeAreaCache(); this.areaToRecompute = null; } else { logger.log("Full cache recompute"); + if (G_IS_DEV && globalConfig.debug.renderChanges) { + this.root.hud.parts.changesDebugger.renderChange( + "ejector-full", + new Rectangle(-1000, -1000, 2000, 2000), + "#fe50a6" + ); + } + // Try to find acceptors for every ejector for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; @@ -69,10 +83,10 @@ export class ItemEjectorSystem extends GameSystemWithFilter { } /** - * - * @param {Rectangle} area + * Recomputes the cache in the given area */ - recomputeAreaCache(area) { + recomputeAreaCache() { + const area = this.areaToRecompute; let entryCount = 0; logger.log("Recomputing area:", area.x, area.y, "/", area.w, area.h); @@ -171,23 +185,28 @@ export class ItemEjectorSystem extends GameSystemWithFilter { for (let i = 0; i < this.allEntities.length; ++i) { const sourceEntity = this.allEntities[i]; const sourceEjectorComp = sourceEntity.components.ItemEjector; + if (!sourceEjectorComp.enabled) { + continue; + } + if (!sourceEjectorComp.cachedConnectedSlots) { continue; } - for (let j = 0; j < sourceEjectorComp.cachedConnectedSlots.length; j++) { - const sourceSlot = sourceEjectorComp.cachedConnectedSlots[j]; - const destSlot = sourceSlot.cachedDestSlot; - const targetEntity = sourceSlot.cachedTargetEntity; + const slots = sourceEjectorComp.cachedConnectedSlots; + for (let j = 0; j < slots.length; ++j) { + const sourceSlot = slots[j]; const item = sourceSlot.item; - if (!item) { // No item available to be ejected continue; } + const destSlot = sourceSlot.cachedDestSlot; + const targetEntity = sourceSlot.cachedTargetEntity; + // Advance items on the slot - sourceSlot.progress = Math_min(1, sourceSlot.progress + progressGrowth); + sourceSlot.progress = Math.min(1, sourceSlot.progress + progressGrowth); // Check if we are still in the process of ejecting, can't proceed then if (sourceSlot.progress < 1.0) { @@ -224,20 +243,13 @@ export class ItemEjectorSystem extends GameSystemWithFilter { const beltComp = receiver.components.Belt; if (beltComp) { - // Ayy, its a belt! - if (beltComp.canAcceptItem()) { - beltComp.takeItem(item); - return true; - } - } - - const storageComp = receiver.components.Storage; - if (storageComp) { - // It's a storage - if (storageComp.canAcceptItem(item)) { - storageComp.takeItem(item); + const path = beltComp.assignedPath; + assert(path, "belt has no path"); + if (path.tryAcceptItem(item)) { return true; } + // Belt can have nothing else + return false; } const itemProcessorComp = receiver.components.ItemProcessor; @@ -246,6 +258,8 @@ export class ItemEjectorSystem extends GameSystemWithFilter { if (itemProcessorComp.tryTakeItem(item, slotIndex)) { return true; } + // Item processor can have nothing else + return false; } const undergroundBeltComp = receiver.components.UndergroundBelt; @@ -259,6 +273,32 @@ export class ItemEjectorSystem extends GameSystemWithFilter { ) { return true; } + + // Underground belt can have nothing else + return false; + } + + const storageComp = receiver.components.Storage; + if (storageComp) { + // It's a storage + if (storageComp.canAcceptItem(item)) { + storageComp.takeItem(item); + return true; + } + + // Storage can't have anything else + return false; + } + + const energyGeneratorComp = receiver.components.EnergyGenerator; + if (energyGeneratorComp) { + if (energyGeneratorComp.tryTakeItem(item)) { + // Passed it over + return true; + } + + // Energy generator comp can't have anything else + return false; } return false; diff --git a/src/js/game/systems/item_processor.js b/src/js/game/systems/item_processor.js index ae7a4568..d3d9b498 100644 --- a/src/js/game/systems/item_processor.js +++ b/src/js/game/systems/item_processor.js @@ -1,4 +1,3 @@ -import { Math_max } from "../../core/builtins"; import { globalConfig } from "../../core/config"; import { BaseItem } from "../base_item"; import { enumColorMixingResults } from "../colors"; @@ -21,7 +20,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { const ejectorComp = entity.components.ItemEjector; // First of all, process the current recipe - processorComp.secondsUntilEject = Math_max( + processorComp.secondsUntilEject = Math.max( 0, processorComp.secondsUntilEject - this.root.dynamicTickrate.deltaSeconds ); diff --git a/src/js/game/systems/underground_belt.js b/src/js/game/systems/underground_belt.js index 0456638a..7e97181f 100644 --- a/src/js/game/systems/underground_belt.js +++ b/src/js/game/systems/underground_belt.js @@ -1,17 +1,20 @@ -import { Math_max } from "../../core/builtins"; import { globalConfig } from "../../core/config"; import { Loader } from "../../core/loader"; +import { createLogger } from "../../core/logging"; +import { Rectangle } from "../../core/rectangle"; import { + enumAngleToDirection, enumDirection, enumDirectionToAngle, enumDirectionToVector, - Vector, - enumAngleToDirection, enumInvertedDirections, } from "../../core/vector"; import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt"; import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; +import { fastArrayDelete } from "../../core/utils"; + +const logger = createLogger("tunnels"); export class UndergroundBeltSystem extends GameSystemWithFilter { constructor(root) { @@ -26,30 +29,40 @@ export class UndergroundBeltSystem extends GameSystemWithFilter { ), }; - this.root.signals.entityManuallyPlaced.add(this.onEntityPlaced, this); + this.root.signals.entityManuallyPlaced.add(this.onEntityManuallyPlaced, this); + + /** + * @type {Rectangle} + */ + this.areaToRecompute = null; + + this.root.signals.entityAdded.add(this.onEntityChanged, this); + this.root.signals.entityDestroyed.add(this.onEntityChanged, this); } - update() { - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; + /** + * Called when an entity got added or removed + * @param {Entity} entity + */ + onEntityChanged(entity) { + if (!this.root.gameInitialized) { + return; + } + const undergroundComp = entity.components.UndergroundBelt; + if (!undergroundComp) { + return; + } - const undergroundComp = entity.components.UndergroundBelt; + const affectedArea = entity.components.StaticMapEntity.getTileSpaceBounds().expandedInAllDirections( + globalConfig.undergroundBeltMaxTilesByTier[ + globalConfig.undergroundBeltMaxTilesByTier.length - 1 + ] + 1 + ); - // Decrease remaining time of all items in belt - for (let k = 0; k < undergroundComp.pendingItems.length; ++k) { - const item = undergroundComp.pendingItems[k]; - item[1] = Math_max(0, item[1] - this.root.dynamicTickrate.deltaSeconds); - - if (G_IS_DEV && globalConfig.debug.instantBelts) { - item[1] = 0; - } - } - - if (undergroundComp.mode === enumUndergroundBeltMode.sender) { - this.handleSender(entity); - } else { - this.handleReceiver(entity); - } + if (this.areaToRecompute) { + this.areaToRecompute = this.areaToRecompute.getUnion(affectedArea); + } else { + this.areaToRecompute = affectedArea; } } @@ -57,7 +70,7 @@ export class UndergroundBeltSystem extends GameSystemWithFilter { * Callback when an entity got placed, used to remove belts between underground belts * @param {Entity} entity */ - onEntityPlaced(entity) { + onEntityManuallyPlaced(entity) { if (!this.root.app.settings.getAllSettings().enableTunnelSmartplace) { // Smart-place disabled return; @@ -88,10 +101,12 @@ export class UndergroundBeltSystem extends GameSystemWithFilter { } const contentsUndergroundComp = contents.components.UndergroundBelt; + const contentsStaticComp = contents.components.StaticMapEntity; if ( contentsUndergroundComp && contentsUndergroundComp.tier === undergroundComp.tier && - contentsUndergroundComp.mode === enumUndergroundBeltMode.sender + contentsUndergroundComp.mode === enumUndergroundBeltMode.sender && + enumAngleToDirection[contentsStaticComp.rotation] === direction ) { matchingEntrance = { entity: contents, @@ -206,63 +221,157 @@ export class UndergroundBeltSystem extends GameSystemWithFilter { } } + /** + * Recomputes the cache in the given area, invalidating all entries there + */ + recomputeArea() { + const area = this.areaToRecompute; + logger.log("Recomputing area:", area.x, area.y, "/", area.w, area.h); + if (G_IS_DEV && globalConfig.debug.renderChanges) { + this.root.hud.parts.changesDebugger.renderChange("tunnels", this.areaToRecompute, "#fc03be"); + } + + for (let x = area.x; x < area.right(); ++x) { + for (let y = area.y; y < area.bottom(); ++y) { + const entity = this.root.map.getTileContentXY(x, y); + if (!entity) { + continue; + } + const undergroundComp = entity.components.UndergroundBelt; + if (!undergroundComp) { + continue; + } + + undergroundComp.cachedLinkedEntity = null; + } + } + } + + update() { + if (this.areaToRecompute) { + this.recomputeArea(); + this.areaToRecompute = null; + } + + const delta = this.root.dynamicTickrate.deltaSeconds; + + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const undergroundComp = entity.components.UndergroundBelt; + const pendingItems = undergroundComp.pendingItems; + + // Decrease remaining time of all items in belt + for (let k = 0; k < pendingItems.length; ++k) { + const item = pendingItems[k]; + item[1] = Math.max(0, item[1] - delta); + if (G_IS_DEV && globalConfig.debug.instantBelts) { + item[1] = 0; + } + } + if (undergroundComp.mode === enumUndergroundBeltMode.sender) { + this.handleSender(entity); + } else { + this.handleReceiver(entity); + } + } + } + + /** + * Finds the receiver for a given sender + * @param {Entity} entity + * @returns {import("../components/underground_belt").LinkedUndergroundBelt} + */ + findRecieverForSender(entity) { + const staticComp = entity.components.StaticMapEntity; + const undergroundComp = entity.components.UndergroundBelt; + const searchDirection = staticComp.localDirectionToWorld(enumDirection.top); + const searchVector = enumDirectionToVector[searchDirection]; + const targetRotation = enumDirectionToAngle[searchDirection]; + let currentTile = staticComp.origin; + + // Search in the direction of the tunnel + for ( + let searchOffset = 0; + searchOffset < globalConfig.undergroundBeltMaxTilesByTier[undergroundComp.tier]; + ++searchOffset + ) { + currentTile = currentTile.add(searchVector); + + const potentialReceiver = this.root.map.getTileContent(currentTile); + if (!potentialReceiver) { + // Empty tile + continue; + } + const receiverUndergroundComp = potentialReceiver.components.UndergroundBelt; + if (!receiverUndergroundComp || receiverUndergroundComp.tier !== undergroundComp.tier) { + // Not a tunnel, or not on the same tier + continue; + } + + if (receiverUndergroundComp.mode !== enumUndergroundBeltMode.receiver) { + // Not a receiver + continue; + } + + const receiverStaticComp = potentialReceiver.components.StaticMapEntity; + if (receiverStaticComp.rotation !== targetRotation) { + // Wrong rotation + continue; + } + + return { entity: potentialReceiver, distance: searchOffset }; + } + + // None found + return { entity: null, distance: 0 }; + } + /** * * @param {Entity} entity */ handleSender(entity) { - const staticComp = entity.components.StaticMapEntity; const undergroundComp = entity.components.UndergroundBelt; - // Check if we have any item + // Find the current receiver + let receiver = undergroundComp.cachedLinkedEntity; + if (!receiver) { + // We don't have a receiver, compute it + receiver = undergroundComp.cachedLinkedEntity = this.findRecieverForSender(entity); + if (G_IS_DEV && globalConfig.debug.renderChanges) { + this.root.hud.parts.changesDebugger.renderChange( + "sender", + entity.components.StaticMapEntity.getTileSpaceBounds(), + "#fc03be" + ); + } + } + + if (!receiver.entity) { + // If there is no connection to a receiver, ignore this one + return; + } + + // Check if we have any item if (undergroundComp.pendingItems.length > 0) { + assert(undergroundComp.pendingItems.length === 1, "more than 1 pending"); const nextItemAndDuration = undergroundComp.pendingItems[0]; const remainingTime = nextItemAndDuration[1]; const nextItem = nextItemAndDuration[0]; + // Check if the item is ready to be emitted if (remainingTime === 0) { - // Try to find a receiver - const searchDirection = staticComp.localDirectionToWorld(enumDirection.top); - const searchVector = enumDirectionToVector[searchDirection]; - const targetRotation = enumDirectionToAngle[searchDirection]; - - let currentTile = staticComp.origin; - - for ( - let searchOffset = 0; - searchOffset < globalConfig.undergroundBeltMaxTilesByTier[undergroundComp.tier]; - ++searchOffset + // Check if the receiver can accept it + if ( + receiver.entity.components.UndergroundBelt.tryAcceptTunneledItem( + nextItem, + receiver.distance, + this.root.hubGoals.getUndergroundBeltBaseSpeed() + ) ) { - currentTile = currentTile.add(searchVector); - - const contents = this.root.map.getTileContent(currentTile); - if (contents) { - const receiverUndergroundComp = contents.components.UndergroundBelt; - if ( - receiverUndergroundComp && - receiverUndergroundComp.tier === undergroundComp.tier - ) { - const receiverStaticComp = contents.components.StaticMapEntity; - if (receiverStaticComp.rotation === targetRotation) { - if (receiverUndergroundComp.mode === enumUndergroundBeltMode.receiver) { - // Try to pass over the item to the receiver - if ( - receiverUndergroundComp.tryAcceptTunneledItem( - nextItem, - searchOffset, - this.root.hubGoals.getUndergroundBeltBaseSpeed() - ) - ) { - undergroundComp.pendingItems = []; - } - } - - // When we hit some underground belt, always stop, no matter what - break; - } - } - } + // Drop this item + fastArrayDelete(undergroundComp.pendingItems, 0); } } } @@ -275,7 +384,7 @@ export class UndergroundBeltSystem extends GameSystemWithFilter { handleReceiver(entity) { const undergroundComp = entity.components.UndergroundBelt; - // Try to eject items, we only check the first one cuz its sorted by remaining time + // Try to eject items, we only check the first one because it is sorted by remaining time const items = undergroundComp.pendingItems; if (items.length > 0) { const nextItemAndDuration = undergroundComp.pendingItems[0]; diff --git a/src/js/game/systems/wired_pins.js b/src/js/game/systems/wired_pins.js new file mode 100644 index 00000000..44e062b0 --- /dev/null +++ b/src/js/game/systems/wired_pins.js @@ -0,0 +1,51 @@ +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { WiredPinsComponent } from "../components/wired_pins"; +import { DrawParameters } from "../../core/draw_parameters"; +import { Entity } from "../entity"; +import { THEME } from "../theme"; + +export class WiredPinsSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [WiredPinsComponent]); + } + + update() { + // TODO + } + + drawWiresLayer(parameters) { + this.forEachMatchingEntityOnScreen(parameters, this.drawEntityPins.bind(this)); + } + + /** + * + * @param {DrawParameters} parameters + * @param {Entity} entity + */ + drawEntityPins(parameters, entity) { + const staticComp = entity.components.StaticMapEntity; + + if (!staticComp.shouldBeDrawn(parameters)) { + return; + } + + const pinsComp = entity.components.WiredPins; + + const slots = pinsComp.slots; + + for (let i = 0; i < slots.length; ++i) { + const slot = slots[i]; + const tile = staticComp.localTileToWorld(slot.pos); + + const worldPos = tile.toWorldSpaceCenterOfTile(); + + parameters.context.fillStyle = THEME.map.wires.pins[slot.type]; + parameters.context.beginCircle(worldPos.x, worldPos.y, 5); + parameters.context.fill(); + + parameters.context.lineWidth = 2; + parameters.context.fillStyle = "rgba(0, 0, 0, 0.1)"; + parameters.context.stroke(); + } + } +} diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index a544b399..231e4bcd 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -23,6 +23,13 @@ "chunkOverview": { "empty": "#444856", "filled": "#646b7d" + }, + + "wires": { + "overlay": "rgba(52, 150, 128, 0.5)", + "pins": { + "energyEjector": "#c425d7" + } } }, diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index 58231fa0..1356a2b6 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -24,6 +24,13 @@ "chunkOverview": { "empty": "#a6afbb", "filled": "#c5ccd6" + }, + + "wires": { + "overlay": "rgba(52, 150, 128, 0.8)", + "pins": { + "energyEjector": "#c425d7" + } } }, diff --git a/src/js/game/time/game_time.js b/src/js/game/time/game_time.js index 748ccb2d..7d0c310d 100644 --- a/src/js/game/time/game_time.js +++ b/src/js/game/time/game_time.js @@ -6,7 +6,6 @@ import { types, BasicSerializableObject } from "../../savegame/serialization"; import { RegularGameSpeed } from "./regular_game_speed"; import { BaseGameSpeed } from "./base_game_speed"; import { PausedGameSpeed } from "./paused_game_speed"; -import { performanceNow, Math_max } from "../../core/builtins"; import { FastForwardGameSpeed } from "./fast_forward_game_speed"; import { gGameSpeedRegistry } from "../../core/global_registries"; import { globalConfig } from "../../core/config"; @@ -55,7 +54,7 @@ export class GameTime extends BasicSerializableObject { * Fetches the new "real" time, called from the core once per frame, since performance now() is kinda slow */ updateRealtimeNow() { - this.realtimeSeconds = performanceNow() / 1000.0 + this.realtimeAdjust; + this.realtimeSeconds = performance.now() / 1000.0 + this.realtimeAdjust; } /** @@ -104,7 +103,7 @@ export class GameTime extends BasicSerializableObject { } // Check for too big pile of updates -> reduce it to 1 - let maxLogicSteps = Math_max( + let maxLogicSteps = Math.max( 3, (this.speed.getMaxLogicStepsInQueue() * this.root.dynamicTickrate.currentTickRate) / 60 ); @@ -209,7 +208,7 @@ export class GameTime extends BasicSerializableObject { } // Adjust realtime now difference so they match - this.realtimeAdjust = this.realtimeSeconds - performanceNow() / 1000.0; + this.realtimeAdjust = this.realtimeSeconds - performance.now() / 1000.0; this.updateRealtimeNow(); // Make sure we have a quantizied time diff --git a/src/js/game/tutorial_goals.js b/src/js/game/tutorial_goals.js index c7fa581b..8a66c184 100644 --- a/src/js/game/tutorial_goals.js +++ b/src/js/game/tutorial_goals.js @@ -23,6 +23,8 @@ export const enumHubGoalRewards = { reward_painter_quad: "reward_painter_quad", reward_storage: "reward_storage", + reward_wires: "reward_wires", + reward_blueprints: "reward_blueprints", reward_freeplay: "reward_freeplay", @@ -88,7 +90,7 @@ export const tutorialGoals = [ // 8 { shape: "RbRb----", // painter t2 - required: 1250, + required: 1000, reward: enumHubGoalRewards.reward_mixer, }, @@ -96,7 +98,7 @@ export const tutorialGoals = [ // Mixing (purple) { shape: "CpCpCpCp", // belts t3 - required: 1750, + required: 1400, reward: enumHubGoalRewards.reward_splitter_compact, }, @@ -104,7 +106,7 @@ export const tutorialGoals = [ // Star shape + cyan { shape: "ScScScSc", // miners t3 - required: 2250, + required: 1600, reward: enumHubGoalRewards.reward_stacker, }, @@ -112,7 +114,7 @@ export const tutorialGoals = [ // Stacker { shape: "CgScScCg", // processors t3 - required: 3000, + required: 1800, reward: enumHubGoalRewards.reward_miner_chainable, }, @@ -120,7 +122,7 @@ export const tutorialGoals = [ // Blueprints { shape: "CbCbCbRb:CwCwCwCw", - required: 4000, + required: 2000, reward: enumHubGoalRewards.reward_blueprints, }, @@ -155,7 +157,7 @@ export const tutorialGoals = [ // 17 { shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two varinats) - required: 100000, + required: 120000, reward: enumHubGoalRewards.reward_painter_quad, }, @@ -168,7 +170,7 @@ export const tutorialGoals = [ ]; if (G_IS_DEV) { - tutorialGoals.forEach(({ shape, required, reward }) => { + tutorialGoals.forEach(({ shape }) => { try { ShapeDefinition.fromShortKey(shape); } catch (ex) { diff --git a/src/js/game/upgrades.js b/src/js/game/upgrades.js index 500a2afe..6e0c7c64 100644 --- a/src/js/game/upgrades.js +++ b/src/js/game/upgrades.js @@ -12,7 +12,7 @@ export const UPGRADES = { improvement: 1, }, { - required: [{ shape: "--CuCu--", amount: 1500 }], + required: [{ shape: "--CuCu--", amount: 1200 }], improvement: 2, }, { @@ -42,7 +42,7 @@ export const UPGRADES = { improvement: 1, }, { - required: [{ shape: "Cu------", amount: 5500 }], + required: [{ shape: "Cu------", amount: 4000 }], improvement: 2, }, { @@ -102,7 +102,7 @@ export const UPGRADES = { improvement: 2, }, { - required: [{ shape: "WrWrWrWr", amount: 5000 }], + required: [{ shape: "WrWrWrWr", amount: 4000 }], improvement: 1, }, { diff --git a/src/js/platform/ad_providers/adinplay.js b/src/js/platform/ad_providers/adinplay.js index efad0574..00a08fcb 100644 --- a/src/js/platform/ad_providers/adinplay.js +++ b/src/js/platform/ad_providers/adinplay.js @@ -5,7 +5,6 @@ import { Application } from "../../application"; import { AdProviderInterface } from "../ad_provider"; import { createLogger } from "../../core/logging"; import { ClickDetector } from "../../core/click_detector"; -import { performanceNow } from "../../core/builtins"; import { clamp } from "../../core/utils"; import { T } from "../../translations"; @@ -52,7 +51,7 @@ export class AdinplayAdProvider extends AdProviderInterface { return ( this.getHasAds() && !this.videoAdResolveFunction && - performanceNow() - this.lastVideoAdShowTime > minimumTimeBetweenVideoAdsMs + performance.now() - this.lastVideoAdShowTime > minimumTimeBetweenVideoAdsMs ); } @@ -141,7 +140,7 @@ export class AdinplayAdProvider extends AdProviderInterface { showVideoAd() { assert(this.getHasAds(), "Called showVideoAd but ads are not supported!"); assert(!this.videoAdResolveFunction, "Video ad still running, can not show again!"); - this.lastVideoAdShowTime = performanceNow(); + this.lastVideoAdShowTime = performance.now(); document.body.appendChild(this.adContainerMainElement); this.adContainerMainElement.classList.add("visible"); this.adContainerMainElement.classList.remove("waitingForFinish"); @@ -167,7 +166,7 @@ export class AdinplayAdProvider extends AdProviderInterface { this.videoAdResolveTimer = null; // When the ad closed, also set the time - this.lastVideoAdShowTime = performanceNow(); + this.lastVideoAdShowTime = performance.now(); resolve(); }; diff --git a/src/js/platform/ad_providers/gamedistribution.js b/src/js/platform/ad_providers/gamedistribution.js index 431d6096..6ff031f0 100644 --- a/src/js/platform/ad_providers/gamedistribution.js +++ b/src/js/platform/ad_providers/gamedistribution.js @@ -3,7 +3,6 @@ import { Application } from "../../application"; /* typehints:end */ import { AdProviderInterface } from "../ad_provider"; -import { performanceNow } from "../../core/builtins"; import { createLogger } from "../../core/logging"; const minimumTimeBetweenVideoAdsMs = G_IS_DEV ? 1 : 5 * 60 * 1000; @@ -45,7 +44,7 @@ export class GamedistributionAdProvider extends AdProviderInterface { return ( this.getHasAds() && !this.videoAdResolveFunction && - performanceNow() - this.lastVideoAdShowTime > minimumTimeBetweenVideoAdsMs + performance.now() - this.lastVideoAdShowTime > minimumTimeBetweenVideoAdsMs ); } @@ -84,7 +83,7 @@ export class GamedistributionAdProvider extends AdProviderInterface { showVideoAd() { assert(this.getHasAds(), "Called showVideoAd but ads are not supported!"); assert(!this.videoAdResolveFunction, "Video ad still running, can not show again!"); - this.lastVideoAdShowTime = performanceNow(); + this.lastVideoAdShowTime = performance.now(); console.log("🎬 Gamedistribution: Start ad"); try { @@ -104,7 +103,7 @@ export class GamedistributionAdProvider extends AdProviderInterface { this.videoAdResolveTimer = null; // When the ad closed, also set the time - this.lastVideoAdShowTime = performanceNow(); + this.lastVideoAdShowTime = performance.now(); resolve(); }; diff --git a/src/js/platform/browser/google_analytics.js b/src/js/platform/browser/google_analytics.js index 3c54fbbd..782471d2 100644 --- a/src/js/platform/browser/google_analytics.js +++ b/src/js/platform/browser/google_analytics.js @@ -1,5 +1,4 @@ import { AnalyticsInterface } from "../analytics"; -import { Math_random, performanceNow } from "../../core/builtins"; import { createLogger } from "../../core/logging"; const logger = createLogger("ga"); diff --git a/src/js/platform/browser/wrapper.js b/src/js/platform/browser/wrapper.js index ab5ec327..d6c722ae 100644 --- a/src/js/platform/browser/wrapper.js +++ b/src/js/platform/browser/wrapper.js @@ -1,4 +1,3 @@ -import { Math_min } from "../../core/builtins"; import { globalConfig, IS_DEMO, IS_MOBILE } from "../../core/config"; import { createLogger } from "../../core/logging"; import { queryParamOptions } from "../../core/query_parameters"; @@ -130,7 +129,7 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface { return 1; } - const avgDims = Math_min(this.app.screenWidth, this.app.screenHeight); + const avgDims = Math.min(this.app.screenWidth, this.app.screenHeight); return clamp((avgDims / 1000.0) * 1.9, 0.1, 10); } diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 74734949..be81c695 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -215,7 +215,7 @@ export const allApplicationSettings = [ }), new EnumSetting("refreshRate", { - options: ["60", "100", "144", "165", "250", "500"], + options: ["60", "100", "144", "165", "250", G_IS_DEV ? "10" : "500"], valueGetter: rate => rate, textGetter: rate => rate + " Hz", category: categoryGame, diff --git a/src/js/savegame/savegame.js b/src/js/savegame/savegame.js index 359a48b5..1db813d7 100644 --- a/src/js/savegame/savegame.js +++ b/src/js/savegame/savegame.js @@ -14,6 +14,7 @@ import { SavegameInterface_V1001 } from "./schemas/1001"; import { SavegameInterface_V1002 } from "./schemas/1002"; import { SavegameInterface_V1003 } from "./schemas/1003"; import { SavegameInterface_V1004 } from "./schemas/1004"; +import { SavegameInterface_V1005 } from "./schemas/1005"; const logger = createLogger("savegame"); @@ -45,7 +46,7 @@ export class Savegame extends ReadWriteProxy { * @returns {number} */ static getCurrentVersion() { - return 1004; + return 1005; } /** @@ -104,6 +105,11 @@ export class Savegame extends ReadWriteProxy { data.version = 1004; } + if (data.version === 1004) { + SavegameInterface_V1005.migrate1004to1005(data); + data.version = 1005; + } + return ExplainedResult.good(); } @@ -203,7 +209,7 @@ export class Savegame extends ReadWriteProxy { // Construct a new serializer const serializer = new SavegameSerializer(); - // let timer = performanceNow(); + // let timer = performance.now(); const dump = serializer.generateDumpFromGameRoot(root); if (!dump) { return false; diff --git a/src/js/savegame/savegame_interface_registry.js b/src/js/savegame/savegame_interface_registry.js index 6144ca62..fb1df52f 100644 --- a/src/js/savegame/savegame_interface_registry.js +++ b/src/js/savegame/savegame_interface_registry.js @@ -5,6 +5,7 @@ import { SavegameInterface_V1001 } from "./schemas/1001"; import { SavegameInterface_V1002 } from "./schemas/1002"; import { SavegameInterface_V1003 } from "./schemas/1003"; import { SavegameInterface_V1004 } from "./schemas/1004"; +import { SavegameInterface_V1005 } from "./schemas/1005"; /** @type {Object.} */ export const savegameInterfaces = { @@ -13,6 +14,7 @@ export const savegameInterfaces = { 1002: SavegameInterface_V1002, 1003: SavegameInterface_V1003, 1004: SavegameInterface_V1004, + 1005: SavegameInterface_V1005, }; const logger = createLogger("savegame_interface_registry"); diff --git a/src/js/savegame/savegame_manager.js b/src/js/savegame/savegame_manager.js index 2c20c819..e3052806 100644 --- a/src/js/savegame/savegame_manager.js +++ b/src/js/savegame/savegame_manager.js @@ -3,8 +3,6 @@ import { createLogger } from "../core/logging"; import { ReadWriteProxy } from "../core/read_write_proxy"; import { globalConfig } from "../core/config"; import { Savegame } from "./savegame"; -import { Math_floor } from "../core/builtins"; - const logger = createLogger("savegame_manager"); const Rusha = require("rusha"); diff --git a/src/js/savegame/savegame_serializer.js b/src/js/savegame/savegame_serializer.js index 52a59528..1a2c8d74 100644 --- a/src/js/savegame/savegame_serializer.js +++ b/src/js/savegame/savegame_serializer.js @@ -3,7 +3,6 @@ import { Component } from "../game/component"; import { GameRoot } from "../game/root"; /* typehints:end */ -import { JSON_stringify } from "../core/builtins"; import { ExplainedResult } from "../core/explained_result"; import { createLogger } from "../core/logging"; // import { BuildingComponent } from "../components/impl/building"; @@ -40,6 +39,7 @@ export class SavegameSerializer { hubGoals: root.hubGoals.serialize(), pinnedShapes: root.hud.parts.pinnedShapes.serialize(), waypoints: root.hud.parts.waypoints.serialize(), + beltPaths: root.systemMgr.systems.belt.serializePaths(), }; data.entities = this.internal.serializeEntityArray(root.entityMgr.entities); @@ -87,7 +87,7 @@ export class SavegameSerializer { // Verify components if (!entity.components) { return ExplainedResult.bad( - "Entity is missing key 'components': " + JSON_stringify(entity) + "Entity is missing key 'components': " + JSON.stringify(entity) ); } const components = entity.components; @@ -140,6 +140,7 @@ export class SavegameSerializer { errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes); errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints); errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities); + errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths); // Check for errors if (errorReason) { diff --git a/src/js/savegame/savegame_typedefs.js b/src/js/savegame/savegame_typedefs.js index 6211150f..642865cd 100644 --- a/src/js/savegame/savegame_typedefs.js +++ b/src/js/savegame/savegame_typedefs.js @@ -5,6 +5,10 @@ import { Entity } from "../game/entity"; * }} SavegameStats */ +/** + * + */ + /** * @typedef {{ * camera: any, @@ -14,7 +18,8 @@ import { Entity } from "../game/entity"; * hubGoals: any, * pinnedShapes: any, * waypoints: any, - * entities: Array + * entities: Array, + * beltPaths: Array * }} SerializedGame */ diff --git a/src/js/savegame/schemas/1005.js b/src/js/savegame/schemas/1005.js new file mode 100644 index 00000000..0380f8eb --- /dev/null +++ b/src/js/savegame/schemas/1005.js @@ -0,0 +1,45 @@ +import { createLogger } from "../../core/logging.js"; +import { SavegameInterface_V1004 } from "./1004.js"; + +const schema = require("./1005.json"); +const logger = createLogger("savegame_interface/1005"); + +export class SavegameInterface_V1005 extends SavegameInterface_V1004 { + getVersion() { + return 1005; + } + + getSchemaUncached() { + return schema; + } + + /** + * @param {import("../savegame_typedefs.js").SavegameData} data + */ + static migrate1004to1005(data) { + logger.log("Migrating 1004 to 1005"); + const dump = data.dump; + if (!dump) { + return true; + } + + // just reset belt paths for now + dump.beltPaths = []; + + const entities = dump.entities; + + // clear ejector slots + for (let i = 0; i < entities.length; ++i) { + const entity = entities[i]; + const itemEjector = entity.components.ItemEjector; + if (itemEjector) { + const slots = itemEjector.slots; + for (let k = 0; k < slots.length; ++k) { + const slot = slots[k]; + slot.item = null; + slot.progress = 0; + } + } + } + } +} diff --git a/src/js/savegame/schemas/1005.json b/src/js/savegame/schemas/1005.json new file mode 100644 index 00000000..6682f615 --- /dev/null +++ b/src/js/savegame/schemas/1005.json @@ -0,0 +1,5 @@ +{ + "type": "object", + "required": [], + "additionalProperties": true +} diff --git a/src/js/savegame/serialization.js b/src/js/savegame/serialization.js index 7ab6f678..aea2f944 100644 --- a/src/js/savegame/serialization.js +++ b/src/js/savegame/serialization.js @@ -1,4 +1,3 @@ -import { JSON_stringify } from "../core/builtins"; import { BaseDataType, TypeArray, @@ -223,7 +222,7 @@ export function serializeSchema(obj, schema, mergeWith = {}) { ); } if (!schema[key]) { - assert(false, "Invalid schema (bad key '" + key + "'): " + JSON_stringify(schema)); + assert(false, "Invalid schema (bad key '" + key + "'): " + JSON.stringify(schema)); } if (G_IS_DEV) { diff --git a/src/js/savegame/serialization_data_types.js b/src/js/savegame/serialization_data_types.js index 332dc274..86b177c1 100644 --- a/src/js/savegame/serialization_data_types.js +++ b/src/js/savegame/serialization_data_types.js @@ -5,8 +5,6 @@ import { BasicSerializableObject } from "./serialization"; import { Vector } from "../core/vector"; import { round4Digits, schemaObject, accessNestedPropertyReverse } from "../core/utils"; -import { JSON_stringify } from "../core/builtins"; - export const globalJsonSchemaDefs = {}; /** @@ -128,7 +126,7 @@ export class BaseDataType { "serialization verify failed: " + errorCode + " [value " + - JSON_stringify(value).substr(0, 100) + + JSON.stringify(value).substr(0, 100) + "]" ); } diff --git a/translations/README.md b/translations/README.md index e799926c..6e46a6fe 100644 --- a/translations/README.md +++ b/translations/README.md @@ -27,6 +27,7 @@ The base translation is `base-en.yaml`. It will always contain the latest phrase - [Arabic](base-ar.yaml) - [Norwegian](base-no.yaml) - [Kroatian](base-hr.yaml) +- [Danish](base-da.yaml) (If you want to translate into a new language, see below!) diff --git a/translations/base-da.yaml b/translations/base-da.yaml new file mode 100644 index 00000000..930e5f29 --- /dev/null +++ b/translations/base-da.yaml @@ -0,0 +1,848 @@ +# +# GAME TRANSLATIONS +# +# Contributing: +# +# If you want to contribute, please make a pull request on this respository +# and I will have a look. +# +# Placeholders: +# +# Do *not* replace placeholders! Placeholders have a special syntax like +# `Hotkey: `. They are encapsulated within angle brackets. The correct +# translation for this one in German for example would be: `Taste: ` (notice +# how the placeholder stayed '' and was not replaced!) +# +# Adding a new language: +# +# If you want to add a new language, ask me in the discord and I will setup +# the basic structure so the game also detects it. +# + +steamPage: + # This is the short text appearing on the steam page + shortText: shapez.io is a game about building factories to automate the creation and combination of increasingly complex shapes within an infinite map. + + # This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page. + # NOTICE: + # - Do not translate the first line (This is the gif image at the start of the store) + # - Please keep the markup (Stuff like [b], [list] etc) in the same format + longText: >- + [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] + + shapez.io is a game about building factories to automate the creation and combination of shapes. Deliver the requested, increasingly complex shapes to progress within the game and unlock upgrades to speed up your factory. + + Since the demand raises you will have to scale up your factory to fit the needs - Don't forget about resources though, you will have to expand in the [b]infinite map[/b]! + + Since shapes can get boring soon you need to mix colors and paint your shapes with it - Combine red, green and blue color resources to produce different colors and paint shapes with it to satisfy the demand. + + This game features 18 levels (Which should keep you busy for hours already!) but I'm constantly adding new content - There is a lot planned! + + + [b]Standalone Advantages[/b] + + [list] + [*] Waypoints + [*] Unlimited Savegames + [*] Dark Mode + [*] More settings + [*] Allow me to further develop shapez.io ❤️ + [*] More features in the future! + [/list] + + [b]Planned features & Community suggestions[/b] + + This game is open source - Anybody can contribute! Besides of that, I listen [b]a lot[/b] to the community! I try to read all suggestions and take as much feedback into account as possible. + + [list] + [*] Story mode where buildings cost shapes + [*] More levels & buildings (standalone exclusive) + [*] Different maps, and maybe map obstacles + [*] Configurable map creation (Edit number and size of patches, seed, and more) + [*] More types of shapes + [*] More performance improvements (Although the game already runs pretty good!) + [*] Color blind mode + [*] And much more! + [/list] + + Be sure to check out my trello board for the full roadmap! https://trello.com/b/ISQncpJP/shapezio + +global: + loading: Loading + error: Error + + # How big numbers are rendered, e.g. "10,000" + thousandsDivider: "," + + # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. + suffix: + thousands: k + millions: M + billions: B + trillions: T + + # Shown for infinitely big numbers + infinite: inf + + time: + # Used for formatting past time dates + oneSecondAgo: one second ago + xSecondsAgo: seconds ago + oneMinuteAgo: one minute ago + xMinutesAgo: minutes ago + oneHourAgo: one hour ago + xHoursAgo: hours ago + oneDayAgo: one day ago + xDaysAgo: days ago + + # Short formats for times, e.g. '5h 23m' + secondsShort: s + minutesAndSecondsShort: m s + hoursAndMinutesShort: h m + + xMinutes: minutes + + keys: + tab: TAB + control: CTRL + alt: ALT + escape: ESC + shift: SHIFT + space: SPACE + +demoBanners: + # This is the "advertisement" shown in the main menu and other various places + title: Demo Version + intro: >- + Get the standalone to unlock all features! + +mainMenu: + play: Play + continue: Continue + newGame: New Game + changelog: Changelog + subreddit: Reddit + importSavegame: Import + openSourceHint: This game is open source! + discordLink: Official Discord Server + helpTranslate: Help translate! + madeBy: Made by + + # This is shown when using firefox and other browsers which are not supported. + browserWarning: >- + Sorry, but the game is known to run slow on your browser! Get the standalone version or download chrome for the full experience. + + savegameLevel: Level + savegameLevelUnknown: Unknown Level + + contests: + contest_01_03062020: + title: "Contest #01" + desc: Win $25 for the coolest base! + longDesc: >- + To give something back to you, I thought it would be cool to make weekly contests! +

+ This weeks topic: Build the coolest base! +

+ Here's the deal:
+ +
+ I'm looking forward to seeing your awesome creations! + + showInfo: View + contestOver: This contest has ended - Join the discord to get noticed about new contests! + +dialogs: + buttons: + ok: OK + delete: Delete + cancel: Cancel + later: Later + restart: Restart + reset: Reset + getStandalone: Get Standalone + deleteGame: I know what I do + viewUpdate: View Update + showUpgrades: Show Upgrades + showKeybindings: Show Keybindings + + importSavegameError: + title: Import Error + text: >- + Failed to import your savegame: + + importSavegameSuccess: + title: Savegame Imported + text: >- + Your savegame has been successfully imported. + + gameLoadFailure: + title: Game is broken + text: >- + Failed to load your savegame: + + confirmSavegameDelete: + title: Confirm deletion + text: >- + Are you sure you want to delete the game? + + savegameDeletionError: + title: Failed to delete + text: >- + Failed to delete the savegame: + + restartRequired: + title: Restart required + text: >- + You need to restart the game to apply the settings. + + editKeybinding: + title: Change Keybinding + desc: Press the key or mouse button you want to assign, or escape to cancel. + + resetKeybindingsConfirmation: + title: Reset keybindings + desc: This will reset all keybindings to their default values. Please confirm. + + keybindingsResetOk: + title: Keybindings reset + desc: The keybindings have been reset to their respective defaults! + + featureRestriction: + title: Demo Version + desc: You tried to access a feature () which is not available in the demo. Consider to get the standalone for the full experience! + + oneSavegameLimit: + title: Limited savegames + desc: You can only have one savegame at a time in the demo version. Please remove the existing one or get the standalone! + + updateSummary: + title: New update! + desc: >- + Here are the changes since you last played: + + upgradesIntroduction: + title: Unlock Upgrades + desc: >- + All shapes you produce can be used to unlock upgrades - Don't destroy your old factories! + The upgrades tab can be found on the top right corner of the screen. + + massDeleteConfirm: + title: Confirm delete + desc: >- + You are deleting a lot of buildings ( to be exact)! Are you sure you want to do this? + + massCutConfirm: + title: Confirm cut + desc: >- + You are cutting a lot of buildings ( to be exact)! Are you sure you want to do this? + + blueprintsNotUnlocked: + title: Not unlocked yet + desc: >- + Complete level 12 to unlock Blueprints! + + keybindingsIntroduction: + title: Useful keybindings + desc: >- + This game has a lot of keybindings which make it easier to build big factories. + Here are a few, but be sure to check out the keybindings!

+ CTRL + Drag: Select an area.
+ SHIFT: Hold to place multiple of one building.
+ ALT: Invert orientation of placed belts.
+ + createMarker: + title: New Marker + desc: Give it a meaningful name, you can also include a short key of a shape (Which you can generate here) + + markerDemoLimit: + desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers! + + exportScreenshotWarning: + title: Export screenshot + desc: You requested to export your base as a screenshot. Please note that this can be quite slow for a big base and even crash your game! + +ingame: + # This is shown in the top left corner and displays useful keybindings in + # every situation + keybindingsOverlay: + moveMap: Move + selectBuildings: Select area + stopPlacement: Stop placement + rotateBuilding: Rotate building + placeMultiple: Place multiple + reverseOrientation: Reverse orientation + disableAutoOrientation: Disable auto orientation + toggleHud: Toggle HUD + placeBuilding: Place building + createMarker: Create Marker + delete: Delete + pasteLastBlueprint: Paste last blueprint + lockBeltDirection: Enable belt planner + plannerSwitchSide: Flip planner side + cutSelection: Cut + copySelection: Copy + clearSelection: Clear Selection + pipette: Pipette + switchLayers: Switch Layers + + # Names of the colors, used for the color blind mode + colors: + red: Red + green: Green + blue: Blue + yellow: Yellow + purple: Purple + cyan: Cyan + white: White + uncolored: No color + + # Everything related to placing buildings (I.e. as soon as you selected a building + # from the toolbar) + buildingPlacement: + # Buildings can have different variants which are unlocked at later levels, + # and this is the hint shown when there are multiple variants available. + cycleBuildingVariants: Press to cycle variants. + + # Shows the hotkey in the ui, e.g. "Hotkey: Q" + hotkeyLabel: >- + Hotkey: + + infoTexts: + speed: Speed + range: Range + storage: Storage + oneItemPerSecond: 1 item / second + itemsPerSecond: items / s + itemsPerSecondDouble: (x2) + + tiles: tiles + + # The notification when completing a level + levelCompleteNotification: + # is replaced by the actual level, so this gets 'Level 03' for example. + levelTitle: Level + completed: Completed + unlockText: Unlocked ! + buttonNextLevel: Next Level + + # Notifications on the lower right + notifications: + newUpgrade: A new upgrade is available! + gameSaved: Your game has been saved. + + # The "Upgrades" window + shop: + title: Upgrades + buttonUnlock: Upgrade + + # Gets replaced to e.g. "Tier IX" + tier: Tier + + # The roman number for each tier + tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] + + maximumLevel: MAXIMUM LEVEL (Speed x) + + # The "Statistics" window + statistics: + title: Statistics + dataSources: + stored: + title: Stored + description: Displaying amount of stored shapes in your central building. + produced: + title: Produced + description: Displaying all shapes your whole factory produces, including intermediate products. + delivered: + title: Delivered + description: Displaying shapes which are delivered to your central building. + noShapesProduced: No shapes have been produced so far. + + # Displays the shapes per minute, e.g. '523 / m' + shapesPerMinute: / m + + # Settings menu, when you press "ESC" + settingsMenu: + playtime: Playtime + + buildingsPlaced: Buildings + beltsPlaced: Belts + + buttons: + continue: Continue + settings: Settings + menu: Return to menu + + # Bottom left tutorial hints + tutorialHints: + title: Need help? + showHint: Show hint + hideHint: Close + + # When placing a blueprint + blueprintPlacer: + cost: Cost + + # Map markers + waypoints: + waypoints: Markers + hub: HUB + description: Left-click a marker to jump to it, right-click to delete it.

Press to create a marker from the current view, or right-click to create a marker at the selected location. + creationSuccessNotification: Marker has been created. + + # Shape viewer + shapeViewer: + title: Layers + empty: Empty + copyKey: Copy Key + + # Interactive tutorial + interactiveTutorial: + title: Tutorial + hints: + 1_1_extractor: Place an extractor on top of a circle shape to extract it! + 1_2_conveyor: >- + Connect the extractor with a conveyor belt to your hub!

Tip: Click and drag the belt with your mouse! + + 1_3_expand: >- + This is NOT an idle game! Build more extractors and belts to finish the goal quicker.

Tip: Hold SHIFT to place multiple extractors, and use R to rotate them. + +# All shop upgrades +shopUpgrades: + belt: + name: Belts, Distributor & Tunnels + description: Speed x → x + miner: + name: Extraction + description: Speed x → x + processors: + name: Cutting, Rotating & Stacking + description: Speed x → x + painting: + name: Mixing & Painting + description: Speed x → x + +# Buildings and their name / description +buildings: + hub: + deliver: Deliver + toUnlock: to unlock + levelShortcut: LVL + + belt: + default: + name: &belt Conveyor Belt + description: Transports items, hold and drag to place multiple. + + miner: # Internal name for the Extractor + default: + name: &miner Extractor + description: Place over a shape or color to extract it. + + chainable: + name: Extractor (Chain) + description: Place over a shape or color to extract it. Can be chained. + + underground_belt: # Internal name for the Tunnel + default: + name: &underground_belt Tunnel + description: Allows to tunnel resources under buildings and belts. + + tier2: + name: Tunnel Tier II + description: Allows to tunnel resources under buildings and belts. + + splitter: # Internal name for the Balancer + default: + name: &splitter Balancer + description: Multifunctional - Evenly distributes all inputs onto all outputs. + + compact: + name: Merger (compact) + description: Merges two conveyor belts into one. + + compact-inverse: + name: Merger (compact) + description: Merges two conveyor belts into one. + + cutter: + default: + name: &cutter Cutter + description: Cuts shapes from top to bottom and outputs both halfs. If you use only one part, be sure to destroy the other part or it will stall! + quad: + name: Cutter (Quad) + description: Cuts shapes into four parts. If you use only one part, be sure to destroy the other parts or it will stall! + + rotater: + default: + name: &rotater Rotate + description: Rotates shapes clockwise by 90 degrees. + ccw: + name: Rotate (CCW) + description: Rotates shapes counter clockwise by 90 degrees. + + stacker: + default: + name: &stacker Stacker + description: Stacks both items. If they can not be merged, the right item is placed above the left item. + + mixer: + default: + name: &mixer Color Mixer + description: Mixes two colors using additive blending. + + painter: + default: + name: &painter Painter + description: &painter_desc Colors the whole shape on the left input with the color from the top input. + + mirrored: + name: *painter + description: *painter_desc + + double: + name: Painter (Double) + description: Colors the shapes on the left inputs with the color from the top input. + quad: + name: Painter (Quad) + description: Allows to color each quadrant of the shape with a different color. + + trash: + default: + name: &trash Trash + description: Accepts inputs from all sides and destroys them. Forever. + + storage: + name: Storage + description: Stores excess items, up to a given capacity. Can be used as an overflow gate. + + energy_generator: + deliver: Deliver + toGenerateEnergy: For energy + + default: + name: &energy_generator Energy Generator + description: Generates energy by consuming shapes. Each energy generator requires a different shapes. + +storyRewards: + # Those are the rewards gained from completing the store + reward_cutter_and_trash: + title: Cutting Shapes + desc: You just unlocked the cutter - it cuts shapes half from top to bottom regardless of its orientation!

Be sure to get rid of the waste, or otherwise it will stall - For this purpose I gave you a trash, which destroys everything you put into it! + + reward_rotater: + title: Rotating + desc: The rotater has been unlocked! It rotates shapes clockwise by 90 degrees. + + reward_painter: + title: Painting + desc: >- + The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a color blind mode in the settings! + + reward_mixer: + title: Color Mixing + desc: The mixer has been unlocked - Combine two colors using additive blending with this building! + + reward_stacker: + title: Combiner + desc: You can now combine shapes with the combiner! Both inputs are combined, and if they can be put next to each other, they will be fused. If not, the right input is stacked on top of the left input! + + reward_splitter: + title: Splitter/Merger + desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging items onto multiple belts!

+ + reward_tunnel: + title: Tunnel + desc: The tunnel has been unlocked - You can now tunnel items through belts and buildings with it! + + reward_rotater_ccw: + title: CCW Rotating + desc: You have unlocked a variant of the rotater - It allows to rotate counter clockwise! To build it, select the rotater and press 'T' to cycle its variants! + + reward_miner_chainable: + title: Chaining Extractor + desc: You have unlocked the chaining extractor! It can forward its resources to other extractors so you can more efficiently extract resources! + + reward_underground_belt_tier_2: + title: Tunnel Tier II + desc: You have unlocked a new variant of the tunnel - It has a bigger range, and you can also mix-n-match those tunnels now! + + reward_splitter_compact: + title: Compact Balancer + desc: >- + You have unlocked a compact variant of the balancer - It accepts two inputs and merges them into one! + + reward_cutter_quad: + title: Quad Cutting + desc: You have unlocked a variant of the cutter - It allows you to cut shapes in four parts instead of just two! + + reward_painter_double: + title: Double Painting + desc: You have unlocked a variant of the painter - It works as the regular painter but processes two shapes at once consuming just one color instead of two! + + reward_painter_quad: + title: Quad Painting + desc: You have unlocked a variant of the painter - It allows to paint each part of the shape individually! + + reward_storage: + title: Storage Buffer + desc: You have unlocked a variant of the trash - It allows to store items up to a given capacity! + + reward_freeplay: + title: Freeplay + desc: You did it! You unlocked the free-play mode! This means that shapes are now randomly generated! (No worries, more content is planned for the standalone!) + + reward_blueprints: + title: Blueprints + desc: You can now copy and paste parts of your factory! Select an area (Hold CTRL, then drag with your mouse), and press 'C' to copy it.

Pasting it is not free, you need to produce blueprint shapes to afford it! (Those you just delivered). + + # Special reward, which is shown when there is no reward actually + no_reward: + title: Next level + desc: >- + This level gave you no reward, but the next one will!

PS: Better don't destroy your existing factory - You need all those shapes later again to unlock upgrades! + + reward_wires: + title: Wires + desc: TODO + + no_reward_freeplay: + title: Next level + desc: >- + Congratulations! By the way, more content is planned for the standalone! + +settings: + title: Settings + categories: + game: Game + app: Application + + versionBadges: + dev: Development + staging: Staging + prod: Production + buildDate: Built + + labels: + uiScale: + title: Interface scale + description: >- + Changes the size of the user interface. The interface will still scale based on your device resolution, but this setting controls the amount of scale. + scales: + super_small: Super small + small: Small + regular: Regular + large: Large + huge: Huge + + autosaveInterval: + title: Autosave Interval + description: >- + Controls how often the game saves automatically. You can also disable it entirely here. + + intervals: + one_minute: 1 Minute + two_minutes: 2 Minutes + five_minutes: 5 Minutes + ten_minutes: 10 Minutes + twenty_minutes: 20 Minutes + disabled: Disabled + + scrollWheelSensitivity: + title: Zoom sensitivity + description: >- + Changes how sensitive the zoom is (Either mouse wheel or trackpad). + sensitivity: + super_slow: Super slow + slow: Slow + regular: Regular + fast: Fast + super_fast: Super fast + + movementSpeed: + title: Movement speed + description: >- + Changes how fast the view moves when using the keyboard. + speeds: + super_slow: Super slow + slow: Slow + regular: Regular + fast: Fast + super_fast: Super Fast + extremely_fast: Extremely Fast + + language: + title: Language + description: >- + Change the language. All translations are user contributed and might be incomplete! + + enableColorBlindHelper: + title: Color Blind Mode + description: >- + Enables various tools which allow to play the game if you are color blind. + + fullscreen: + title: Fullscreen + description: >- + It is recommended to play the game in fullscreen to get the best experience. Only available in the standalone. + + soundsMuted: + title: Mute Sounds + description: >- + If enabled, mutes all sound effects. + + musicMuted: + title: Mute Music + description: >- + If enabled, mutes all music. + + theme: + title: Game theme + description: >- + Choose the game theme (light / dark). + themes: + dark: Dark + light: Light + + refreshRate: + title: Simulation Target + description: >- + If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow. + + alwaysMultiplace: + title: Multiplace + description: >- + If enabled, all buildings will stay selected after placement until you cancel it. This is equivalent to holding SHIFT permanently. + + offerHints: + title: Hints & Tutorials + description: >- + Whether to offer hints and tutorials while playing. Also hides certain UI elements onto a given level to make it easier to get into the game. + + enableTunnelSmartplace: + title: Smart Tunnels + description: >- + When enabled, placing tunnels will automatically remove unnecessary belts. This also enables to drag tunnels and excess tunnels will get removed. + + vignette: + title: Vignette + description: >- + Enables the vignette which darkens the screen corners and makes text easier to read. + + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. This may be more comfortable if you frequently switch between placing different building types. + + compactBuildingInfo: + title: Compact Building Infos + description: >- + Shortens info boxes for buildings by only showing their ratios. Otherwise a description and image is shown. + + disableCutDeleteWarnings: + title: Disable Cut/Delete Warnings + description: >- + Disable the warning dialogs brought up when cutting/deleting more than 100 entities. + +keybindings: + title: Keybindings + hint: >- + Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options. + + resetKeybindings: Reset Keybindings + + categoryLabels: + general: Application + ingame: Game + navigation: Navigating + placement: Placement + massSelect: Mass Select + buildings: Building Shortcuts + placementModifiers: Placement Modifiers + + mappings: + confirm: Confirm + back: Back + mapMoveUp: Move Up + mapMoveRight: Move Right + mapMoveDown: Move Down + mapMoveLeft: Move Left + mapMoveFaster: Move Faster + centerMap: Center Map + + mapZoomIn: Zoom in + mapZoomOut: Zoom out + createMarker: Create Marker + + menuOpenShop: Upgrades + menuOpenStats: Statistics + + toggleHud: Toggle HUD + toggleFPSInfo: Toggle FPS and Debug Info + switchLayers: Switch layers + exportScreenshot: Export whole Base as Image + belt: *belt + splitter: *splitter + underground_belt: *underground_belt + miner: *miner + cutter: *cutter + rotater: *rotater + stacker: *stacker + mixer: *mixer + energy_generator: *energy_generator + painter: *painter + trash: *trash + + pipette: Pipette + rotateWhilePlacing: Rotate + rotateInverseModifier: >- + Modifier: Rotate CCW instead + cycleBuildingVariants: Cycle Variants + confirmMassDelete: Delete area + pasteLastBlueprint: Paste last blueprint + cycleBuildings: Cycle Buildings + lockBeltDirection: Enable belt planner + switchDirectionLockSide: >- + Planner: Switch side + + massSelectStart: Hold and drag to start + massSelectSelectMultiple: Select multiple areas + massSelectCopy: Copy area + massSelectCut: Cut area + + placementDisableAutoOrientation: Disable automatic orientation + placeMultiple: Stay in placement mode + placeInverse: Invert automatic belt orientation + +about: + title: About this Game + body: >- + This game is open source and developed by Tobias Springer (this is me).

+ + If you want to contribute, check out shapez.io on github.

+ + This game wouldn't have been possible without the great discord community around my games - You should really join the discord server!

+ + The soundtrack was made by Peppsen - He's awesome.

+ + Finally, huge thanks to my best friend Niklas - Without our factorio sessions this game would never have existed. + +changelog: + title: Changelog + +demo: + features: + restoringGames: Restoring savegames + importingGames: Importing savegames + oneGameLimit: Limited to one savegame + customizeKeybindings: Customizing Keybindings + exportingBase: Exporting whole Base as Image + + settingNotAvailable: Not available in the demo. diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 39eb3cd3..930e5f29 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -290,6 +290,7 @@ ingame: copySelection: Copy clearSelection: Clear Selection pipette: Pipette + switchLayers: Switch Layers # Names of the colors, used for the color blind mode colors: @@ -522,6 +523,14 @@ buildings: name: Storage description: Stores excess items, up to a given capacity. Can be used as an overflow gate. + energy_generator: + deliver: Deliver + toGenerateEnergy: For energy + + default: + name: &energy_generator Energy Generator + description: Generates energy by consuming shapes. Each energy generator requires a different shapes. + storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: @@ -600,6 +609,10 @@ storyRewards: desc: >- This level gave you no reward, but the next one will!

PS: Better don't destroy your existing factory - You need all those shapes later again to unlock upgrades! + reward_wires: + title: Wires + desc: TODO + no_reward_freeplay: title: Next level desc: >- @@ -773,6 +786,7 @@ keybindings: toggleHud: Toggle HUD toggleFPSInfo: Toggle FPS and Debug Info + switchLayers: Switch layers exportScreenshot: Export whole Base as Image belt: *belt splitter: *splitter @@ -782,6 +796,7 @@ keybindings: rotater: *rotater stacker: *stacker mixer: *mixer + energy_generator: *energy_generator painter: *painter trash: *trash diff --git a/translations/base-fr.yaml b/translations/base-fr.yaml index 7140e456..77b6b402 100644 --- a/translations/base-fr.yaml +++ b/translations/base-fr.yaml @@ -533,7 +533,7 @@ storyRewards: reward_painter: title: Peintre desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a color blind mode in the settings! + Le peintre a été débloqué - Extrayez des pigments de couleur (comme vous le faites avec les formes) et combinez les avec une forme dans un peintre pour les colorier !

PS: Si vous êtes daltonien, il y a un mode daltonien paramétrable dans les préférences ! reward_mixer: title: Mélangeur de couleurs @@ -728,11 +728,10 @@ settings: title: Mode Daltonien description: Active divers outils qui permettent de jouer à ce jeu si vous êtes daltonien. rotationByBuilding: - title: Rotation by building type - description: >- - Each building type remembers the rotation you last set it to individually. - This may be more comfortable if you frequently switch between placing - different building types. + title: Rotation par catégorie de bâtiment + description: >- + Chaque catégorie de bâtiment enregistre le sens de rotation que vous lui avez assigné la dernière fois, de manière individuelle. + Cela sera sans doute plus confortable si vous alternez fréquemment entre le placement de différents types de bâtiments. keybindings: title: Contrôles diff --git a/translations/base-nl.yaml b/translations/base-nl.yaml index 2deb72b7..178fba98 100644 --- a/translations/base-nl.yaml +++ b/translations/base-nl.yaml @@ -694,7 +694,7 @@ settings: Wanneer dit aan staat zullen tunnels automatisch onnodige lopende banden verwijderen. Ook kun je dan tunnels slepen en onnodige tunnels worden ook verwijderd. vignette: - title: Vignette + title: Vignet description: >- Schakelt de vignet in, wat de hoeken van het scherm donkerder maakt zodat de tekst makkelijker te lezen is. @@ -717,7 +717,7 @@ settings: Informatie weergeven bij gebouwen wordt beperkt tot alleen hun 'ratios'. Anders zie je een beschrijving en een afbeelding. disableCutDeleteWarnings: - title: Schakel knip/delete waarschuwingen uit. + title: Schakel knip/delete waarschuwingen uit description: >- Schakelt de waarschuwing uit die wordt weergegeven wanneer je meer dan 100 dingen probeert te knippen/deleten. @@ -725,11 +725,11 @@ settings: title: Kleurenblindmodus description: Schakelt verschillende hulpmiddelen in zodat je het spel alsnog kunt spelen wanneer je kleurenblind bent. rotationByBuilding: - title: Rotation by building type + title: Rotatie per type gebouw description: >- - Each building type remembers the rotation you last set it to individually. - This may be more comfortable if you frequently switch between placing - different building types. + Elk type gebouw onthoud apart de rotatie waarin je het voor het laatst geplaatst hebt. + Dit kan handig zijn wanneer je vaak tussen verschillende + soorten gebouwen wisselt. keybindings: title: Sneltoetsen diff --git a/translations/base-no.yaml b/translations/base-no.yaml index b3c1bc84..dd971ec1 100644 --- a/translations/base-no.yaml +++ b/translations/base-no.yaml @@ -529,7 +529,7 @@ storyRewards: reward_painter: title: Maling desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a color blind mode in the settings! + Maleren har blitt tilgjengelig - Hent ut fargeressurser (på samme måte som du gjør med objekter) og kombiner det med et objekt i maleren for å male de!

PS: Hvis du er fargeblind, så er det en modus for fargeblinde i instillinger! reward_mixer: title: Fargemikser @@ -727,11 +727,11 @@ settings: title: Fargeblind Modus description: Aktiverer forskjellige verktøy som lar deg spille spillet om du er fargeblind. rotationByBuilding: - title: Rotation by building type + title: Roter basert på bygningstype description: >- - Each building type remembers the rotation you last set it to individually. - This may be more comfortable if you frequently switch between placing - different building types. + Hver bygning type husker rotasjonen du sist brukte individuelt. + Dette kan være mer komfortabelt hvis du ofte veksler mellom plassering + av forskjellige bygninger. keybindings: title: Hurtigtaster diff --git a/translations/base-pl.yaml b/translations/base-pl.yaml index 8f66d2a0..3b685e4d 100644 --- a/translations/base-pl.yaml +++ b/translations/base-pl.yaml @@ -546,7 +546,7 @@ storyRewards: reward_painter: title: Malowanie desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a color blind mode in the settings! + Odblokowano nową maszynę: Maszyna Malująca - wydobądź kilka pigmentów (identycznie jak kształty) i połącz je z kształtami aby je pomalować!

PS: Jeśli nie widzisz kolorów, w ustawieniach znajduje się color blind mode! reward_mixer: title: Mieszanie diff --git a/translations/base-pt-PT.yaml b/translations/base-pt-PT.yaml index a9f93efc..2aeb7efa 100644 --- a/translations/base-pt-PT.yaml +++ b/translations/base-pt-PT.yaml @@ -529,7 +529,7 @@ storyRewards: reward_painter: title: Pintura desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a color blind mode in the settings! + O Pintor foi desbloqueado - Extrai alguns pigmentos coloridos (tal como fazes com as formas) e combina-os com uma forma no pintor para a colorir!

PS: Se fores daltónico, existe um modo daltónico nas definições! reward_mixer: title: Mistura de Cores @@ -726,11 +726,11 @@ settings: title: Modo Daltónico description: Ativa várias ferramentas para daltónicos. rotationByBuilding: - title: Rotation by building type + title: Rotação por tipo de construção description: >- - Each building type remembers the rotation you last set it to individually. - This may be more comfortable if you frequently switch between placing - different building types. + Cada construção lembra-se da última rotação que definiste. + Esta definição pode ser mais confortável se alternas frequentemente + a colocação de diferentes tipos de construções. keybindings: title: Atalhos diff --git a/translations/base-ru.yaml b/translations/base-ru.yaml index 1e0fee9e..73679e16 100644 --- a/translations/base-ru.yaml +++ b/translations/base-ru.yaml @@ -532,7 +532,7 @@ storyRewards: reward_painter: title: Покраска desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a color blind mode in the settings! + Разблокирован покрасчик! Добудьте краситель из жилы (так же как и фигуры) и объедините его с фигурой в покрасчике, чтобы раскрасить ее!

PS: Если вы дальтоник, то в настройках есть Режим Дальтоника! reward_mixer: title: Смешивание Цветов @@ -728,11 +728,11 @@ settings: title: Режим Дальтоника description: Включает различные инструменты, которые позволяют играть в игру дальтоникам. rotationByBuilding: - title: Rotation by building type + title: Поворот по типу здания description: >- - Each building type remembers the rotation you last set it to individually. - This may be more comfortable if you frequently switch between placing - different building types. + Каждый тип здания запоминает поворот, который в последний раз был установлен. + С этой настройкой может быть удобнее, при частом переключении между + различными типами зданий. keybindings: title: Настройки управления diff --git a/translations/base-sv.yaml b/translations/base-sv.yaml index 2aebe2ff..9bf3d4e3 100644 --- a/translations/base-sv.yaml +++ b/translations/base-sv.yaml @@ -203,23 +203,23 @@ dialogs: Du behöver starta om spelet för att applicera inställningar. editKeybinding: - title: Ändra tangentbindningar + title: Ändra snabbtangenter desc: Tryck ned tangenten eller musknappen du vill tillsätta, eller escape för att avbryta. resetKeybindingsConfirmation: - title: Återställ tangentbindningar + title: Återställ snabbtangenter desc: Detta kommer att återställa alla tangentbindningar till deras standardtangenter. Var snäll och bekräfta. keybindingsResetOk: - title: Återställning av tangentbindningar - desc: Tangentbindningar har återställts! + title: Återställning av snabbtangenter + desc: Snabbtangenterna har återställts! featureRestriction: title: Demoversion - desc: Du försökte nå en funktion () som inte är tillgänglig i demversionen. Överväg att skaffa den fristående versionen för den fulla upplevelsen! + desc: Du försökte nå en funktion () som inte är tillgänglig i demoversionen. Överväg att skaffa den fristående versionen för den fulla upplevelsen! oneSavegameLimit: - title: Limiterad mängd sparfiler + title: Begränsad mängd sparfiler desc: Du kan bara ha en sparfil åt gången i demoversionen. Var snäll och ta bort det existerande eller skaffa den fristående versionen! updateSummary: @@ -231,7 +231,7 @@ dialogs: title: Lås upp Uppgraderingar desc: >- Alla former du producerar kan användas för att låsa upp uppgraderingar - Förstör inte dina gamla fabriker! - Uppgraderingsmenyn kan finnas i det övre högra hörnet på skärmen. + Uppgraderingsmenyn finns i det övre högra hörnet på skärmen. massDeleteConfirm: title: Bekräfta borttagning @@ -239,7 +239,7 @@ dialogs: Du tar nu bort ganska många byggnader ( för att vara exakt)! Är du säker på att du vill göra detta? blueprintsNotUnlocked: - title: Inte än upplåst + title: Inte upplåst än desc: >- Ritningar är inte än upplåsta! Klara fler nivåer för att låsa upp dem. @@ -268,7 +268,7 @@ dialogs: title: Exportera skärmdump desc: >- Du efterfrågade att exportera din fabrik som en skärmdump. - Var snäll och notera att detta kan ta ett tag för en stor bas och i vissa fall till och med krascha ditt spel + Vänligen notera att detta kan ta ett tag för en stor bas och i vissa fall till och med krascha ditt spel ingame: # This is shown in the top left corner and displays useful keybindings in @@ -286,7 +286,7 @@ ingame: createMarker: Skapa Markör delete: Förstör pasteLastBlueprint: Klistra in ritning - lockBeltDirection: Sätt på rullbandsplannerare + lockBeltDirection: Aktivera rullbandsplanerare plannerSwitchSide: Vänd planerarsidan cutSelection: Klipp copySelection: Kopiera @@ -298,7 +298,7 @@ ingame: buildingPlacement: # Buildings can have different variants which are unlocked at later levels, # and this is the hint shown when there are multiple variants available. - cycleBuildingVariants: Tryck ned För att cykla igenom varianter. + cycleBuildingVariants: Tryck ned För att bläddra igenom varianter. # Shows the hotkey in the ui, e.g. "Hotkey: Q" hotkeyLabel: >- @@ -393,10 +393,10 @@ ingame: hints: 1_1_extractor: Placera en extraktor över en cirkel för att extrahera den! 1_2_conveyor: >- - Koppla extraktorn med ettrullband till din hub!

Tips: Clicka och dra rullbandet med musen! + Koppla extraktorn med ettrullband till din hub!

Tips: Klicka och dra rullbandet med musen! 1_3_expand: >- - Detta är INTE ett idle-spel! Bygg fler extraktörer för att klara målet snabbare.

Tips: Håll SHIFT för att placera flera extraktörer, och använd R för att rotera dem. + Detta är INTE ett idle-spel! Bygg fler extraktörer för att klara målet snabbare.

Tips: Håll SKIFT för att placera flera extraktörer, och använd R för att rotera dem. colors: red: Röd @@ -420,7 +420,7 @@ shopUpgrades: name: Extraktion description: Hastighet x → x processors: - name: Klippning, Rotationg & Stapling + name: Klippning, Rotering & Stapling description: Hastighet x → x painting: name: Blandning & Färgning @@ -431,7 +431,7 @@ buildings: belt: default: name: &belt Rullband - description: Transporterar obejekt, håll in och dra för att placera flera. + description: Transporterar objekt, håll in och dra för att placera flera. miner: # Internal name for the Extractor default: @@ -467,10 +467,10 @@ buildings: cutter: default: name: &cutter Klippare - description: Klipper former från topp till botten och outputtar båda halver.Om du bara använder en halva, se till att förstöra den andra, annars kommer den uppehålla fabrkien! + description: Klipper former från topp till botten och outputtar båda halvor.Om du endast använder en halva, se till att förstöra den andra, annars kommer den blockera maskinen! quad: name: Klippare (Quad) - description: Klipper former i fyra delar. Om du bara använder en del, se till att förstöra de andra, annars kommer de uppehålla fabrkien! + description: Klipper former i fyra delar. Om du endast använder en del, se till att förstöra de andra, annars kommer de blockera maskinen! rotater: default: @@ -483,7 +483,7 @@ buildings: stacker: default: name: &stacker Staplare - description: Staplar båda obejekt. Om de inte kan slås ihop, placeras det högra obejektet över det vänstra. + description: Staplar båda objekt. Om de inte kan slås ihop, placeras det högra objektet över det vänstra. mixer: default: @@ -530,7 +530,7 @@ storyRewards: reward_painter: title: Måleri desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a color blind mode in the settings! + Målaren har låsts upp - Extrahera färger (precis som du gör med formerna) och kombinera med en form i målaren för att måla formen!

PS: Om du är färgblind finns det ett färblint läge bland inställningarna! reward_mixer: title: Färgblandning @@ -542,7 +542,7 @@ storyRewards: reward_splitter: title: Delning/Sammanslagning - desc: Den multifunktiionella balanseraren har blivit upplåst - Den kan användas för att bygga större fabriker genom att dela eller slå ihop objekt till flera rullband!

+ desc: Den multifunktionella balanseraren har blivit upplåst - Den kan användas för att bygga större fabriker genom att dela eller slå ihop objekt till flera rullband!

reward_tunnel: title: Tunnel @@ -550,7 +550,7 @@ storyRewards: reward_rotater_ccw: title: Motsols rotation - desc: Du har låst upp en variant av roteraren - Den låter dig rotera saker motsols! För att bygga den, välj roteraren och tryck ned 'T' för att cykla genom dess varianter! + desc: Du har låst upp en variant av roteraren - Den låter dig rotera saker motsols! För att bygga den, välj roteraren och tryck ned 'T' för att bläddra genom dess varianter! reward_miner_chainable: title: Kedjeextraktor @@ -575,11 +575,11 @@ storyRewards: reward_painter_quad: title: Quad Färgläggning - desc: Du har låst upp en ny variant av Färgläggaren - Den tillåter dig att färglägga varje del av en form individuellt! + desc: Du har låst upp en ny variant av Färgläggaren - Den låter dig färglägga varje del av en form individuellt! reward_storage: title: Förvaringsbuffert - desc: Du har låst upp en ny variant av skräphantering - Den tillåter dig att förvara objekt upp till en viss kapacitet! + desc: Du har låst upp en ny variant av skräphantering - Den låter dig förvara objekt upp till en viss kapacitet! reward_freeplay: title: Friläge @@ -593,12 +593,12 @@ storyRewards: no_reward: title: Nästa nivå desc: >- - Denna nivå har ingen belöning, men nästa kommer!

PS: Se till att inte förstöra din redan existerande fabrik - Du behöver alla de där formerna igen för att låsa upp uppgraderingar! + Denna nivå har ingen belöning, men nästa har!

PS: Se till att inte förstöra din redan existerande fabrik - Du behöver alla de där formerna igen för att låsa upp uppgraderingar! no_reward_freeplay: title: Nästa nivå desc: >- - Grattis! Föresten, mer spelinnehåll är planerat för den fristående versionen! + Grattis! Förresten, mer spelinnehåll är planerat för den fristående versionen! settings: title: Inställningar @@ -643,7 +643,7 @@ settings: fullscreen: title: Fullskärm description: >- - Det är rekommenderat att spela i fullskärm för bästa upplevelse. Endast tillgänglig i den fristående versionen. + Det är rekommenderat att spela i fullskärm för bästa upplevelse. Endast tillgängligt i den fristående versionen. soundsMuted: title: Dämpa Ljud @@ -727,18 +727,17 @@ settings: title: Färgblint läge description: Aktiverar olika verktyg som låter dig spela spelet om du är färbling. rotationByBuilding: - title: Rotation by building type + title: Rotering per byggnadstyp description: >- - Each building type remembers the rotation you last set it to individually. - This may be more comfortable if you frequently switch between placing - different building types. + Varje byggnadstyp kommer ihåg rotationen du senast var på individuellt. + Detta kan vara mer bekvämt om du ofta bytar byggnader som du placerar. keybindings: - title: Tangentbindningar + title: Snabbtangenter hint: >- Tips: Se till att använda CTRL, SKIFT, och ALT! De låter dig använda olika placeringslägen. - resetKeybindings: Återställ Tangentbindningar + resetKeybindings: Återställ Snabbtangenter categoryLabels: general: Applikation @@ -828,7 +827,7 @@ demo: restoringGames: Återställer sparfiler importingGames: Importerar sparfiler oneGameLimit: Limiterad till endast en sparfil - customizeKeybindings: Finjustera tangentbindningar + customizeKeybindings: Finjustera snabbtangenter exportingBase: Exportera hela fabriken som en bild settingNotAvailable: Inte tillgänglig i demoversionen. diff --git a/translations/base-tr.yaml b/translations/base-tr.yaml index 8084ee4a..0046b8b5 100644 --- a/translations/base-tr.yaml +++ b/translations/base-tr.yaml @@ -68,39 +68,39 @@ steamPage: Be sure to check out my trello board for the full roadmap! https://trello.com/b/ISQncpJP/shapezio global: - loading: Loading - error: Error + loading: yükleniyor + error: Hata # How big numbers are rendered, e.g. "10,000" thousandsDivider: "," # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. suffix: - thousands: k + thousands: b millions: M billions: B trillions: T # Shown for infinitely big numbers - infinite: inf + infinite: sonsuz time: # Used for formatting past time dates - oneSecondAgo: one second ago - xSecondsAgo: seconds ago - oneMinuteAgo: one minute ago - xMinutesAgo: minutes ago - oneHourAgo: one hour ago - xHoursAgo: hours ago - oneDayAgo: one day ago - xDaysAgo: days ago + oneSecondAgo: bir saniye önce + xSecondsAgo: saniye önce + oneMinuteAgo: bir dakika önce + xMinutesAgo: dakika önce + oneHourAgo: bir saat önce + xHoursAgo: saat önce + oneDayAgo: bir gün önce + xDaysAgo: gün önce # Short formats for times, e.g. '5h 23m' secondsShort: s - minutesAndSecondsShort: m s - hoursAndMinutesShort: h m + minutesAndSecondsShort: d s + hoursAndMinutesShort: S m - xMinutes: minutes + xMinutes: dakika keys: tab: TAB @@ -117,19 +117,19 @@ demoBanners: Get the standalone to unlock all features! mainMenu: - play: Play + play: Oyna changelog: Changelog - importSavegame: Import - openSourceHint: This game is open source! - discordLink: Official Discord Server - helpTranslate: Help translate! + importSavegame: Yükle + openSourceHint: Bu oyun açık kaynak kodlu! + discordLink: Resmi Discord Sunucusu + helpTranslate: Çeviriye yardım et! # This is shown when using firefox and other browsers which are not supported. browserWarning: >- Sorry, but the game is known to run slow on your browser! Get the standalone version or download chrome for the full experience. - savegameLevel: Level - savegameLevelUnknown: Unknown Level + savegameLevel: Seviye + savegameLevelUnknown: Bilinmeyen seviye contests: contest_01_03062020: @@ -153,21 +153,21 @@ mainMenu: showInfo: View contestOver: This contest has ended - Join the discord to get noticed about new contests! - continue: Continue - newGame: New Game + continue: Devam et + newGame: Yeni Oyun madeBy: Made by subreddit: Reddit dialogs: buttons: ok: OK - delete: Delete - cancel: Cancel - later: Later - restart: Restart - reset: Reset - getStandalone: Get Standalone - deleteGame: I know what I do + delete: Sil + cancel: İptal + later: Sonra + restart: Yeniden başla + reset: Sıfırla + getStandalone: Tam versiyona eriş + deleteGame: Ne yaptığımi biliyorum viewUpdate: View Update showUpgrades: Show Upgrades showKeybindings: Show Keybindings @@ -180,7 +180,7 @@ dialogs: importSavegameSuccess: title: Savegame Imported text: >- - Your savegame has been successfully imported. + Kayıtlı oyun başarıyla yüklendi. gameLoadFailure: title: Game is broken @@ -190,7 +190,7 @@ dialogs: confirmSavegameDelete: title: Confirm deletion text: >- - Are you sure you want to delete the game? + Oyunu silmek istediğinizden emin misiniz? savegameDeletionError: title: Failed to delete @@ -360,9 +360,9 @@ ingame: # Settings menu, when you press "ESC" settingsMenu: - playtime: Playtime + playtime: Oynama zamani - buildingsPlaced: Buildings + buildingsPlaced: Yapılar beltsPlaced: Belts buttons: @@ -399,17 +399,17 @@ ingame: This is NOT an idle game! Build more extractors and belts to finish the goal quicker.

Tip: Hold SHIFT to place multiple extractors, and use R to rotate them. colors: - red: Red - green: Green - blue: Blue - yellow: Yellow - purple: Purple + red: Kırmızı + green: Yesil + blue: Mavi + yellow: Sarı + purple: Mor cyan: Cyan - white: White - uncolored: No color + white: Beyaz + uncolored: Renksiz shapeViewer: title: Layers - empty: Empty + empty: Bos # All shop upgrades shopUpgrades: diff --git a/translations/base-zh-TW.yaml b/translations/base-zh-TW.yaml index 973312d0..b0b0474f 100644 --- a/translations/base-zh-TW.yaml +++ b/translations/base-zh-TW.yaml @@ -26,7 +26,7 @@ # Shape:圖形 # tile:格子 # Keybind:按鍵設置 -# Menu:主界面 +# Menu:主選單 # Center/Hub:基地 # Upgrade:升級 # Efficiency:效率 @@ -59,9 +59,9 @@ steamPage: shapez.io 是一款在無邊際的地圖上建造工廠、自動化生產與組合愈加複雜的圖形的遊戲。提交任務,製造更複雜的流水線,解鎖升級來提升您工廠的運作速度。 - 你將會需要隨著不斷上升得需求擴大你的工廠。當然,不要忘記你可以在[b]無盡[/b]的地圖上開採資源! + 你將會需要隨著不斷上升的需求擴大你的工廠。當然,不要忘記你可以在[b]無盡[/b]的地圖上開採資源! - 只對圖形進行加工可能會使你感到無聊。我們為你準備了顏色資源——將紅、綠、藍三種顏色混合,生產更多不同的顏色並粉刷在圖形上以滿足需求。 + 只對圖形進行加工可能會使你感到無聊。我們為你準備了顏色資源——將紅、綠、藍三種顏色調色,產生更多不同的顏色並著色在圖形上以滿足任務需求。 這個遊戲目前有18個關卡(這應該已經能讓你忙碌幾個小時了!),並且遊戲正在不斷地更新中。很多新關卡已經在開發計劃當中! @@ -84,7 +84,7 @@ steamPage: [list] [*] 要消耗圖形來造建築的的故事模式 - [*] 更多關卡&建築(單機版獨有) + [*] 更多關卡&建築(獨立版獨有) [*] 更多地圖,也許會有障礙物 [*] 可配置的地圖生成(礦脈密度與大小、 隨機種子以及其他地圖設置) [*] 更多圖形 @@ -148,8 +148,8 @@ mainMenu: play: 開始遊戲 changelog: 更新日誌 importSavegame: 導入 - openSourceHint: 本游戏已开源頭! - discordLink: 官方Discord服務器 + openSourceHint: 本遊戲已開源! + discordLink: 官方Discord伺服器 helpTranslate: 幫助我們翻譯! # This is shown when using firefox and other browsers which are not supported. @@ -168,12 +168,12 @@ mainMenu:

本週主題:建立最酷的基地!

- 這是交易:
+ 是這樣的:
    -
  • 將您的基地的屏幕截圖提交到 contest@shapez.io +
  • 將您的基地的螢幕截圖提交到 contest@shapez.io
  • 如果您在社交媒體上分享會有額外獎勵積分! -
  • 我將選擇5張屏幕截圖,並將其提交給 discord 社區進行投票。 -
  • 獲勝者將獲得 $ 25 (貝寶paypal,亞馬遜禮品卡,隨您喜歡) +
  • 我將選擇5張螢幕截圖,並將其提交給 discord 社區進行投票。 +
  • 獲勝者將獲得 $ 25 (PayPal,亞馬遜禮品卡,隨您喜歡)
  • 截止日期:CEST 07.06.2020 12:00 AM
    @@ -189,7 +189,7 @@ mainMenu: dialogs: buttons: ok: 確認 - delete: 删除 + delete: 刪除 cancel: 取消 later: 之後 restart: 重啟 @@ -201,19 +201,19 @@ dialogs: showKeybindings: 顯示按鍵設置 importSavegameError: - title: 導入錯誤 + title: 匯入錯誤 text: >- - 未能導入你這存檔: + 存檔匯入失敗: importSavegameSuccess: - title: 導入成功 + title: 匯入成功 text: >- - 存檔被成功導入 + 存檔匯入成功 gameLoadFailure: - title: 存檔被損壞 + title: 存檔損毀 text: >- - 未能導入你這存檔: + 存檔載入失敗: confirmSavegameDelete: title: 確認刪除 @@ -223,12 +223,12 @@ dialogs: savegameDeletionError: title: 刪除錯誤 text: >- - 未能删除你這存档 + 存檔刪除失敗 restartRequired: title: 需要重啟 text: >- - 你需要重啟遊戲以應用變更的設置。 + 你需要重啟遊戲以套用變更的設置。 editKeybinding: title: 更改按鍵設置 @@ -258,8 +258,8 @@ dialogs: upgradesIntroduction: title: 解鎖建築升級 desc: >- -
    你生產過的所有圖形都會被用來升級建築。升級菜單在屏幕右上角。 - 不要銷毀你之前建造的工廠! + 你生產過的所有圖形可以被用來升級建築。不要銷毀你之前建造的工廠! + 升級選單在屏幕右上角。 massDeleteConfirm: title: 確認刪除 @@ -287,14 +287,14 @@ dialogs: markerDemoLimit: desc: 在演示版中你只能創建兩個地圖標記。請獲取獨立版以創建更多標記。 massCutConfirm: - title: 確認剪割 + title: 確認剪下 desc: >- - 你將要剪割很多建築,準確來說有幢!你確定要這麼做嗎? + 你將要剪下很多建築,準確來說有幢!你確定要這麼做嗎? exportScreenshotWarning: title: 工廠截圖 desc: >- - 你將要導出你的工廠的截圖。如果你的基地很大,截圖過程將會很慢,甚至有可能導致遊戲崩潰! + 你將要匯出你的工廠的截圖。如果你的基地很大,截圖過程將會很慢,甚至有可能導致遊戲當掉! ingame: # This is shown in the top left corner and displays useful keybindings in @@ -314,9 +314,9 @@ ingame: pasteLastBlueprint: 貼上一個藍圖 lockBeltDirection: 啟用傳送帶規劃 plannerSwitchSide: 規劃器換邊 - cutSelection: 剪下選項 - copySelection: 複製選項 - clearSelection: 清空選項 + cutSelection: 剪下選取 + copySelection: 複製選取 + clearSelection: 清空選取 pipette: 吸附 # Everything related to placing buildings (I.e. as soon as you selected a building @@ -355,8 +355,8 @@ ingame: # The "Upgrades" window shop: - title: Upgrades - buttonUnlock: Upgrade + title: 建築升級 + buttonUnlock: 升級 # Gets replaced to e.g. "Tier IX" tier: 級 @@ -365,11 +365,11 @@ ingame: # Chinese translation: Chinese characters for each tier tierLabels: [壹, 貳, 叁, 肆, 伍, 陸, 柒, 捌, 玖, 拾] - maximumLevel: 最高级(倍效率) + maximumLevel: 最高級(倍效率) # The "Statistics" window statistics: - title: 統計信息 + title: 統計資訊 dataSources: stored: title: 儲存 @@ -395,7 +395,7 @@ ingame: buttons: continue: 繼續 settings: 設置 - menu: 回到主界面 + menu: 回到主選單 # Bottom left tutorial hints tutorialHints: @@ -420,10 +420,10 @@ ingame: hints: 1_1_extractor: 在圓形礦脈上放一個開採機來獲取圓形! 1_2_conveyor: >- - 用傳送帶將你的開採機連接到基地上!

    提示:用你的鼠標按下並拖動傳送帶! + 用傳送帶將你的開採機連接到基地上!

    提示:用你的游標按下並拖動傳送帶! 1_3_expand: >- - 這不是一個挂機遊戲!建造更多的開採機和傳送帶來更快地完成目標。

    + 這不是一個放置型遊戲!建造更多的開採機和傳送帶來更快地完成目標。

    提示:按住SHIFT鍵來放置多個開採機,用R鍵旋轉它們。 colors: @@ -432,7 +432,7 @@ ingame: blue: 藍 yellow: 黃 purple: 紫 - cyan: 淺藍 + cyan: 青 white: 白 uncolored: 無顏色 shapeViewer: @@ -495,10 +495,10 @@ buildings: cutter: default: name: &cutter 切割機 - description: 將圖形從上到下切開並輸出。 如果你只需要其中一半,記得把另一半銷毀掉,否則切割機會停止工作! + description: 將圖形從上到下切開並輸出。 如果你只需要其中一半,記得把另一半銷毀掉,否則切割機會停止運作! quad: name: 切割機(四分) - description: 將輸入的圖形切成四塊。 如果你只需要其中一塊,記得把其他的銷毀掉,否則切割機會停止工作! + description: 將輸入的圖形切成四塊。 如果你只需要其中一塊,記得把其他的銷毀掉,否則切割機會停止運作! rotater: default: @@ -511,12 +511,12 @@ buildings: stacker: default: name: &stacker 混合機 - description: 將輸入的圖形拼貼在一起。如果不能被直接拼貼,右邊的圖形會被混合在左邊的圖形上面. + description: 將輸入的圖形拼貼在一起。如果不能被直接拼貼,右邊的圖形會被疊在左邊的圖形上面. mixer: default: name: &mixer 混色機 - description: 將兩個顏色混合在一起。 (加法混合) + description: 將兩個顏色加在一起。 painter: default: @@ -524,7 +524,7 @@ buildings: description: &painter_desc 將整個圖形塗上輸入的顏色。 double: name: 上色機(雙倍) - description: 同時為兩個輸入的圖形上色,每次上色只消耗一份顏色。 + description: 同時為兩個輸入的圖形上色,每次上色只消耗一份顏色塗料。 quad: name: 上色機(四向) description: 為圖形的四個角塗上不同的顏色。 @@ -549,7 +549,7 @@ storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: title: 切割圖形 - desc: 切割機已解鎖。不論切割機的方向,它都會把圖形從上到下切成兩半。

    記得把不需要的部分處理掉,否則這個這個建築會停止工作。為此我給你準備了垃圾桶,它會把所有放進去的物品銷毀掉。 + desc: 切割機已解鎖。不論切割機的方向,它都會把圖形垂直地切成兩半。

    記得把不需要的部分處理掉,否則這個這個建築會停止運作。為此我給你準備了垃圾桶,它會把所有放進去的物品銷毀掉。 reward_rotater: title: 順時針旋轉 @@ -558,15 +558,15 @@ storyRewards: reward_painter: title: 上色 desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

    PS: If you are colorblind, there is a color blind mode in the settings! + 上色機已解鎖。開採一些顏色,用上色機把顏色和圖形混合,就可以為圖形著色。

    備註:如果你是色盲,設置中有色盲模式可以選。 reward_mixer: title: 混色 - desc: 混色器已解鎖-在此建築物中使用附加混合結合兩種顏色! + desc: 混色器已解鎖-在此建築物中使用附加混合結合兩種顏色! reward_stacker: title: 混合 - desc: 混合機已解鎖。混合機會嘗試把兩個輸入的圖形拼貼在一起。如果沒有重疊的部分,右邊的輸入會被混合到左邊的輸入上方! + desc: 混合機已解鎖。如果沒有重疊的部分,混合機會嘗試把兩個輸入的圖形拼貼在一起。如果有重疊的部分,右邊的輸入會被到左邊的輸入上方! reward_splitter: title: 分離/合併 @@ -574,19 +574,19 @@ storyRewards: reward_tunnel: title: 隧道 - desc: 隧道已解鎖。你現在可以從其他傳送帶或建築底下運送物品了(最長距離為四)! + desc: 隧道已解鎖。你現在可以從其他傳送帶或建築底下運送物品了! reward_rotater_ccw: title: 逆時針旋轉 - desc: 您已解鎖了旋轉器的變體-它可以逆時針旋轉! 要構建它,請選擇旋轉器,然後按“ T”鍵循環其變種! + desc: 您已解鎖了旋轉器的變體-它可以逆時針旋轉! 要構建它,請選擇旋轉器,然後按“T”鍵切換其變種! reward_miner_chainable: title: 鏈式開採機 - desc: 鏈式開採機變體已解鎖。它是開採機的一個變體。它可以將開采出來的資源傳遞給其他的開採機,使得資源提取更加高效! + desc: 鏈式開採機變體已解鎖。它是開採機的一個變體。它可以將開採出來的資源傳遞給其他的開採機,使得資源提取更加高效! reward_underground_belt_tier_2: title: 貳級隧道 - desc: 貳級隧道變體已解鎖。這個隧道有更長的傳輸距離。你還可以混用不同的隧道變體(點與點最長距離為7)! + desc: 貳級隧道變體已解鎖。這個隧道有更長的傳輸距離。你還可以混用不同的隧道變體! reward_splitter_compact: title: 小型合流機 @@ -615,7 +615,7 @@ storyRewards: reward_blueprints: title: 藍圖 - desc: 現在,您可以複製並貼上工廠的各個部分! 選擇一個區域(按住CTRL,然後用鼠標拖動),然後按'C'將其複制。

    貼上為非空閒,您需要生成藍圖 形狀來負擔得起! (您剛交付的那些)。 + desc: 現在,您可以複製並貼上工廠的各個部分! 選擇一個區域(按住CTRL,然後用游標拖動),然後按'C'將其複制。

    複製不是免費的,您需要用藍圖 形狀來支付! (您剛交付的那些)。 # Special reward, which is shown when there is no reward actually no_reward: @@ -669,9 +669,9 @@ settings: 改變語言。所有的翻譯皆由玩家提供,且有可能正在施工中! fullscreen: - title: 全屏 + title: 全螢幕 description: >- - 全屏以獲得更好的遊戲體驗。僅在獨立版中可用。 + 全螢幕以獲得更好的遊戲體驗。僅在獨立版中可用。 soundsMuted: title: 關閉音效 @@ -695,7 +695,7 @@ settings: refreshRate: title: 模擬頻率、刷新頻率 description: >- - 如果你的顯示器是144hz的,請在這裡更改刷新頻率,這樣遊戲可以正確地根據你的屏幕進行模擬。但是如果你的電腦性能不佳,提高刷新頻率可能降低幀數。 + 如果你的顯示器是144hz的,請在這裡更改刷新頻率,這樣遊戲可以正確地根據你的螢幕進行模擬。但是如果你的電腦性能不佳,提高刷新頻率可能降低幀數。 # description: >- # If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow. @@ -732,7 +732,7 @@ settings: vignette: title: 暈映 description: >- - 啟用暈映,將屏幕角落裡的顏色變深,更容易閱讀文本。 + 啟用暈映,將屏幕角落裡的顏色變深,更容易閱讀文字。 autosaveInterval: title: 自動刷新時間 @@ -744,27 +744,27 @@ settings: five_minutes: 5分鐘 ten_minutes: 10分鐘 twenty_minutes: 20分鐘 - disabled: 禁用 + disabled: 停用 compactBuildingInfo: title: 省略建築信息 description: >- - 通過顯示建築物的比率來縮短建築物的信息框。 否則 + 通過顯示建築物的比率來縮短建築物的資訊框。 否則 顯示所有說明+圖像。 disableCutDeleteWarnings: - title: 禁用剪下/刪除的警告 + title: 停用剪下/刪除的警告 description: >- - 剪下/刪除超過100幢建築物時禁用警告。 + 剪下/刪除超過100幢建築物時不顯示警告。 enableColorBlindHelper: title: 色盲模式 description: 如果您是色盲者,啟用了這設定,就可以玩遊戲了。 rotationByBuilding: - title: Rotation by building type + title: 依建築類型旋轉 description: >- - Each building type remembers the rotation you last set it to individually. - This may be more comfortable if you frequently switch between placing - different building types. + 每個建築類型,將會分別記住您最後一次使用的旋轉方向。 + 如果您常常切換不同類型的建築,這樣可能會更方便。 + keybindings: title: 按鍵設置 hint: >- @@ -794,8 +794,8 @@ keybindings: mapZoomOut: 縮小 createMarker: 創建地圖標記 - menuOpenShop: 升級菜單 - menuOpenStats: 統計菜單 + menuOpenShop: 升級選單 + menuOpenStats: 統計選單 toggleHud: 開關HUD toggleFPSInfo: 開關幀數與調試信息 @@ -823,9 +823,9 @@ keybindings: placementDisableAutoOrientation: 取消自動定向 placeMultiple: 繼續放置 placeInverse: 反向放置傳送帶 - pasteLastBlueprint: 粘貼上一張藍圖 + pasteLastBlueprint: 貼上前一張藍圖 massSelectCut: 剪切 - exportScreenshot: 導出截圖 + exportScreenshot: 匯出截圖 mapMoveFaster: 快速移動 lockBeltDirection: 啟用傳送帶規劃 @@ -857,9 +857,9 @@ changelog: demo: features: restoringGames: 恢復存檔 #中? - importingGames: 倒入存檔 #中? + importingGames: 匯入存檔 #中? oneGameLimit: 最多一個存檔 customizeKeybindings: 按鍵設置 # customizeKeybindings: Customizing Keybindings - exportingBase: 導出工廠截圖 + exportingBase: 匯出工廠截圖 settingNotAvailable: 在演示版中不可用。