diff --git a/artwork/itch.io/background.jpg b/artwork/itch.io/background.jpg index 619876a2..524b6358 100644 Binary files a/artwork/itch.io/background.jpg and b/artwork/itch.io/background.jpg differ diff --git a/artwork/itch.io/bg.png b/artwork/itch.io/bg.png new file mode 100644 index 00000000..425e94d8 Binary files /dev/null and b/artwork/itch.io/bg.png differ diff --git a/artwork/steam/announcement.png b/artwork/steam/announcement.png new file mode 100644 index 00000000..c531e3ab Binary files /dev/null and b/artwork/steam/announcement.png differ diff --git a/artwork/steam/announcement.psd b/artwork/steam/announcement.psd new file mode 100644 index 00000000..4282d647 --- /dev/null +++ b/artwork/steam/announcement.psd @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de805473208e0901f1185485164a35fde59e4baf3000f30102d0f332842410a5 +size 184895 diff --git a/artwork/steam/store_page_gif.gif b/artwork/steam/store_page_gif.gif new file mode 100644 index 00000000..1491fb0a Binary files /dev/null and b/artwork/steam/store_page_gif.gif differ diff --git a/electron/favicon.icns b/electron/favicon.icns deleted file mode 100644 index d40d7f21..00000000 Binary files a/electron/favicon.icns and /dev/null differ diff --git a/gulp/gulpfile.js b/gulp/gulpfile.js index d87ba792..520a9286 100644 --- a/gulp/gulpfile.js +++ b/gulp/gulpfile.js @@ -189,7 +189,7 @@ function serve({ standalone }) { gulp.watch("../res_built/atlas/*.json", ["imgres.atlas"]); // Watch the build folder and reload when anything changed - const extensions = ["html", "js", "png", "jpg", "svg", "mp3", "ico", "woff2", "json"]; + const extensions = ["html", "js", "png", "gif", "jpg", "svg", "mp3", "ico", "woff2", "json"]; gulp.watch(extensions.map(ext => path.join(buildFolder, "**", "*." + ext))).on("change", function (e) { return gulp.src(e.path).pipe(browserSync.reload({ stream: true })); }); @@ -275,11 +275,7 @@ gulp.task( $.sequence("sounds.fullbuild", "translations.fullBuild", "js.standalone-beta") ); gulp.task("step.standalone-beta.mainbuild", cb => - $.multiProcess( - ["utils.copyAdditionalBuildFiles", "step.baseResources", "step.standalone-beta.code"], - cb, - false - ) + $.multiProcess(["step.baseResources", "step.standalone-beta.code"], cb, false) ); gulp.task( "step.standalone-beta.all", @@ -293,11 +289,7 @@ gulp.task( $.sequence("sounds.fullbuild", "translations.fullBuild", "js.standalone-prod") ); gulp.task("step.standalone-prod.mainbuild", cb => - $.multiProcess( - ["utils.copyAdditionalBuildFiles", "step.baseResources", "step.standalone-prod.code"], - cb, - false - ) + $.multiProcess(["step.baseResources", "step.standalone-prod.code"], cb, false) ); gulp.task( "step.standalone-prod.all", diff --git a/gulp/html.js b/gulp/html.js index 94c25010..a21d66cb 100644 --- a/gulp/html.js +++ b/gulp/html.js @@ -96,12 +96,20 @@ function gulptasksHTML($, gulp, buildFolder, browserSync) { const initScript = document.createElement("script"); initScript.textContent = ` - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', 'UA-165342524-1', { anonymize_ip: true }); + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', 'UA-165342524-1', { anonymize_ip: true }); `; document.head.appendChild(initScript); + + const abTestingScript = document.createElement("script"); + abTestingScript.setAttribute( + "src", + "https://www.googleoptimize.com/optimize.js?id=OPT-M5NHCV7" + ); + abTestingScript.setAttribute("async", ""); + document.head.appendChild(abTestingScript); } // Do not need to preload in app or standalone diff --git a/gulp/image-resources.js b/gulp/image-resources.js index b01ca400..90268517 100644 --- a/gulp/image-resources.js +++ b/gulp/image-resources.js @@ -5,7 +5,7 @@ const path = require("path"); const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/*.webm"]; // Globs for ui resources -const imageResourcesGlobs = ["../res/**/*.png", "../res/**/*.svg", "../res/**/*.jpg"]; +const imageResourcesGlobs = ["../res/**/*.png", "../res/**/*.svg", "../res/**/*.jpg", "../res/**/*.gif"]; function gulptasksImageResources($, gulp, buildFolder) { // Lossless options @@ -17,6 +17,10 @@ function gulptasksImageResources($, gulp, buildFolder) { $.imagemin.optipng({ optimizationLevel: 3, }), + $.imageminGifsicle({ + optimizationLevel: 3, + colors: 128, + }), ]; // Lossy options @@ -36,6 +40,10 @@ function gulptasksImageResources($, gulp, buildFolder) { $.imagemin.optipng({ optimizationLevel: 3, }), + $.imageminGifsicle({ + optimizationLevel: 3, + colors: 128, + }), ]; // Where the resources folder are @@ -124,6 +132,7 @@ function gulptasksImageResources($, gulp, buildFolder) { path.join(buildFolder, "res", "ui", "**", "*.png"), path.join(buildFolder, "res", "ui", "**", "*.jpg"), path.join(buildFolder, "res", "ui", "**", "*.svg"), + path.join(buildFolder, "res", "ui", "**", "*.gif"), ], { read: false } ) diff --git a/gulp/package.json b/gulp/package.json index c4ab10c4..f2d542a8 100644 --- a/gulp/package.json +++ b/gulp/package.json @@ -70,6 +70,7 @@ "css-mqpacker": "^7.0.0", "cssnano": "^4.1.10", "electron-packager": "^14.0.6", + "imagemin-gifsicle": "^7.0.0", "faster.js": "^1.1.0", "glob": "^7.1.3", "gulp": "^3.9.1", diff --git a/gulp/webpack.production.config.js b/gulp/webpack.production.config.js index 14c6598a..312923f0 100644 --- a/gulp/webpack.production.config.js +++ b/gulp/webpack.production.config.js @@ -237,7 +237,7 @@ module.exports = ({ pattern: /globalConfig\.beltSpeedItemsPerSecond/g, replacement: () => "2.0", }, - { pattern: /globalConfig\.itemSpacingOnBelts/g, replacement: () => "0.8" }, + { pattern: /globalConfig\.itemSpacingOnBelts/g, replacement: () => "0.63" }, { pattern: /globalConfig\.debug/g, replacement: () => "''" }, ], }), diff --git a/gulp/yarn.lock b/gulp/yarn.lock index 7389e868..8e92a91f 100644 --- a/gulp/yarn.lock +++ b/gulp/yarn.lock @@ -3412,6 +3412,15 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cross-zip@^2.1.5: version "2.1.6" resolved "https://registry.yarnpkg.com/cross-zip/-/cross-zip-2.1.6.tgz#344d3ba9488609942987d815bb84860cff3d9491" @@ -4875,6 +4884,21 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.2.tgz#ad87fb7b2d9d564f70d2b62d511bee41d5cbb240" + integrity sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + executable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/executable/-/executable-1.1.0.tgz#877980e9112f3391066da37265de7ad8434ab4d9" @@ -5738,7 +5762,7 @@ get-stream@^4.0.0, get-stream@^4.1.0: dependencies: pump "^3.0.0" -get-stream@^5.1.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== @@ -5767,6 +5791,16 @@ gifsicle@^4.0.0: execa "^1.0.0" logalot "^2.0.0" +gifsicle@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/gifsicle/-/gifsicle-5.1.0.tgz#08f878e9048c70adf046185115a6350516a1fdc0" + integrity sha512-hQsOH7yjC7fMokntysN6f2QuxrnX+zmKKKVy0sC3Vhtnk8WrOxLdfH/Z2PNn7lVVx+1+drzIeAe8ufcmdSC/8g== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + execa "^4.0.0" + logalot "^2.0.0" + glob-all@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-all/-/glob-all-3.1.0.tgz#8913ddfb5ee1ac7812656241b03d5217c64b02ab" @@ -6859,6 +6893,11 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -6912,6 +6951,15 @@ imagemin-gifsicle@^6.0.1: gifsicle "^4.0.0" is-gif "^3.0.0" +imagemin-gifsicle@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/imagemin-gifsicle/-/imagemin-gifsicle-7.0.0.tgz#1a7ab136a144c4678657ba3b6c412f80805d26b0" + integrity sha512-LaP38xhxAwS3W8PFh4y5iQ6feoTSF+dTAXFRUEYQWYst6Xd+9L/iPk34QGgK/VO/objmIlmq9TStGfVY2IcHIA== + dependencies: + execa "^1.0.0" + gifsicle "^5.0.0" + is-gif "^3.0.0" + imagemin-jpegtran@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/imagemin-jpegtran/-/imagemin-jpegtran-6.0.0.tgz#c8d3bcfb6ec9c561c20a987142854be70d90b04f" @@ -8700,6 +8748,11 @@ merge-stream@^1.0.0: dependencies: readable-stream "^2.0.1" +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + merge2@^1.2.3: version "1.3.0" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" @@ -8810,7 +8863,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0: +mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -9312,6 +9365,13 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -9533,6 +9593,13 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + open@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" @@ -9997,6 +10064,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -12038,11 +12110,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shelljs@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" @@ -12680,6 +12764,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" @@ -13973,6 +14062,13 @@ which@1, which@^1.1.1, which@^1.2.14, which@^1.2.9, which@^1.3.1: dependencies: isexe "^2.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" diff --git a/package.json b/package.json index 8774e693..cc5cffdf 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "lint": "npx eslint src/js", "prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.*", "publishOnItchWindows": "butler push tmp_standalone_files/shapez.io-standalone-win32-x64 tobspr/shapezio:windows --userversion-file version", - "publishOnItchLinux": "butler push tmp_standalone_files/shapez.io-standalone-linux-x64 tobspr/shapezio:linux-experimental --userversion-file version", + "publishOnItchLinux": "butler push tmp_standalone_files/shapez.io-standalone-linux-x64 tobspr/shapezio:linux --userversion-file version", + "publishOnItch": "yarn publishOnItchWindows && yarn publishOnItchLinux", "publishOnSteam": "cd gulp/steampipe && ./upload.bat", "publishStandalone": "yarn publishOnItch && yarn publishOnSteam", "publishWeb": "cd gulp && yarn main.deploy.prod", @@ -41,6 +42,7 @@ "howler": "^2.1.2", "html-loader": "^0.5.5", "ignore-loader": "^0.1.2", + "logrocket": "^1.0.7", "lz-string": "^1.4.4", "markdown-loader": "^4.0.0", "obfuscator-loader": "^1.1.2", diff --git a/res/ui/icons/notification_upgrade.png b/res/ui/icons/notification_upgrade.png index 9d98cd90..9d778459 100644 Binary files a/res/ui/icons/notification_upgrade.png and b/res/ui/icons/notification_upgrade.png differ diff --git a/res/ui/icons/shop.png b/res/ui/icons/shop.png index 8d065ae1..29519103 100644 Binary files a/res/ui/icons/shop.png and b/res/ui/icons/shop.png differ diff --git a/res/ui/interactive_tutorial.noinline/1_1_extractor.gif b/res/ui/interactive_tutorial.noinline/1_1_extractor.gif new file mode 100644 index 00000000..c7208ac2 Binary files /dev/null and b/res/ui/interactive_tutorial.noinline/1_1_extractor.gif differ diff --git a/res/ui/interactive_tutorial.noinline/1_2_conveyor.gif b/res/ui/interactive_tutorial.noinline/1_2_conveyor.gif new file mode 100644 index 00000000..db59bfd4 Binary files /dev/null and b/res/ui/interactive_tutorial.noinline/1_2_conveyor.gif differ diff --git a/res/ui/interactive_tutorial.noinline/1_3_expand.gif b/res/ui/interactive_tutorial.noinline/1_3_expand.gif new file mode 100644 index 00000000..9c655ab1 Binary files /dev/null and b/res/ui/interactive_tutorial.noinline/1_3_expand.gif differ diff --git a/src/css/ingame_hud/blueprint_placer.scss b/src/css/ingame_hud/blueprint_placer.scss index acf66087..e1cf06ef 100644 --- a/src/css/ingame_hud/blueprint_placer.scss +++ b/src/css/ingame_hud/blueprint_placer.scss @@ -5,7 +5,7 @@ transform: translateX(-50%); color: #333; z-index: 9999; - background: rgba(0, 10, 20, 0.5); + background: $ingameHudBg; @include S(padding, 5px); display: flex; flex-direction: column; diff --git a/src/css/ingame_hud/building_placer.scss b/src/css/ingame_hud/building_placer.scss index 0548390b..99c4d654 100644 --- a/src/css/ingame_hud/building_placer.scss +++ b/src/css/ingame_hud/building_placer.scss @@ -10,7 +10,7 @@ @include S(width, 240px); @include S(grid-column-gap, 5px); - background: rgba(#333438, 0.8); + background: $ingameHudBg; grid-template-columns: 1fr auto; grid-template-rows: auto 1fr; @@ -108,7 +108,8 @@ .variant { grid-row: 2 / 3; @include S(border-radius, $globalBorderRadius); - background: rgba(0, 10, 20, 0.2); + background: rgba($ingameHudBg, 0.3); + opacity: 0.5; display: inline-flex; vertical-align: top; position: relative; @@ -117,7 +118,8 @@ @include S(grid-gap, 10px); &.active { - background-color: rgba(74, 163, 223, 0.6); + opacity: 1; + background-color: rgba($colorBlueBright, 0.8); } $iconSize: 25px; diff --git a/src/css/ingame_hud/buildings_toolbar.scss b/src/css/ingame_hud/buildings_toolbar.scss index 13da9f99..d481407e 100644 --- a/src/css/ingame_hud/buildings_toolbar.scss +++ b/src/css/ingame_hud/buildings_toolbar.scss @@ -41,8 +41,8 @@ background: center center / 70% no-repeat; &:not(.unlocked) { - @include S(width, 30px); - opacity: 0.8; + @include S(width, 20px); + opacity: 0.15; background-image: none !important; &::before { diff --git a/src/css/ingame_hud/debug_info.scss b/src/css/ingame_hud/debug_info.scss index 37e3a07c..f370d04e 100644 --- a/src/css/ingame_hud/debug_info.scss +++ b/src/css/ingame_hud/debug_info.scss @@ -1,8 +1,9 @@ #ingame_HUD_DebugInfo { position: absolute; @include S(bottom, 5px); - @include S(left, 5px); + @include S(right, 5px); + text-align: right; font-size: 15px; display: flex; line-height: 15px; diff --git a/src/css/ingame_hud/dialogs.scss b/src/css/ingame_hud/dialogs.scss index c40db728..eef87505 100644 --- a/src/css/ingame_hud/dialogs.scss +++ b/src/css/ingame_hud/dialogs.scss @@ -118,7 +118,7 @@ pointer-events: all; @include S(width, 350px); - > strong { + strong { font-weight: bold; } @@ -139,6 +139,14 @@ background-color: rgb(250, 206, 206); } } + + ul.bucketList { + padding-left: 30px; + + li { + display: list-item; + } + } } > .buttons { diff --git a/src/css/ingame_hud/game_menu.scss b/src/css/ingame_hud/game_menu.scss index b191bc31..41ea600f 100644 --- a/src/css/ingame_hud/game_menu.scss +++ b/src/css/ingame_hud/game_menu.scss @@ -76,7 +76,7 @@ } } - > button { + .buttonContainer button { @include PlainText; color: #fff; border-color: rgba(0, 0, 0, 0.1); @@ -86,7 +86,7 @@ @include S(margin-right, 3px); @include IncreasedClickArea(0px); @include ButtonText; - @include S(min-height, 30px); + @include S(min-height, 40px); transition: all 0.12s ease-in-out; transition-property: opacity, transform; display: inline-flex; diff --git a/src/css/ingame_hud/interactive_tutorial.scss b/src/css/ingame_hud/interactive_tutorial.scss new file mode 100644 index 00000000..d4fb58e1 --- /dev/null +++ b/src/css/ingame_hud/interactive_tutorial.scss @@ -0,0 +1,50 @@ +#ingame_HUD_InteractiveTutorial { + position: absolute; + @include S(left, 10px); + @include S(bottom, 10px); + + @include StyleBelowWidth(1430px) { + @include S(bottom, 10px + 40px); + } + + @include S(width, 150px); + + background: $ingameHudBg; + @include S(padding, 4px); + color: #eee; + display: flex; + flex-direction: column; + + @include MakeAnimationWrappedEvenOdd(0.5s ease-in-out) { + 0% { + } + + 50% { + transform: translateX(-100%); + } + + 100% { + } + } + + .title { + color: #fff; + opacity: 0.5; + @include SuperSmallText; + text-transform: uppercase; + } + + .desc { + @include SuperSmallText; + strong { + color: $colorBlueBright; + font-weight: bold; + } + } + + .helperGif { + @include S(margin-top, 5px); + @include S(height, 150px); + background: center center / contain no-repeat; + } +} diff --git a/src/css/ingame_hud/mass_selector.scss b/src/css/ingame_hud/mass_selector.scss index 5ccbf86a..ddd2d40a 100644 --- a/src/css/ingame_hud/mass_selector.scss +++ b/src/css/ingame_hud/mass_selector.scss @@ -3,8 +3,7 @@ @include S(top, 50px); left: 50%; transform: translateX(-50%); - background: rgba(lighten(#f77, 5), 0.95); - @include S(border-radius, $globalBorderRadius); + background: $ingameHudBg; @include S(padding, 6px, 10px); @include SuperSmallText; color: #fff; diff --git a/src/css/ingame_hud/tutorial_hints.scss b/src/css/ingame_hud/tutorial_hints.scss index 41cffacd..ad1096af 100644 --- a/src/css/ingame_hud/tutorial_hints.scss +++ b/src/css/ingame_hud/tutorial_hints.scss @@ -1,7 +1,12 @@ #ingame_HUD_TutorialHints { position: absolute; @include S(left, 10px); - @include S(bottom, 50px); + @include S(bottom, 10px); + + @include StyleBelowWidth(1430px) { + @include S(bottom, 50px); + } + display: flex; flex-direction: column; background: rgba(50, 60, 70, 0); @@ -50,6 +55,7 @@ button.toggleHint { @include PlainText; + @include IncreasedClickArea(0px); } } @@ -60,7 +66,7 @@ } &.enlarged { - background: rgba(50, 60, 70, 0.9); + background: $ingameHudBg; left: 50%; bottom: 50%; transform: translate(-50%, 50%); @@ -75,7 +81,7 @@ bottom: -1000px; z-index: 0; - background: rgba(50, 60, 70, 0.3); + background: rgba($ingameHudBg, 0.3); } .header { diff --git a/src/css/ingame_hud/watermark.scss b/src/css/ingame_hud/watermark.scss index 4d0e83a2..d14a7fc8 100644 --- a/src/css/ingame_hud/watermark.scss +++ b/src/css/ingame_hud/watermark.scss @@ -1,6 +1,6 @@ #ingame_HUD_Watermark { position: absolute; - background: uiResource("get_on_itch_io.svg") center center / contain no-repeat; + background: uiResource("get_on_steam.png") center center / contain no-repeat; @include S(width, 110px); @include S(height, 40px); @include S(top, 10px); diff --git a/src/css/ingame_hud/waypoints.scss b/src/css/ingame_hud/waypoints.scss index 6517bbcf..fee1ec37 100644 --- a/src/css/ingame_hud/waypoints.scss +++ b/src/css/ingame_hud/waypoints.scss @@ -1,7 +1,7 @@ #ingame_HUD_Waypoints_Hint { position: absolute; @include S(right, 10px); - @include S(bottom, 100px); + @include S(bottom, 10px); display: flex; flex-direction: column; diff --git a/src/css/main.scss b/src/css/main.scss index 46fdab9c..10bf384e 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -49,6 +49,7 @@ @import "ingame_hud/watermark"; @import "ingame_hud/blueprint_placer"; @import "ingame_hud/waypoints"; +@import "ingame_hud/interactive_tutorial"; // prettier-ignore $elements: @@ -69,6 +70,7 @@ ingame_HUD_Notifications, ingame_HUD_MassSelector, ingame_HUD_DebugInfo, ingame_HUD_EntityDebugger, +ingame_HUD_InteractiveTutorial, ingame_HUD_TutorialHints, ingame_HUD_buildings_toolbar, ingame_HUD_BlueprintPlacer, @@ -79,9 +81,9 @@ ingame_HUD_Watermark, ingame_HUD_BetaOverlay, // Dialogs -ingame_HUD_UnlockNotification, ingame_HUD_Shop, ingame_HUD_Statistics, +ingame_HUD_UnlockNotification, ingame_HUD_SettingsMenu, ingame_HUD_ModalDialogs; diff --git a/src/css/states/main_menu.scss b/src/css/states/main_menu.scss index e5d8bbef..6466500c 100644 --- a/src/css/states/main_menu.scss +++ b/src/css/states/main_menu.scss @@ -60,28 +60,17 @@ .mainWrapper { @include S(padding, 0, 10px); - align-items: center; + align-items: start; justify-items: center; - &.noDemo { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } - - &.demo { - @include S(grid-column-gap, 10px); - display: grid; - grid-template-columns: 1fr 1fr; - } + @include S(grid-column-gap, 10px); + display: grid; + grid-template-columns: 1fr 1fr; .standaloneBanner { background: rgb(255, 234, 245); @include S(border-radius, $globalBorderRadius); - height: 100%; box-sizing: border-box; - @include S(width, 300px); @include S(padding, 15px); display: flex; @@ -114,9 +103,9 @@ .steamLink { width: 100%; - @include S(height, 50px); + @include S(height, 40px); - background: uiResource("get_on_itch_io.svg") center center / contain no-repeat; + background: uiResource("get_on_steam.png") center center / contain no-repeat; overflow: hidden; display: block; text-indent: -999em; @@ -165,6 +154,44 @@ border: #{D(2px)} solid rgba(0, 10, 20, 0.1); } + .sideContainer { + display: flex; + flex-direction: column; + @include S(width, 300px); + + .standaloneBanner { + flex-grow: 1; + @include S(margin-bottom, 10px); + } + + .contest { + flex-grow: 1; + background: rgb(32, 187, 166); + @include S(padding, 15px); + + h3 { + @include Heading; + color: #fff; + font-weight: bold; + text-transform: uppercase; + @include S(margin-bottom, 5px); + } + p { + color: #fff; + @include Text; + strong { + font-weight: bold; + } + @include S(margin-bottom, 5px); + } + + button { + background: #fff; + color: #333538; + } + } + } + .mainContainer { display: flex; align-items: center; diff --git a/src/css/states/mobile_warning.scss b/src/css/states/mobile_warning.scss index 2e68b56a..6dc84e3c 100644 --- a/src/css/states/mobile_warning.scss +++ b/src/css/states/mobile_warning.scss @@ -30,7 +30,7 @@ width: 200px; height: 80px; min-height: 40px; - background: uiResource("get_on_itch_io.svg") center center / contain no-repeat; + background: uiResource("get_on_steam.png") center center / contain no-repeat; overflow: hidden; display: block; text-indent: -999em; diff --git a/src/css/variables.scss b/src/css/variables.scss index 03213900..5f056a00 100644 --- a/src/css/variables.scss +++ b/src/css/variables.scss @@ -34,8 +34,7 @@ $colorGreenBright: #66bb6a; $colorBlueBright: rgb(74, 163, 223); $colorRedBright: #ef5072; $themeColor: #393747; -$ingameHudBg: rgba($accentColorBright, 0.1); -$ingameHudBorder: #{D(1.5px)} solid $accentColorDark; +$ingameHudBg: rgba(#333438, 0.9); $text3dColor: #f4ffff; diff --git a/src/html/index.html b/src/html/index.html index 243455ea..b1d89377 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -40,9 +40,6 @@ - - - diff --git a/src/js/application.js b/src/js/application.js index d337f94a..b2e57ddc 100644 --- a/src/js/application.js +++ b/src/js/application.js @@ -121,29 +121,18 @@ export class Application { * Initializes all platform instances */ initPlatformDependentInstances() { - logger.log("Creating platform dependent instances"); - - // Start with empty ad provider - this.adProvider = new NoAdProvider(this); - - if (G_IS_STANDALONE) { - this.storage = new StorageImplElectron(this); - } else { - if (window.indexedDB) { - this.storage = new StorageImplBrowserIndexedDB(this); - } else { - this.storage = new StorageImplBrowser(this); - } - } - this.sound = new SoundImplBrowser(this); + logger.log("Creating platform dependent instances (standalone=", G_IS_STANDALONE, ")"); if (G_IS_STANDALONE) { this.platformWrapper = new PlatformWrapperImplElectron(this); } else { this.platformWrapper = new PlatformWrapperImplBrowser(this); } - this.analytics = new GoogleAnalyticsImpl(this); + // Start with empty ad provider + this.adProvider = new NoAdProvider(this); + this.sound = new SoundImplBrowser(this); + this.analytics = new GoogleAnalyticsImpl(this); this.gameAnalytics = new ShapezGameAnalytics(this); } @@ -267,11 +256,15 @@ export class Application { onAppRenderableStateChanged(renderable) { logger.log("Application renderable:", renderable); window.focus(); + const currentState = this.stateMgr.getCurrentState(); if (!renderable) { - this.stateMgr.getCurrentState().onAppPause(); + if (currentState) { + currentState.onAppPause(); + } } else { - // Got resume - this.stateMgr.getCurrentState().onAppResume(); + if (currentState) { + currentState.onAppResume(); + } this.checkResize(); } @@ -285,7 +278,10 @@ export class Application { if (!this.unloaded) { logSection("UNLOAD HANDLER", "#f77"); this.unloaded = true; - this.stateMgr.getCurrentState().onBeforeExit(); + const currentState = this.stateMgr.getCurrentState(); + if (currentState) { + currentState.onBeforeExit(); + } this.deinitialize(); } } @@ -295,8 +291,9 @@ export class Application { */ onBeforeUnload(event) { logSection("BEFORE UNLOAD HANDLER", "#f77"); + const currentState = this.stateMgr.getCurrentState(); - if (!G_IS_DEV && this.stateMgr.getCurrentState().getHasUnloadConfirmation()) { + if (!G_IS_DEV && currentState && currentState.getHasUnloadConfirmation()) { if (!G_IS_STANDALONE) { // Need to show a "Are you sure you want to exit" event.preventDefault(); @@ -346,7 +343,10 @@ export class Application { return; } - this.stateMgr.getCurrentState().onBackgroundTick(dt); + const currentState = this.stateMgr.getCurrentState(); + if (currentState) { + currentState.onBackgroundTick(dt); + } } /** @@ -366,7 +366,10 @@ export class Application { this.lastResizeCheck = time; } - this.stateMgr.getCurrentState().onRender(dt); + const currentState = this.stateMgr.getCurrentState(); + if (currentState) { + currentState.onRender(dt); + } } /** @@ -379,7 +382,10 @@ export class Application { if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) { this.screenWidth = w; this.screenHeight = h; - this.stateMgr.getCurrentState().onResized(this.screenWidth, this.screenHeight); + const currentState = this.stateMgr.getCurrentState(); + if (currentState) { + currentState.onResized(this.screenWidth, this.screenHeight); + } const scale = this.getEffectiveUiScale(); waitNextFrame().then(() => document.documentElement.style.setProperty("--ui-scale", scale)); diff --git a/src/js/changelog.js b/src/js/changelog.js index 0559801f..bbf9cb74 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -1,4 +1,39 @@ export const CHANGELOG = [ + { + version: "1.1.8", + date: "07.06.2020", + entries: [ + "You can now purchase the standalone on steam! View steam page", + "Added ability to create markers in the demo, but only two.", + "Contest #01 has ended! I'll now work through the entries, select the 5 I like most and present them to the community to vote for!", + ], + }, + { + version: "1.1.7", + date: "04.06.2020", + entries: ["HOTFIX: Fix savegames not showing up on the standalone version"], + }, + { + version: "1.1.6", + date: "04.06.2020", + entries: [ + "The steam release will happen on the 7th of June - Be sure to add it to your wishlist! View on steam", + "Fixed level complete dialog being blurred when the shop was opened before", + "Standalone: Increased icon visibility for windows builds", + "Web version: Fixed firefox not loading the game when browsing in private mode", + ], + }, + + { + version: "1.1.5", + date: "03.06.2020", + entries: ["Added weekly contests!"], + }, + { + version: "1.1.4", + date: "01.06.2020", + entries: ["Add 'interactive' tutorial for the first level to improve onboarding experience"], + }, { version: "1.1.3", date: "01.06.2020", diff --git a/src/js/core/background_resources_loader.js b/src/js/core/background_resources_loader.js index 28d414f2..ff99d23c 100644 --- a/src/js/core/background_resources_loader.js +++ b/src/js/core/background_resources_loader.js @@ -10,7 +10,10 @@ import { AtlasDefinition, atlasFiles } from "./atlas_definitions"; const logger = createLogger("background_loader"); -const essentialMainMenuSprites = ["logo.png", ...G_ALL_UI_IMAGES.filter(src => src.startsWith("ui/"))]; +const essentialMainMenuSprites = [ + "logo.png", + ...G_ALL_UI_IMAGES.filter(src => src.startsWith("ui/") && src.indexOf(".gif") < 0), +]; const essentialMainMenuSounds = [ SOUNDS.uiClick, SOUNDS.uiError, @@ -21,7 +24,7 @@ const essentialMainMenuSounds = [ ]; const essentialBareGameAtlases = atlasFiles; -const essentialBareGameSprites = G_ALL_UI_IMAGES; +const essentialBareGameSprites = G_ALL_UI_IMAGES.filter(src => src.indexOf(".gif") < 0); const essentialBareGameSounds = [MUSIC.theme]; const additionalGameSprites = []; diff --git a/src/js/core/click_detector.js b/src/js/core/click_detector.js index 1e332aa2..5f6816f7 100644 --- a/src/js/core/click_detector.js +++ b/src/js/core/click_detector.js @@ -9,7 +9,7 @@ import { GLOBAL_APP } from "./globals"; const logger = createLogger("click_detector"); -export const MAX_MOVE_DISTANCE_PX = IS_MOBILE ? 20 : 40; +export const MAX_MOVE_DISTANCE_PX = IS_MOBILE ? 20 : 80; // For debugging const registerClickDetectors = G_IS_DEV && true; @@ -404,11 +404,11 @@ export class ClickDetector { if (this.clickDownPosition) { const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); const distance = pos.distance(this.clickDownPosition); - if (distance <= this.maxDistance) { + if (!IS_MOBILE || distance <= this.maxDistance) { dispatchClick = true; dispatchClickPos = pos; } else { - // console.warn("[ClickDetector] Touch does not count as click: ms=", timeSinceStart, "-> tolerance:", tolerance, "(was", distance, ")"); + console.warn("[ClickDetector] Touch does not count as click:", "(was", distance, ")"); } } diff --git a/src/js/core/config.js b/src/js/core/config.js index c84dee3a..540bccfd 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -15,8 +15,7 @@ export const THIRDPARTY_URLS = { discord: "https://discord.gg/HN7EVzV", github: "https://github.com/tobspr/shapez.io", - // standaloneStorePage: "https://steam.shapez.io", - standaloneStorePage: "https://tobspr.itch.io/shapez.io", + standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/", }; export const globalConfig = { @@ -46,7 +45,7 @@ export const globalConfig = { // Belt speeds // NOTICE: Update webpack.production.config too! beltSpeedItemsPerSecond: 2, - itemSpacingOnBelts: 0.63, + itemSpacingOnBelts: 0.8, minerSpeedItemsPerSecond: 0, // COMPUTED undergroundBeltMaxTilesByTier: [5, 8], @@ -83,7 +82,7 @@ export const globalConfig = { debug: { /* dev:start */ - // fastGameEnter: true, + fastGameEnter: true, // noArtificialDelays: true, // disableSavegameWrite: true, // showEntityBounds: true, @@ -95,7 +94,7 @@ export const globalConfig = { // rewardsInstant: true, allBuildingsUnlocked: true, blueprintsNoCost: true, - // upgradesNoCost: true, + upgradesNoCost: true, // disableUnlockDialog: true, // disableLogicTicks: true, // testClipping: true, @@ -104,11 +103,12 @@ export const globalConfig = { // enableEntityInspector: true, // testAds: true, // disableMapOverview: true, - disableTutorialHints: true, + // disableTutorialHints: true, disableUpgradeNotification: true, // instantBelts: true, // instantProcessors: true, // instantMiners: true, + // resumeGameOnFastEnter: false, // renderForTrailer: true, /* dev:end */ diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index 7d1dbacd..ef7797a9 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -94,14 +94,15 @@ export class HubGoals extends BasicSerializableObject { this.createNextGoal(); - // Allow quickly switching goals in dev mode with key "C" + // Allow quickly switching goals in dev mode if (G_IS_DEV) { - this.root.gameState.inputReciever.keydown.add(key => { - if (key.keyCode === 66) { - // Key: b - this.onGoalCompleted(); - } - }); + if (G_IS_DEV) { + window.addEventListener("keydown", ev => { + if (ev.key === "b") { + this.onGoalCompleted(); + } + }); + } } } diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index e1f1dbbf..9104e3bf 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -28,6 +28,7 @@ import { HUDWatermark } from "./parts/watermark"; import { HUDModalDialogs } from "./parts/modal_dialogs"; import { HUDPartTutorialHints } from "./parts/tutorial_hints"; import { HUDWaypoints } from "./parts/waypoints"; +import { HUDInteractiveTutorial } from "./parts/interactive_tutorial"; /* dev:start */ import { TrailerMaker } from "./trailer_maker"; @@ -87,6 +88,7 @@ export class GameHUD { } if (this.root.app.settings.getAllSettings().offerHints) { this.parts.tutorialHints = new HUDPartTutorialHints(this.root); + this.parts.interactiveTutorial = new HUDInteractiveTutorial(this.root); } const frag = document.createDocumentFragment(); diff --git a/src/js/game/hud/parts/game_menu.js b/src/js/game/hud/parts/game_menu.js index ef05bdbf..64285624 100644 --- a/src/js/game/hud/parts/game_menu.js +++ b/src/js/game/hud/parts/game_menu.js @@ -5,9 +5,9 @@ import { enumNotificationType } from "./notifications"; import { T } from "../../../translations"; import { KEYMAPPINGS } from "../../key_action_mapper"; import { IS_DEMO } from "../../../core/config"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; export class HUDGameMenu extends BaseHUDPart { - initialize() {} createElements(parent) { this.element = makeDiv(parent, "ingame_HUD_GameMenu"); @@ -22,12 +22,16 @@ export class HUDGameMenu extends BaseHUDPart { T.ingame.notifications.newUpgrade, enumNotificationType.upgrade, ]), + visible: () => + !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3, }, { id: "stats", label: "Stats", handler: () => this.root.hud.parts.statistics.show(), keybinding: KEYMAPPINGS.ingame.menuOpenStats, + visible: () => + !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3, }, ]; @@ -36,14 +40,24 @@ export class HUDGameMenu extends BaseHUDPart { * button: HTMLElement, * badgeElement: HTMLElement, * lastRenderAmount: number, + * condition?: function, * notification: [string, enumNotificationType] * }>} */ this.badgesToUpdate = []; - buttons.forEach(({ id, label, handler, keybinding, badge, notification }) => { + /** @type {Array<{ + * button: HTMLElement, + * condition: function, + * domAttach: DynamicDomAttach + * }>} */ + this.visibilityToUpdate = []; + + this.buttonsElement = makeDiv(this.element, null, ["buttonContainer"]); + + buttons.forEach(({ id, label, handler, keybinding, badge, notification, visible }) => { const button = document.createElement("button"); button.setAttribute("data-button-id", id); - this.element.appendChild(button); + this.buttonsElement.appendChild(button); this.trackClicks(button, handler); if (keybinding) { @@ -52,6 +66,14 @@ export class HUDGameMenu extends BaseHUDPart { binding.appendLabelToElement(button); } + if (visible) { + this.visibilityToUpdate.push({ + button, + condition: visible, + domAttach: new DynamicDomAttach(this.root, button), + }); + } + if (badge) { const badgeElement = makeDiv(button, null, ["badge"]); this.badgesToUpdate.push({ @@ -60,6 +82,7 @@ export class HUDGameMenu extends BaseHUDPart { button, badgeElement, notification, + condition: visible, }); } }); @@ -78,27 +101,52 @@ export class HUDGameMenu extends BaseHUDPart { this.musicButton.classList.toggle("muted", this.root.app.settings.getAllSettings().musicMuted); this.sfxButton.classList.toggle("muted", this.root.app.settings.getAllSettings().soundsMuted); - + } + initialize() { this.root.signals.gameSaved.add(this.onGameSaved, this); } update() { let playSound = false; let notifications = new Set(); + + // Update visibility of buttons + for (let i = 0; i < this.visibilityToUpdate.length; ++i) { + const { button, condition, domAttach } = this.visibilityToUpdate[i]; + domAttach.update(condition()); + } + + // Check for notifications and badges for (let i = 0; i < this.badgesToUpdate.length; ++i) { - const { badge, button, badgeElement, lastRenderAmount, notification } = this.badgesToUpdate[i]; + const { + badge, + button, + badgeElement, + lastRenderAmount, + notification, + condition, + } = this.badgesToUpdate[i]; + + if (condition && !condition()) { + // Do not show notifications for invisible buttons + continue; + } + + // Check if the amount shown differs from the one shown last frame const amount = badge(); if (lastRenderAmount !== amount) { if (amount > 0) { badgeElement.innerText = amount; } - // Check if the badge increased + // Check if the badge increased, if so play a notification if (amount > lastRenderAmount) { playSound = true; if (notification) { notifications.add(notification); } } + + // Rerender notifications this.badgesToUpdate[i].lastRenderAmount = amount; button.classList.toggle("hasBadge", amount > 0); } @@ -107,6 +155,7 @@ export class HUDGameMenu extends BaseHUDPart { if (playSound) { this.root.soundProxy.playUi(SOUNDS.badgeNotification); } + notifications.forEach(([notification, type]) => { this.root.hud.signals.notification.dispatch(notification, type); }); @@ -118,13 +167,6 @@ export class HUDGameMenu extends BaseHUDPart { } startSave() { - // if (IS_DEMO) { - // this.root.hud.parts.dialogs.showFeatureRestrictionInfo( - // null, - // T.dialogs.saveNotPossibleInDemo.desc - // ); - // } - this.root.gameState.doSave(); } diff --git a/src/js/game/hud/parts/interactive_tutorial.js b/src/js/game/hud/parts/interactive_tutorial.js new file mode 100644 index 00000000..40273638 --- /dev/null +++ b/src/js/game/hud/parts/interactive_tutorial.js @@ -0,0 +1,81 @@ +import { BaseHUDPart } from "../base_hud_part"; +import { makeDiv } from "../../../core/utils"; +import { GameRoot } from "../../root"; +import { MinerComponent } from "../../components/miner"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { TrackedState } from "../../../core/tracked_state"; +import { cachebust } from "../../../core/cachebust"; +import { T } from "../../../translations"; + +const tutorialsByLevel = [ + // Level 1 + [ + // 1.1. place an extractor + { + id: "1_1_extractor", + condition: /** @param {GameRoot} root */ root => { + return root.entityMgr.getAllWithComponent(MinerComponent).length === 0; + }, + }, + // 1.2. connect to hub + { + id: "1_2_conveyor", + condition: /** @param {GameRoot} root */ root => { + return root.hubGoals.getCurrentGoalDelivered() === 0; + }, + }, + // 1.3 wait for completion + { + id: "1_3_expand", + condition: () => true, + }, + ], +]; + +export class HUDInteractiveTutorial extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv( + parent, + "ingame_HUD_InteractiveTutorial", + ["animEven"], + ` + Tutorial + ` + ); + + this.elementDescription = makeDiv(this.element, null, ["desc"]); + this.elementGif = makeDiv(this.element, null, ["helperGif"]); + } + + initialize() { + this.domAttach = new DynamicDomAttach(this.root, this.element); + this.currentHintId = new TrackedState(this.onHintChanged, this); + } + + onHintChanged(hintId) { + this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId]; + this.elementGif.style.backgroundImage = + "url('" + cachebust("res/ui/interactive_tutorial.noinline/" + hintId + ".gif") + "')"; + this.element.classList.toggle("animEven"); + this.element.classList.toggle("animOdd"); + } + + update() { + // Compute current hint + const thisLevelHints = tutorialsByLevel[this.root.hubGoals.level - 1]; + let targetHintId = null; + + if (thisLevelHints) { + for (let i = 0; i < thisLevelHints.length; ++i) { + const hint = thisLevelHints[i]; + if (hint.condition(this.root)) { + targetHintId = hint.id; + break; + } + } + } + + this.currentHintId.set(targetHintId); + this.domAttach.update(!!targetHintId); + } +} diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index 013eb5f7..05455065 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -23,10 +23,6 @@ export class HUDKeybindingOverlay extends BaseHUDPart { "ingame_HUD_KeybindingOverlay", [], ` -
- ${getKeycode(KEYMAPPINGS.navigation.createMarker)} - -
@@ -35,8 +31,15 @@ export class HUDKeybindingOverlay extends BaseHUDPart { ${getKeycode(KEYMAPPINGS.navigation.mapMoveDown)} ${getKeycode(KEYMAPPINGS.navigation.mapMoveRight)} -
- + + + + +
+ + +
+
${getKeycode( KEYMAPPINGS.massSelect.massSelectStart diff --git a/src/js/game/hud/parts/tutorial_hints.js b/src/js/game/hud/parts/tutorial_hints.js index 27fd5530..428923d0 100644 --- a/src/js/game/hud/parts/tutorial_hints.js +++ b/src/js/game/hud/parts/tutorial_hints.js @@ -1,4 +1,3 @@ -import { cachebust } from "../../../core/cachebust"; import { InputReceiver } from "../../../core/input_receiver"; import { TrackedState } from "../../../core/tracked_state"; import { makeDiv } from "../../../core/utils"; @@ -6,9 +5,8 @@ import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; import { T } from "../../../translations"; -import { globalConfig } from "../../../core/config"; -const tutorialVideos = [1, 2, 3, 4, 5, 6, 7, 9, 10, 11]; +const tutorialVideos = [2, 3, 4, 5, 6, 7, 9, 10, 11]; export class HUDPartTutorialHints extends BaseHUDPart { createElements(parent) { @@ -55,15 +53,6 @@ export class HUDPartTutorialHints extends BaseHUDPart { this.domAttach = new DynamicDomAttach(this.root, this.element); this.currentShownLevel = new TrackedState(this.updateVideoUrl, this); - - this.root.signals.postLoadHook.add(() => { - if (this.root.hubGoals.level === 1 && !(G_IS_DEV && globalConfig.debug.disableTutorialHints)) { - this.root.hud.parts.dialogs.showInfo( - T.dialogs.hintDescription.title, - T.dialogs.hintDescription.desc - ); - } - }); } updateVideoUrl(level) { diff --git a/src/js/game/hud/parts/unlock_notification.js b/src/js/game/hud/parts/unlock_notification.js index 847efc70..20a4482f 100644 --- a/src/js/game/hud/parts/unlock_notification.js +++ b/src/js/game/hud/parts/unlock_notification.js @@ -28,7 +28,7 @@ export class HUDUnlockNotification extends BaseHUDPart { createElements(parent) { this.inputReciever = new InputReceiver("unlock-notification"); - this.element = makeDiv(parent, "ingame_HUD_UnlockNotification", []); + this.element = makeDiv(parent, "ingame_HUD_UnlockNotification", ["noBlur"]); const dialog = makeDiv(this.element, null, ["dialog"]); diff --git a/src/js/game/hud/parts/watermark.js b/src/js/game/hud/parts/watermark.js index 94df6257..0b5b85bc 100644 --- a/src/js/game/hud/parts/watermark.js +++ b/src/js/game/hud/parts/watermark.js @@ -13,7 +13,7 @@ export class HUDWatermark extends BaseHUDPart { } onWatermarkClick() { - this.root.app.analytics.trackUiClick("watermark_click"); + this.root.app.analytics.trackUiClick("watermark_click_2"); this.root.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.standaloneStorePage); } diff --git a/src/js/game/hud/parts/waypoints.js b/src/js/game/hud/parts/waypoints.js index d981d33e..82187e55 100644 --- a/src/js/game/hud/parts/waypoints.js +++ b/src/js/game/hud/parts/waypoints.js @@ -133,11 +133,6 @@ export class HUDWaypoints extends BaseHUDPart { * @param {Vector=} worldPos Override the world pos, otherwise it is the camera position */ requestCreateMarker(worldPos = null) { - if (IS_DEMO) { - this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.creatingMarkers); - return; - } - const markerNameInput = new FormElementInput({ id: "markerName", label: null, @@ -157,6 +152,11 @@ export class HUDWaypoints extends BaseHUDPart { const center = worldPos || this.root.camera.center; dialog.buttonSignals.ok.add(() => { + if (IS_DEMO && this.waypoints.length > 2) { + this.root.hud.parts.dialogs.showFeatureRestrictionInfo("", T.dialogs.markerDemoLimit.desc); + return; + } + this.waypoints.push({ label: markerNameInput.getValue(), center: { x: center.x, y: center.y }, diff --git a/src/js/game/tutorial_goals.js b/src/js/game/tutorial_goals.js index 14b231f2..c7fa581b 100644 --- a/src/js/game/tutorial_goals.js +++ b/src/js/game/tutorial_goals.js @@ -35,7 +35,7 @@ export const tutorialGoals = [ // Circle { shape: "CuCuCuCu", // belts t1 - required: 20, + required: 40, reward: enumHubGoalRewards.reward_cutter_and_trash, }, diff --git a/src/js/main.js b/src/js/main.js index 4803d980..3847141e 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -18,6 +18,36 @@ if (window.coreThreadLoadedCb) { window.coreThreadLoadedCb(); } +if (!G_IS_DEV && !G_IS_STANDALONE) { + const monthlyUsers = 300; // thousand + const logrocketLimit = 10; // thousand + const percentageOfUsers = logrocketLimit / monthlyUsers; + + if (Math.random() <= percentageOfUsers) { + logger.log("Analyzing this session with logrocket"); + const logrocket = require("logrocket"); + logrocket.init("p1x9zh/shapezio"); + + try { + logrocket.getSessionURL(function (sessionURL) { + logger.log("Connected lockrocket to GA"); + // @ts-ignore + try { + window.ga("send", { + hitType: "event", + eventCategory: "LogRocket", + eventAction: sessionURL, + }); + } catch (ex) { + logger.warn("Logrocket connection to analytics failed:", ex); + } + }); + } catch (ex) { + logger.warn("Logrocket connection to analytics failed:", ex); + } + } +} + console.log( `%cshapez.io ️%c\n© 2020 Tobias Springer IT Solutions\nCommit %c${G_BUILD_COMMIT_HASH}%c on %c${new Date( G_BUILD_TIME diff --git a/src/js/platform/browser/wrapper.js b/src/js/platform/browser/wrapper.js index fa3f80b3..ab5ec327 100644 --- a/src/js/platform/browser/wrapper.js +++ b/src/js/platform/browser/wrapper.js @@ -1,11 +1,13 @@ import { Math_min } from "../../core/builtins"; -import { globalConfig, IS_MOBILE, IS_DEBUG, IS_DEMO } from "../../core/config"; +import { globalConfig, IS_DEMO, IS_MOBILE } from "../../core/config"; import { createLogger } from "../../core/logging"; import { queryParamOptions } from "../../core/query_parameters"; import { clamp } from "../../core/utils"; +import { GamedistributionAdProvider } from "../ad_providers/gamedistribution"; import { NoAdProvider } from "../ad_providers/no_ad_provider"; import { PlatformWrapperInterface } from "../wrapper"; -import { GamedistributionAdProvider } from "../ad_providers/gamedistribution"; +import { StorageImplBrowser } from "./storage"; +import { StorageImplBrowserIndexedDB } from "./storage_indexed_db"; const logger = createLogger("platform/browser"); @@ -72,7 +74,43 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface { logger.log("Embed provider:", this.embedProvider.id); - return super.initialize().then(() => this.initializeAdProvider()); + return this.detectStorageImplementation() + .then(() => this.initializeAdProvider()) + .then(() => super.initialize()); + } + + detectStorageImplementation() { + return new Promise(resolve => { + logger.log("Detecting storage"); + + if (!window.indexedDB) { + logger.log("Indexed DB not supported"); + this.app.storage = new StorageImplBrowser(this.app); + resolve(); + return; + } + + // Try accessing the indexedb + let request; + try { + request = window.indexedDB.open("indexeddb_feature_detection", 1); + } catch (ex) { + logger.warn("Error while opening indexed db:", ex); + resolve(); + return; + } + request.onerror = err => { + logger.log("Indexed DB can *not* be accessed: ", err); + logger.log("Using fallback to local storage"); + this.app.storage = new StorageImplBrowser(this.app); + resolve(); + }; + request.onsuccess = () => { + logger.log("Indexed DB *can* be accessed"); + this.app.storage = new StorageImplBrowserIndexedDB(this.app); + resolve(); + }; + }); } getHasUnlimitedSavegames() { @@ -83,71 +121,6 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface { return this.embedProvider.showDemoBadge; } - onSentryLoaded() { - logger.log("Initializing sentry"); - window.Sentry.init({ - dsn: "TODO SENTRY DSN", - release: G_APP_ENVIRONMENT + "-" + G_BUILD_VERSION + "@" + G_BUILD_COMMIT_HASH, - // Will cause a deprecation warning, but the demise of `ignoreErrors` is still under discussion. - // See: https://github.com/getsentry/raven-js/issues/73 - ignoreErrors: [ - // Random plugins/extensions - "top.GLOBALS", - // See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error.html - "originalCreateNotification", - "canvas.contentDocument", - "MyApp_RemoveAllHighlights", - "http://tt.epicplay.com", - "Can't find variable: ZiteReader", - "jigsaw is not defined", - "ComboSearch is not defined", - "http://loading.retry.widdit.com/", - "atomicFindClose", - // Facebook borked - "fb_xd_fragment", - // ISP "optimizing" proxy - `Cache-Control: no-transform` seems to reduce this. (thanks @acdha) - // See http://stackoverflow.com/questions/4113268/how-to-stop-javascript-injection-from-vodafone-proxy - "bmi_SafeAddOnload", - "EBCallBackMessageReceived", - // See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx - "conduitPage", - // Generic error code from errors outside the security sandbox - // You can delete this if using raven.js > 1.0, which ignores these automatically. - "Script error.", - - // Errors from ads - "Cannot read property 'postMessage' of null", - - // Firefox only - "AbortError: The operation was aborted.", - - "", - ], - ignoreUrls: [ - // Facebook flakiness - /graph\.facebook\.com/i, - // Facebook blocked - /connect\.facebook\.net\/en_US\/all\.js/i, - // Woopra flakiness - /eatdifferent\.com\.woopra-ns\.com/i, - /static\.woopra\.com\/js\/woopra\.js/i, - // Chrome extensions - /extensions\//i, - /^chrome:\/\//i, - // Other plugins - /127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb - /webappstoolbarba\.texthelp\.com\//i, - /metrics\.itunes\.apple\.com\.edgesuite\.net\//i, - ], - beforeSend(event, hint) { - if (window.anyModLoaded) { - return null; - } - return event; - }, - }); - } - getId() { return "browser@" + this.embedProvider.id; } diff --git a/src/js/platform/electron/wrapper.js b/src/js/platform/electron/wrapper.js index 70ebf3fc..69bc9695 100644 --- a/src/js/platform/electron/wrapper.js +++ b/src/js/platform/electron/wrapper.js @@ -1,10 +1,17 @@ import { PlatformWrapperImplBrowser } from "../browser/wrapper"; import { getIPCRenderer } from "../../core/utils"; import { createLogger } from "../../core/logging"; +import { StorageImplElectron } from "./storage"; +import { PlatformWrapperInterface } from "../wrapper"; const logger = createLogger("electron-wrapper"); export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { + initialize() { + this.app.storage = new StorageImplElectron(this); + return PlatformWrapperInterface.prototype.initialize.call(this); + } + getId() { return "electron"; } @@ -22,6 +29,14 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { return false; } + getHasUnlimitedSavegames() { + return true; + } + + getShowDemoBadges() { + return false; + } + performRestart() { logger.log(this, "Performing restart"); window.location.reload(true); diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index 28b0d45c..8ca023b2 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -24,14 +24,11 @@ export class MainMenuState extends GameState {

${T.demoBanners.intro}

-
    - ${T.demoBanners.advantages.map(advantage => `
  • ${advantage}
  • `).join("")} -
- Get the shapez.io standalone! `; - return ` + return ( + ` @@ -54,18 +51,29 @@ export class MainMenuState extends GameState {
- - ${IS_DEMO ? `
${bannerHtml}
` : ""} +
+ ${IS_DEMO ? `
${bannerHtml}
` : ""} + +
+

${T.mainMenu.contests.contest_01_03062020.title}

+ ` + + /*

${T.mainMenu.contests.contest_01_03062020.desc}

+ */ + + ` +

${T.mainMenu.contests.contestOver}

+ +
+ +
+
${ isSupportedBrowser() @@ -104,7 +112,8 @@ export class MainMenuState extends GameState {
- `; + ` + ); } requestImportSavegame() { @@ -199,7 +208,7 @@ export class MainMenuState extends GameState { if (G_IS_DEV && globalConfig.debug.fastGameEnter) { const games = this.app.savegameMgr.getSavegamesMetaData(); - if (games.length > 0) { + if (games.length > 0 && globalConfig.debug.resumeGameOnFastEnter) { this.resumeGame(games[0]); } else { this.onPlayButtonClicked(); @@ -220,6 +229,11 @@ export class MainMenuState extends GameState { this.trackClicks(qs(".settingsButton"), this.onSettingsButtonClicked); this.trackClicks(qs(".changelog"), this.onChangelogClicked); + const contestButton = qs(".participateContest"); + if (contestButton) { + this.trackClicks(contestButton, this.onContestClicked); + } + if (G_IS_STANDALONE) { this.trackClicks(qs(".exitAppButton"), this.onExitAppButtonClicked); } @@ -254,7 +268,7 @@ export class MainMenuState extends GameState { } onSteamLinkClicked() { - this.app.analytics.trackUiClick("main_menu_steam_link"); + this.app.analytics.trackUiClick("main_menu_steam_link_2"); this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.standaloneStorePage); return false; } @@ -267,6 +281,15 @@ export class MainMenuState extends GameState { this.moveToState("ChangelogState"); } + onContestClicked() { + this.app.analytics.trackUiClick("contest_click"); + + this.dialogs.showInfo( + T.mainMenu.contests.contest_01_03062020.title, + T.mainMenu.contests.contest_01_03062020.longDesc + ); + } + renderSavegames() { const oldContainer = this.htmlElement.querySelector(".mainContainer .savegames"); if (oldContainer) { @@ -375,19 +398,6 @@ export class MainMenuState extends GameState { this.moveToState("SettingsState"); } - doStartNewGame() { - this.app.analytics.trackUiClick("startgame"); - - this.app.adProvider.showVideoAd().then(() => { - const savegame = this.app.savegameMgr.createNewSavegame(); - - this.moveToState("InGameState", { - savegame, - }); - this.app.analytics.trackUiClick("startgame_adcomplete"); - }); - } - onPlayButtonClicked() { if ( IS_DEMO && @@ -399,17 +409,15 @@ export class MainMenuState extends GameState { return; } - if (IS_DEMO) { - this.app.analytics.trackUiClick("startgame_pre_show"); - const { ok } = this.dialogs.showWarning( - T.dialogs.demoExplanation.title, - T.dialogs.demoExplanation.desc - ); - ok.add(() => this.doStartNewGame()); - return; - } + this.app.analytics.trackUiClick("startgame"); + this.app.adProvider.showVideoAd().then(() => { + const savegame = this.app.savegameMgr.createNewSavegame(); - this.doStartNewGame(); + this.moveToState("InGameState", { + savegame, + }); + this.app.analytics.trackUiClick("startgame_adcomplete"); + }); } onLeave() { diff --git a/src/js/states/mobile_warning.js b/src/js/states/mobile_warning.js index c6903164..de17578a 100644 --- a/src/js/states/mobile_warning.js +++ b/src/js/states/mobile_warning.js @@ -13,11 +13,11 @@ export class MobileWarningState extends GameState {

- I'm sorry, but shapez.io is not yet available on mobile devices! - (There is also no estimate when this will change, but feel to make a contribution! It's -  open source!)

+ I'm sorry, but shapez.io is not available on mobile devices yet! + There is also no estimate when this will change, but feel to make a contribution! It's +  open source!

-

If you want to play on your computer, you can also get the standalone on itch.io:

+

If you want to play on your computer, you can also get the standalone on steam:

{ ok.add(resolve); - viewUpdate.add(() => { - window.open("https://tobspr.itch.io/shapezio", "_blank"); - resolve(); - }); }); } }) diff --git a/tools/shape_generator/index.html b/tools/shape_generator/index.html deleted file mode 100644 index e72d8bc3..00000000 --- a/tools/shape_generator/index.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - shapez.io shape generator - - - - - - -

Shape generator

- -
Error
-
- -
- -
- - diff --git a/tools/shape_generator/index.js b/tools/shape_generator/index.js deleted file mode 100644 index 947040ea..00000000 --- a/tools/shape_generator/index.js +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Lots of code here is copied 1:1 from actual game files - * - */ - -/** @enum {string} */ -const enumSubShape = { - rect: "rect", - circle: "circle", - star: "star", - windmill: "windmill", -}; - -/** @enum {string} */ -const enumSubShapeToShortcode = { - [enumSubShape.rect]: "R", - [enumSubShape.circle]: "C", - [enumSubShape.star]: "S", - [enumSubShape.windmill]: "W", -}; - -/** @enum {enumSubShape} */ -const enumShortcodeToSubShape = {}; -for (const key in enumSubShapeToShortcode) { - enumShortcodeToSubShape[enumSubShapeToShortcode[key]] = key; -} - -const arrayQuadrantIndexToOffset = [ - { x: 1, y: -1 }, // tr - { x: 1, y: 1 }, // br - { x: -1, y: 1 }, // bl - { x: -1, y: -1 }, // tl -]; - -// From colors.js -/** @enum {string} */ -const enumColors = { - red: "red", - green: "green", - blue: "blue", - - yellow: "yellow", - purple: "purple", - cyan: "cyan", - - white: "white", - uncolored: "uncolored", -}; - -/** @enum {string} */ -const enumColorToShortcode = { - [enumColors.red]: "r", - [enumColors.green]: "g", - [enumColors.blue]: "b", - - [enumColors.yellow]: "y", - [enumColors.purple]: "p", - [enumColors.cyan]: "c", - - [enumColors.white]: "w", - [enumColors.uncolored]: "u", -}; - -/** @enum {string} */ -const enumColorsToHexCode = { - [enumColors.red]: "#ff666a", - [enumColors.green]: "#78ff66", - [enumColors.blue]: "#66a7ff", - - // red + green - [enumColors.yellow]: "#fcf52a", - - // red + blue - [enumColors.purple]: "#dd66ff", - - // blue + green - [enumColors.cyan]: "#87fff5", - - // blue + green + red - [enumColors.white]: "#ffffff", - - [enumColors.uncolored]: "#aaaaaa", -}; - -/** @enum {enumColors} */ -const enumShortcodeToColor = {}; -for (const key in enumColorToShortcode) { - enumShortcodeToColor[enumColorToShortcode[key]] = key; -} - -CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) { - if (r < 0.05) { - this.beginPath(); - this.rect(x, y, 1, 1); - return; - } - this.beginPath(); - this.arc(x, y, r, 0, 2.0 * Math.PI); -}; - -///////////////////////////////////////////////////// - -function radians(degrees) { - return (degrees * Math.PI) / 180.0; -} - -/** - * Generates the definition from the given short key - */ -function fromShortKey(key) { - const sourceLayers = key.split(":"); - let layers = []; - for (let i = 0; i < sourceLayers.length; ++i) { - const text = sourceLayers[i]; - if (text.length !== 8) { - throw new Error("Invalid layer: '" + text + "' -> must be 8 characters"); - } - - const quads = [null, null, null, null]; - for (let quad = 0; quad < 4; ++quad) { - const shapeText = text[quad * 2 + 0]; - const subShape = enumShortcodeToSubShape[shapeText]; - const color = enumShortcodeToColor[text[quad * 2 + 1]]; - if (subShape) { - if (!color) { - throw new Error("Invalid shape color key: " + key); - } - quads[quad] = { - subShape, - color, - }; - } else if (shapeText !== "-") { - throw new Error("Invalid shape key: " + shapeText); - } - } - layers.push(quads); - } - - return layers; -} - -function renderShape(layers) { - const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById("result")); - const context = canvas.getContext("2d"); - - context.save(); - context.fillStyle = "#fff"; - context.fillRect(0, 0, 1000, 1000); - - const w = 256; - const h = 256; - const dpi = 1; - - context.translate((w * dpi) / 2, (h * dpi) / 2); - context.scale((dpi * w) / 23, (dpi * h) / 23); - - context.fillStyle = "#e9ecf7"; - - const quadrantSize = 10; - const quadrantHalfSize = quadrantSize / 2; - - context.fillStyle = "rgba(40, 50, 65, 0.1)"; - context.beginCircle(0, 0, quadrantSize * 1.15); - context.fill(); - - for (let layerIndex = 0; layerIndex < layers.length; ++layerIndex) { - const quadrants = layers[layerIndex]; - - const layerScale = Math.max(0.1, 0.9 - layerIndex * 0.22); - - for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) { - if (!quadrants[quadrantIndex]) { - continue; - } - const { subShape, color } = quadrants[quadrantIndex]; - - const quadrantPos = arrayQuadrantIndexToOffset[quadrantIndex]; - const centerQuadrantX = quadrantPos.x * quadrantHalfSize; - const centerQuadrantY = quadrantPos.y * quadrantHalfSize; - - const rotation = radians(quadrantIndex * 90); - - context.translate(centerQuadrantX, centerQuadrantY); - context.rotate(rotation); - - context.fillStyle = enumColorsToHexCode[color]; - context.strokeStyle = "#555"; - context.lineWidth = 1; - - const insetPadding = 0.0; - - switch (subShape) { - case enumSubShape.rect: { - context.beginPath(); - const dims = quadrantSize * layerScale; - context.rect( - insetPadding + -quadrantHalfSize, - -insetPadding + quadrantHalfSize - dims, - dims, - dims - ); - - break; - } - case enumSubShape.star: { - context.beginPath(); - const dims = quadrantSize * layerScale; - - let originX = insetPadding - quadrantHalfSize; - let originY = -insetPadding + quadrantHalfSize - dims; - - const moveInwards = dims * 0.4; - context.moveTo(originX, originY + moveInwards); - context.lineTo(originX + dims, originY); - context.lineTo(originX + dims - moveInwards, originY + dims); - context.lineTo(originX, originY + dims); - context.closePath(); - break; - } - - case enumSubShape.windmill: { - context.beginPath(); - const dims = quadrantSize * layerScale; - - let originX = insetPadding - quadrantHalfSize; - let originY = -insetPadding + quadrantHalfSize - dims; - const moveInwards = dims * 0.4; - context.moveTo(originX, originY + moveInwards); - context.lineTo(originX + dims, originY); - context.lineTo(originX + dims, originY + dims); - context.lineTo(originX, originY + dims); - context.closePath(); - break; - } - - case enumSubShape.circle: { - context.beginPath(); - context.moveTo(insetPadding + -quadrantHalfSize, -insetPadding + quadrantHalfSize); - context.arc( - insetPadding + -quadrantHalfSize, - -insetPadding + quadrantHalfSize, - quadrantSize * layerScale, - -Math.PI * 0.5, - 0 - ); - context.closePath(); - break; - } - - default: { - assertAlways(false, "Unkown sub shape: " + subShape); - } - } - - context.fill(); - context.stroke(); - - context.rotate(-rotation); - context.translate(-centerQuadrantX, -centerQuadrantY); - } - } - - context.restore(); -} - -///////////////////////////////////////////////////// - -function showError(msg) { - const errorDiv = document.getElementById("error"); - errorDiv.classList.toggle("hasError", !!msg); - if (msg) { - errorDiv.innerText = msg; - } else { - errorDiv.innerText = "Shape generated"; - } -} - -// @ts-ignore -window.generate = () => { - showError(null); - // @ts-ignore - const code = document.getElementById("code").value.trim(); - - let parsed = null; - try { - parsed = fromShortKey(code); - } catch (ex) { - showError(ex); - return; - } - - renderShape(parsed); -}; - -// @ts-ignore -window.debounce = fn => { - setTimeout(fn, 0); -}; - -// @ts-ignore -window.addEventListener("load", window.generate); diff --git a/tools/shape_generator/package.json b/tools/shape_generator/package.json deleted file mode 100644 index 0715ac87..00000000 --- a/tools/shape_generator/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "shape_generator", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "private": true, - "devDependencies": { - "http-server": "^0.12.3" - }, - "scripts": { - "serve": "http-server . -p 9000 -g --cors -o -c-1" - } -} diff --git a/tools/shape_generator/yarn.lock b/tools/shape_generator/yarn.lock deleted file mode 100644 index 013aab17..00000000 --- a/tools/shape_generator/yarn.lock +++ /dev/null @@ -1,152 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -async@^2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - -basic-auth@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-1.1.0.tgz#45221ee429f7ee1e5035be3f51533f1cdfd29884" - integrity sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ= - -colors@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -corser@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" - integrity sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c= - -debug@^3.0.0, debug@^3.1.1: - 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" - -ecstatic@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/ecstatic/-/ecstatic-3.3.2.tgz#6d1dd49814d00594682c652adb66076a69d46c48" - integrity sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog== - dependencies: - he "^1.1.1" - mime "^1.6.0" - minimist "^1.1.0" - url-join "^2.0.5" - -eventemitter3@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" - integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== - -follow-redirects@^1.0.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.11.0.tgz#afa14f08ba12a52963140fe43212658897bc0ecb" - integrity sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA== - dependencies: - debug "^3.0.0" - -he@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -http-proxy@^1.18.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" - integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -http-server@^0.12.3: - version "0.12.3" - resolved "https://registry.yarnpkg.com/http-server/-/http-server-0.12.3.tgz#ba0471d0ecc425886616cb35c4faf279140a0d37" - integrity sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA== - dependencies: - basic-auth "^1.0.3" - colors "^1.4.0" - corser "^2.0.1" - ecstatic "^3.3.2" - http-proxy "^1.18.0" - minimist "^1.2.5" - opener "^1.5.1" - portfinder "^1.0.25" - secure-compare "3.0.1" - union "~0.5.0" - -lodash@^4.17.14: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -mime@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -minimist@^1.1.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== - -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== - -opener@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" - integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== - -portfinder@^1.0.25: - version "1.0.26" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.26.tgz#475658d56ca30bed72ac7f1378ed350bd1b64e70" - integrity sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ== - dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.1" - -qs@^6.4.0: - version "6.9.4" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" - integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - -secure-compare@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" - integrity sha1-8aAymzCLIh+uN7mXTz1XjQypmeM= - -union@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/union/-/union-0.5.0.tgz#b2c11be84f60538537b846edb9ba266ba0090075" - integrity sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA== - dependencies: - qs "^6.4.0" - -url-join@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728" - integrity sha1-WvIvGMBSoACkjXuCxenC4v7tpyg= diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 7602b64e..92eb4132 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -64,15 +64,9 @@ global: demoBanners: # This is the "advertisement" shown in the main menu and other various places - title: Hey! + title: Demo Version intro: >- - If you enjoy this game, please consider buying the full version! - advantages: - - Unlimited savegames - - Waypoints - - Dark mode & more - - >- - Allow me to further develop shapez.io ❤️ + Get the standalone to unlock all features! mainMenu: play: Play @@ -88,6 +82,29 @@ mainMenu: 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:
+
    +
  • Submit a screenshot of your base to contest@shapez.io
  • +
  • Bonus points if you share it on social media!
  • +
  • I will choose 5 screenshots and propose it to the discord community to vote.
  • +
  • The winner gets $25 (Paypal, Amazon Gift Card, whatever you prefer)
  • +
  • Deadline: 07.06.2020 12:00 AM CEST
  • +
+
+ 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 @@ -153,15 +170,11 @@ dialogs: leaveNotPossibleInDemo: title: Demo version - desc: Your game was saved but you will not be able to restore it in the demo. Restoring your savegames is only possible in the full version. Are you sure? + desc: Your game has been saved, but you will not be able to restore it in the demo. Restoring your savegames is only possible in the full version. Are you sure? newUpdate: title: Update available - desc: There is an update for this game available! - - demoExplanation: - title: Notice from the Developer - desc: I am developing this game in my free time, and I hope you enjoy it! If you do, please consider to buy the standalone version! + desc: There is an update for this game available, be sure to download it! oneSavegameLimit: title: Limited savegames @@ -206,6 +219,9 @@ dialogs: title: New Marker desc: Give it a meaningful name + markerDemoLimit: + desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers! + ingame: # This is shown in the top left corner and displays useful keybindings in # every situation @@ -220,6 +236,7 @@ ingame: toggleHud: Toggle HUD placeBuilding: Place building createMarker: Create Marker + delete: Destroy # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) @@ -313,12 +330,24 @@ ingame: 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. + # 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: @@ -574,7 +603,7 @@ settings: offerHints: title: Hints & Tutorials description: >- - Whether to offer hints and tutorials while playing. + 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. keybindings: title: Keybindings @@ -649,6 +678,5 @@ demo: importingGames: Importing savegames oneGameLimit: Limited to one savegame customizeKeybindings: Customizing Keybindings - creatingMarkers: Create custom markers settingNotAvailable: Not available in the demo. diff --git a/translations/base-fr.yaml b/translations/base-fr.yaml index 649e4187..89e9bc09 100644 --- a/translations/base-fr.yaml +++ b/translations/base-fr.yaml @@ -26,6 +26,13 @@ global: # How big numbers are rendered, e.g. "10,000" thousandsDivider: "." + # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. cf wikipedia système international d'unité + suffix: + thousands: k + millions: M + billions: T + trillions: E + # Shown for infinitely big numbers infinite: inf @@ -152,10 +159,6 @@ dialogs: title: Mise-à-jour disponible desc: Une mise-à-jour est disponible pour ce jeu! - demoExplanation: - title: Note du développeur - desc: Je développe ce jeu pendant mon temps libre, et j'espère que vous l'appréciez! Si c'est le cas, merci de considérez l'achat de la version complète! - oneSavegameLimit: title: Sauvegardes limitées desc: Vous ne pouvez avoir qu'une seule sauvegarde en même temps dans la version démo. Merci de soit effacer l'actuelle ou de vous procurer la version complète! @@ -213,6 +216,7 @@ ingame: toggleHud: Basculet l'ATH placeBuilding: Placer un bâtiment createMarker: Créer une balise + delete: Supprimer # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) @@ -278,7 +282,7 @@ ingame: description: Affiche tous les formes que votre usine entière produit, en incluant les formes intermédiaires. delivered: title: Délivré - description: Affiche les formes qui ont étés délivrées dans votres bâtiment central. + description: Affiche les formes qui ont été livrées dans votre noyau. noShapesProduced: Aucune forme n'a été produite jusqu'à présent. # Displays the shapes per minute, e.g. '523 / m' @@ -306,12 +310,21 @@ ingame: blueprintPlacer: cost: Coût + # Map markers waypoints: waypoints: Balise hub: Noyau description: Cliquez une balise pour vous y rendre, clic-droit pour l'effacer.

Appuyez sur pour créer une balise sur la vue actuelle, ou clic-droit pour en créer une sur l'endroit pointé. creationSuccessNotification: La bailse a été créée. + # Interactive tutorial + interactiveTutorial: + title: Tutoriel + hints: + 1_1_extractor: Placez un extracteur sur une forme en cercle pour l'extraire! + 1_2_conveyor: Connectez l'extracteur avec un convoyeur vers votre noyau! + 1_3_expand: Ceci n'est PAS un jeu incrémental et inactif! Construisez plus d'extracteurs et de convoyeurs pour atteindre plus votre votre but. + # All shop upgrades shopUpgrades: belt: @@ -440,7 +453,7 @@ storyRewards: reward_tunnel: title: Tunnel - desc: Le tunnel a été débloqué - Vous pouvez maintenant faire passer des formes vous les convoyeurs et les bâtimentts avec ça! + desc: Le tunnel a été débloqué - Vous pouvez maintenant faire passer des formes sous les convoyeurs et les bâtiments avec ça! reward_rotater_ccw: title: Pivoteur inversé @@ -448,7 +461,7 @@ storyRewards: reward_miner_chainable: title: Extracteur en série - desc: Vous avez débloqué l'extracteur en série! Il permet de transférer ses resources à d'autres extracteurs pour extraire les ressources plus efficacement! + desc: Vous avez débloqué l'extracteur en série! Il permet de transférer ses resources à d'autres extracteurs pour les extraire plus efficacement! reward_underground_belt_tier_2: title: Tunnel échelon II @@ -481,7 +494,7 @@ storyRewards: reward_blueprints: title: Patrons - desc: Vous pouvez maintenant copier et coller des perties de votre usines! Sélectionnez une zone (Appuyez sur CTRL, et sélectionnez avec votre souris), et appuyez sur 'C' pour la copier.

Coller n'est pas gratuit, vous devez produire des formes de patrons pour vous le payer! (Ceux qu'ont vient de vous donner). + desc: Vous pouvez maintenant copier et coller des parties de votre usines! Sélectionnez une zone (Appuyez sur CTRL, et sélectionnez avec votre souris), et appuyez sur 'C' pour la copier.

Coller n'est pas gratuit, vous devez produire des formes de patrons pour vous le payer! (Ceux que vous venez de livrer). # Special reward, which is shown when there is no reward actually no_reward: @@ -511,6 +524,23 @@ settings: title: Taille de l'interface description: >- Change la taille de l'interface utilisateur. Cette interface se redimensionnera suivant la résolution de votre appareil, mais cette option contrôle le facteur de résolution. + scales: + super_small: Très petite + small: Petite + regular: Normale + large: Largr + huge: Très large + + scrollWheelSensitivity: + title: Sensibilité du zoom + description: >- + Change la sensibilité du zoom (AUssi bien de la roulette de la souris que du trackpad). + sensitivity: + super_slow: Super lent + slow: Lent + regular: Normal + fast: Rapide + super_fast: Super rapide fullscreen: title: Plein écran @@ -557,6 +587,7 @@ keybindings: categoryLabels: general: Application ingame: Jeu + navigation: Navigation placement: Placement massSelect: Suppression de masse buildings: Raccourcis bâtiment @@ -569,9 +600,11 @@ keybindings: mapMoveRight: Aller à droite mapMoveDown: Aller en bas mapMoveLeft: Aller à gauche + centerMap: Centrer la carte mapZoomIn: Zoom avant mapZoomOut: Zoom arrière + createMarker: Créer une balise menuOpenShop: Améliorations menuOpenStats: Statistiques @@ -621,5 +654,5 @@ demo: settingNotAvailable: Indisponible dans la démo. # -# French translation version v0.2 based on english v1.1.1 by Didier WEERTS 'The Corsaire' +# French translation version v0.3 based on english v1.1.4 by Didier WEERTS 'The Corsaire' # \ No newline at end of file diff --git a/translations/base-pt-BR.yaml b/translations/base-pt-BR.yaml new file mode 100644 index 00000000..7284e6fa --- /dev/null +++ b/translations/base-pt-BR.yaml @@ -0,0 +1,673 @@ +# +# 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. +# + +global: + loading: Carregando + error: Erro + + # 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: um segundo atrás + xSecondsAgo: segundos atrás + oneMinuteAgo: um minuto atrás + xMinutesAgo: minutos atrás + oneHourAgo: uma hora atrás + xHoursAgo: horas atrás + oneDayAgo: um dia atrás + xDaysAgo: dias atrás + + # Short formats for times, e.g. '5h 23m' + secondsShort: s + minutesAndSecondsShort: m s + hoursAndMinutesShort: h s + + xMinutes: minutos + + keys: + tab: TAB + control: CTRL + alt: ALT + escape: ESC + shift: SHIFT + space: ESPAÇO + +demoBanners: + # This is the "advertisement" shown in the main menu and other various places + title: Versão Demo + intro: >- + Pegue a versão completa para desbloquear todas os recursos + +mainMenu: + play: Jogar + changelog: Changelog + importSavegame: Importar + openSourceHint: Esse jogo tem código aberto! + discordLink: Discord oficial + + # This is shown when using firefox and other browsers which are not supported. + browserWarning: >- + Desculpe, o jogo fica lento em seu navegador! Compre a versão completa ou baixe o Chrome para obter uma experiência completa. + + savegameLevel: Level + savegameLevelUnknown: Level desconhecido + + contests: + contest_01_03062020: + title: "Concurso #01" + desc: Ganhe $ 25 pela melhor base! + longDesc: >- + Para retribuir, pensei que seria legal fazer concursos semanais! +

+ Tópico dessa semana: Cronstua a base mais legal! +

+ Here's the deal:
+
    +
  • Envie uma captura de tela da sua base para contest@shapez.io
  • +
  • Pontos bônus se você o compartilhar nas mídias sociais!
  • +
  • Vou escolher 5 capturas de tela e propor à votação a comunidade discord.
  • +
  • o vencedor recebe $ 25 (Paypal, Amazon Gift Card, o que você preferir)
  • +
  • Até 07.06.2020 12:00 CEST
  • +
+
+ Estou ansioso para ver suas criações incríveis! + + showInfo: Participate + +dialogs: + buttons: + ok: OK + delete: Deletar + cancel: Cancelar + later: Voltar + restart: Reiniciar + reset: Reset + getStandalone: Obter versão completa + deleteGame: Eu sei o que eu faço + viewUpdate: Atualizações + showUpgrades: Mostrar atualizações + showKeybindings: Controles + + importSavegameError: + title: Erro importante + text: >- + Falha ao carregar seu savegame: + + importSavegameSuccess: + title: Sucesso + text: >- + Seu savegame foi importado. + + gameLoadFailure: + title: Jogo esta quebrado + text: >- + Falha ao carregar seu savegame + + confirmSavegameDelete: + title: Confirmar exclusão + text: >- + Tem certeza que quer excluir esse jogo? + + savegameDeletionError: + title: Falha para deletar + text: >- + Falha ao deletar seu savegame: + + restartRequired: + title: Reiniciar + text: >- + Voce precisa reiniciar o jogo para aplicar as mudanças. + + editKeybinding: + title: Change Keybinding + desc: Press the key you want to assign, or escape to cancel. + + resetKeybindingsConfirmation: + title: Resetar controles + desc: Essa opção deixa os controles no modo padrão. + + keybindingsResetOk: + title: Resetar controles + desc: Os controles foram resetados para o modo padrão. + + featureRestriction: + title: Versão Demo + desc: Você tentou acessar um recurso () que não está disponível na demo. Considere obter a versão completa para a proceder! + + saveNotPossibleInDemo: + desc: Seu jogo foi salvo, mas a restauração só é possível na versão completa. Considere obter a versão completa! + + leaveNotPossibleInDemo: + title: Demo version + desc: Seu jogo foi salvo, mas você não poderá restaurá-lo na demo. Restaurar seus savegames só é possível na versão completa. Você tem certeza? + + newUpdate: + title: Atualização disponível + desc: Uma atualização para esse jogo esta disponível! + + oneSavegameLimit: + title: Save limitado + desc: Você pode ter apenas um savegame por vez na versão demo. Remova o existente ou obtenha a versão completa! + + updateSummary: + title: Nova Atualização! + desc: >- + Aqui estão as alterações desde a última vez que você jogou: + + hintDescription: + title: Tutorial + desc: >- + Sempre que precisar de ajuda ou estiver parado, confira o botão 'Mostrar dica' no canto inferior esquerdo e darei o meu melhor para ajudá-lo! + + upgradesIntroduction: + title: Desbloquear updates + desc: >- + Todas as formas que você produz podem ser usadas para desbloquear atualizações - Não destrua suas antigas fábricas!! + O guia de atualizações pode ser encontrada no canto superior direito da tela. + + massDeleteConfirm: + title: Deletar + desc: >- + Voce esta deletando vários itens ( para ser exato)! Voce quer continuar? + + blueprintsNotUnlocked: + title: Não desbloqueado ainda + desc: >- + Os projetos ainda não foram desbloqueados! Complete mais níveis para desbloqueá-los. + + keybindingsIntroduction: + title: Teclas úteis + desc: >- + Este jogo possui muitas combinações de teclas que facilitam a construção de grandes fábricas + Aqui estão alguns mas certifique-se de verificar as combinações de teclas !

+ CTRL + Drag: Selecione a área para copiar / delete.
+ SHIFT: Mantenha pressionado para colocar vários.
+ ALT: Inverter as posições.
+ + createMarker: + title: Nova Marcação + desc: De um nome +ingame: + # This is shown in the top left corner and displays useful keybindings in + # every situation + keybindingsOverlay: + moveMap: Mover + removeBuildings: Deletar + stopPlacement: Parar + rotateBuilding: Rotação + placeMultiple: Colocar vários + reverseOrientation: reverso + disableAutoOrientation: desligar orientações + toggleHud: Base + placeBuilding: Colocar construção + createMarker: Criar marcador + delete: Destruir + + # 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: Aperte para variações. + + # Shows the hotkey in the ui, e.g. "Hotkey: Q" + hotkeyLabel: >- + Hotkey: + + infoTexts: + speed: velocidade + range: distancia + storage: Estoque + oneItemPerSecond: 1 item / segundo + itemsPerSecond: itens / 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: Completado + unlockText: Desbloqueado ! + buttonNextLevel: Próximo Level + + # Notifications on the lower right + notifications: + newUpgrade: Nova Atualização disponível! + gameSaved: Seu jogo foi Salvo. + + # Mass select information, this is when you hold CTRL and then drag with your mouse + # to select multiple buildings + massSelect: + infoText: Aperte para copiar, para excluir e para cancelar. + + # The "Upgrades" window + shop: + title: Atualizações + buttonUnlock: Atualizações + + # Gets replaced to e.g. "Tier IX" + tier: Nível + + # The roman number for each tier + tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] + + maximumLevel: Level Máximo + + # The "Statistics" window + statistics: + title: Estatísticas + dataSources: + stored: + title: Estoque + description: Exibindo a quantidade de formas armazenadas em seu edifício central. + produced: + title: Produção + description: Exibindo todas as formas que toda a sua fábrica produz, incluindo produtos intermediários.. + delivered: + title: Entregue + description: Exibindo formas entregues ao seu edifício central.. + noShapesProduced: Nenhuma forma foi produzida até o momento. + + # Displays the shapes per minute, e.g. '523 / m' + shapesPerMinute: / m + + # Settings menu, when you press "ESC" + settingsMenu: + playtime: Tempo de Jogo + + buildingsPlaced: Construções + beltsPlaced: Belts + + buttons: + continue: Continue + settings: Definições + menu: Voltar ao menu + + # Bottom left tutorial hints + tutorialHints: + title: Precisa de ajuda? + showHint: Mostrar dica + hideHint: Fechar + + # When placing a blueprint + blueprintPlacer: + cost: Custo + + # Map markers + waypoints: + waypoints: Marcadores + hub: HUB + description: Clique com o botão esquerdo do mouse em um marcador para pular, clique com o botão direito do mouse para excluí-lo.

Pressione para criar um marcador a partir da exibição atual ou clique com o botão direito do mouse para criar um marcador no local selecionado. + creationSuccessNotification: Marcador criado. + + # Interactive tutorial + interactiveTutorial: + title: Tutorial + hints: + 1_1_extractor: Coloque um extrator em cima de uma fonte de círculo para extraí-lo! + 1_2_conveyor: >- + Conecte o extrator com uma esteira transportadora até a sua base!

Dica, clique e arraste a esteira com o mouse! + + 1_3_expand: >- + Este NÃO é um jogo inativo! Construa mais extratores e esteiras para concluir o objetivo mais rapidamente.

Dica, segure SHIFT para colocar vários extratores e use R para girá-los. + +# All shop upgrades +shopUpgrades: + belt: + name: Esteiras, Distribuidores e Túneis + description: Velocidade +% + miner: + name: Extração + description: Velocidade +% + processors: + name: Cortar, Rotacionar e Empilhamento + description: Velocidade +% + painting: + name: Misturador e pintura + description: Velocidade +% + +# Buildings and their name / description +buildings: + belt: + default: + name: &belt Conveyor Belt + description: Transporta itens, mantenha pressionado e arraste para colocar vários. + + miner: # Internal name for the Extractor + default: + name: &miner Extractor + description: Coloque sobre uma forma ou cor para extraí-la. + + chainable: + name: Extractor (Chain) + description: Coloque sobre uma forma ou cor para extraí-la. Pode ser acorrentado. + + underground_belt: # Internal name for the Tunnel + default: + name: &underground_belt Tunnel + description: Permite transportar recursos sob construções. + + tier2: + name: Tunnel Tier II + description: Permite transportar recursos sob construções. + + splitter: # Internal name for the Balancer + default: + name: &splitter Balancer + description: Multifunctional - Distribui uniformemente todas as entradas em todas as saídas. + + compact: + name: Merger (compact) + description: Mescla duas correias transportadoras em uma. + + compact-inverse: + name: Merger (compact) + description: Mescla duas correias transportadoras em uma. + + cutter: + default: + name: &cutter Cutter + description: Corta as formas de cima para baixo e produz as duas metades. Se você usar apenas uma parte, não se esqueça de destruir a outra parte, ou ela irá parar a produção! + quad: + name: Cutter (Quad) + description: Corta as formas em quatro partes. Se você usar apenas uma parte, não se esqueça de destruir as outras, ou ela irá parar a produção! + + rotater: + default: + name: &rotater Rotate + description: Gira as formas no sentido horário em 90 graus. + ccw: + name: Rotate (CCW) + description: RGira as formas no sentido anti-horário em 90 graus. + + stacker: + default: + name: &stacker Stacker + description: Empilha os dois itens. Se eles não puderem ser mesclados, o item certo será colocado acima do item esquerdo. + + mixer: + default: + name: &mixer Color Mixer + description: Mistura duas cores usando mistura aditiva. + + painter: + default: + name: &painter Painter + description: Colore a forma inteira na entrada esquerda com a cor da entrada direita. + double: + name: Painter (Double) + description: Colore as duas formas na entrada esquerda com a cor da entrada direita. + quad: + name: Painter (Quad) + description: Permite colorir cada quadrante da forma com uma cor diferente. + + trash: + default: + name: &trash Lixo + description: Aceita qualquer item e os destrói. PARA SEMPRE + + storage: + name: Estoque + description: Armazena itens em excesso, até uma determinada capacidade. Pode ser usado como uma porta de transbordamento. + +storyRewards: + # Those are the rewards gained from completing the store + reward_cutter_and_trash: + title: Formas de corte + desc: Voce desbloqueou cutter - corte de formas pela metade de cima para baixo independentemente de sua orientação!

Certifique-se de se livrar do lixo, ou então ele irá parar a produção - Para esse propósito, eu lhe dei uma lixo, que destrói tudo o que você coloca nele + + reward_rotater: + title: Rodando + desc: O rotator foi desbloqueado! Gira as formas no sentido horário em 90 graus + + reward_painter: + title: Pintando + desc: >- + O pintor foi desbloqueado - Extraia algumas veias coloridas (como você faz com formas) e combine-as com uma forma no pintor para colori-las!

PS: Se você é daltônico , Já estou trabalhando em uma solução! + + reward_mixer: + title: Mistura de cores + desc: O misturador foi desbloqueado - combine duas cores usando mistura aditiva com esta construção! + + reward_stacker: + title: Combinador + desc: Agora você pode combinar formas com o combinador ! Ambas as entradas são combinadas e, se puderem ser colocadas próximas uma da outra, serão fundidas . Caso contrário, a entrada direita é empilhada em cima da entrada esquerda! + + reward_splitter: + title: Divisor/fusão + desc: O balanceador multifuncional foi desbloqueado - ele pode ser usado para construir fábricas maiores dividindo e mesclando itens !

+ + reward_tunnel: + title: Túnel + desc: O túnel foi desbloqueado - Agora você pode canalizar itens através de construções! + + reward_rotater_ccw: + title: CCW Rotação + desc: Você desbloqueou uma variante do rotater - permite girar no sentido anti-horário! Para construí-lo, selecione o rotador e pressione 'T' para alternar suas variantes ! + + reward_miner_chainable: + title: Extrator de encadeamento + desc: Você desbloqueou o extrator de correntes ! Ele pode encaminhar seus recursos para outros extratores, para que você possa extrair recursos com mais eficiência! + + reward_underground_belt_tier_2: + title: Túnel Tier II + desc: Você desbloqueou uma nova variante do túnel - ele tem um maior alcance , e também pode misturar e combinar esses túneis agora! + + reward_splitter_compact: + title: Balanceador compacto + desc: >- + Você desbloqueou uma variante compacta do balanceador - ele aceita duas entradas e as mescla em uma! + + reward_cutter_quad: + title: Cortador quádruplo + desc: Você desbloqueou uma variante do cortador - permite cortar formas em quatro partes em vez de apenas duas! + + reward_painter_double: + title: Pintura dupla + desc: Você desbloqueou uma variante do pintor - funciona como o pintor regular, mas processa duas formas ao mesmo tempo , consumindo apenas uma cor em vez de duas! + + reward_painter_quad: + title: Pintura quádupla + desc: Você desbloqueou uma variante do pintor - permite pintar cada parte da forma individualmente! + + reward_storage: + title: buffer de armazenamento + desc: Você desbloqueou uma variante do lixo - Permite armazenar itens até uma determinada capacidade! + + reward_freeplay: + title: Modo Livre + desc: Você fez isso! Você desbloqueou o modo de jogo livre ! Isso significa que as formas agora são geradas aleatoriamente! (Não se preocupe, mais conteúdo está planejado para o jogo completo!) + + reward_blueprints: + title: Projetos + desc: Agora você pode copiar e colar partes de sua fábrica! Selecione uma área (mantenha pressionada a tecla CTRL e arraste com o mouse) e pressione 'C' para copiá-la.

Colar não é de graça , é necessário produzir projetos e formas para pagar! (Aqueles que você acabou de entregar). + + # Special reward, which is shown when there is no reward actually + no_reward: + title: Próximo level + desc: >- + Este nível não lhe deu nenhuma recompensa, mas em breve?!

PS: Melhor não destruir sua fábrica existente - Você precisa de todas todas essas formas posteriormente mais tarde para desbloquear atualizações ! + + no_reward_freeplay: + title: Next level + desc: >- + Parabens, não se esqueça existe muita coisa planejada para essa versão. + +settings: + title: opções + categories: + game: Jogo + app: Aplicação + + versionBadges: + dev: Desenvolvedor + staging: Staging + prod: Produção + buildDate: Built + + labels: + uiScale: + title: Fonte + description: >- + Altera o tamanho da fonte do usuário. A interface ainda será dimensionada com base na resolução do dispositivo, mas essa configuração controla a quantidade de escala. + scales: + super_small: Super pequeno + small: Pequeno + regular: Normal + large: Grande + huge: Gigante + + scrollWheelSensitivity: + title: Sensibilidade do zoom + description: >- + Altera a sensibilidade do zoom (scrol do mouse ou touchpad). + sensitivity: + super_slow: Super lento + slow: Lento + regular: Normal + fast: Rápido + super_fast: Super Rápido + + fullscreen: + title: Tecla Cheia + description: >- + É recomendável jogar o jogo em tela cheia para obter a melhor experiência. Disponível apenas na versão completa. + + soundsMuted: + title: Som + description: >- + Se ligado o jogo fica Mudo. + + musicMuted: + title: Musica + description: >- + Se ligado a musica é desligada. + + theme: + title: Tema + description: >- + Escolha o tema entre (Branco / Preto). + + refreshRate: + title: Frequencia + description: >- + Se você possui um monitor de 144 hz, altere a taxa de atualização aqui para que o jogo seja simulado corretamente com taxas de atualização mais altas. Isso pode realmente diminuir o FPS se o computador estiver muito lento. + + alwaysMultiplace: + title: Multiplicidade + description: >- + Se ativado, todos os edifícios permanecerão selecionados após o posicionamento até que você o cancele. Isso é equivalente a manter SHIFT permanentemente. + + offerHints: + title: Dicas e tutoriais + description: >- + Se deve oferecer dicas e tutoriais enquanto estiver jogando.v. + +keybindings: + title: Comandos + hint: >- + Tip: Certifique-se de usar CTRL, SHIFT e ALT! Eles permitem diferentes opções de veiculação. + + resetKeybindings: Reset Keyinbindings + + categoryLabels: + general: Geral + ingame: Jogo + navigation: Navegação + placement: Posicionamento + massSelect: Seleção + buildings: Construções + placementModifiers: Modificações + + mappings: + confirm: Confirmar + back: Voltar + mapMoveUp: Mover para cima + mapMoveRight: Mover para direita + mapMoveDown: Mover para baixo + mapMoveLeft: Mover para a esquerda + centerMap: Centralizar + + mapZoomIn: Aproximar + mapZoomOut: Distanciar + createMarker: Criar marcação + + menuOpenShop: Atualizações + menuOpenStats: Estatísticas + + toggleHud: Ocultar Menu + toggleFPSInfo: Mostar FPS + belt: *belt + splitter: *splitter + underground_belt: *underground_belt + miner: *miner + cutter: *cutter + rotater: *rotater + stacker: *stacker + mixer: *mixer + painter: *painter + trash: *trash + + abortBuildingPlacement: Cancelar + rotateWhilePlacing: Rotacionar + rotateInverseModifier: >- + Modifier: Rotação instantanea + cycleBuildingVariants: Variações + confirmMassDelete: Confirmar exclusão em massa + cycleBuildings: Trocar de construção + + massSelectStart: Segure e arraste para começar + massSelectSelectMultiple: Selecionar área + massSelectCopy: Copiar área + + placementDisableAutoOrientation: Desligar orientações automaticas + placeMultiple: Permanecer no modo de produção + placeInverse: Inverter orientação de esteira + +about: + title: Sobre o jogo + +changelog: + title: Changelog + +demo: + features: + restoringGames: Restaurando jogos salvos + importingGames: Carregando jogos salvos + oneGameLimit: Limitado para um savegamne + customizeKeybindings: Modificando Teclas + creatingMarkers: Criando marcações + + settingNotAvailable: Não disponível na versão demo. diff --git a/version b/version index 9c1218c2..db152789 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.1.3 \ No newline at end of file +1.1.8 \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 90c6e840..f29348b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5604,6 +5604,11 @@ logalot@^2.0.0, logalot@^2.1.0: figures "^1.3.5" squeak "^1.0.0" +logrocket@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/logrocket/-/logrocket-1.0.7.tgz#fe17dbdfc861481cd274fbda533d552de7800a3a" + integrity sha512-v6HWEQIsyG+3FkldB7vIAgHh7/qpsiz2Br4bLK5SHBvjqRrHs/Fp+Jr8oiA2GYq0UurAtCu51U8SWft5+OCKtg== + longest@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"