diff --git a/package-lock.json b/package-lock.json index b87803fbf..0b4c88012 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jeremyckahn/farmhand", - "version": "1.17.6", + "version": "1.18.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@jeremyckahn/farmhand", - "version": "1.17.6", + "version": "1.18.0", "license": "GPL-2.0-or-later", "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.27", @@ -2658,246 +2658,6 @@ "version": "0.8.0", "license": "MIT" }, - "node_modules/@esbuild/android-arm": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz", - "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz", - "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz", - "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz", - "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz", - "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz", - "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz", - "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz", - "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz", - "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz", - "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz", - "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz", - "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz", - "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz", - "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz", - "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.16.17", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz", @@ -2914,102 +2674,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz", - "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz", - "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz", - "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz", - "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz", - "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz", - "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "dev": true, @@ -6909,17 +6573,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*", - "xmlbuilder": ">=11.0.1" - } - }, "node_modules/@types/prettier": { "version": "2.7.2", "dev": true, @@ -7068,13 +6721,6 @@ "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", "dev": true }, - "node_modules/@types/verror": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", - "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", - "dev": true, - "optional": true - }, "node_modules/@types/ws": { "version": "8.5.4", "dev": true, @@ -10430,23 +10076,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "optional": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/clipboardy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", @@ -11037,41 +10666,6 @@ "node": ">=10" } }, - "node_modules/crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "optional": true, - "dependencies": { - "buffer": "^5.1.0" - } - }, - "node_modules/crc/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -12133,32 +11727,6 @@ "node": ">= 10.0.0" } }, - "node_modules/dmg-license": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "@types/plist": "^3.0.1", - "@types/verror": "^1.10.3", - "ajv": "^6.10.0", - "crc": "^3.8.0", - "iconv-corefoundation": "^1.1.7", - "plist": "^3.0.4", - "smart-buffer": "^4.0.2", - "verror": "^1.10.0" - }, - "bin": { - "dmg-license": "bin/dmg-license.js" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dns-equal": { "version": "1.0.0", "dev": true, @@ -12386,9 +11954,9 @@ } }, "node_modules/electron": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.1.0.tgz", - "integrity": "sha512-wz5s4N6V7zyKm4YQmXJImFoxO1Doai32ShYm0FzOLPBMwLMdQBV+REY+j1opRx0KJ9xJEIdjYgcA8OSw6vx3pA==", + "version": "22.3.21", + "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.21.tgz", + "integrity": "sha512-9JzWgvehRrqA30M7RvWCwwbyq2EgPUSbCFaqyZGnG0B52m4ayB8H+uFNIKXyWuyFwAEPDpQW5cGwCzXir1HuLA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -13142,118 +12710,6 @@ "esbuild-windows-arm64": "0.14.47" } }, - "node_modules/esbuild-android-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz", - "integrity": "sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-android-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz", - "integrity": "sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz", - "integrity": "sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz", - "integrity": "sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz", - "integrity": "sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz", - "integrity": "sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-32": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz", - "integrity": "sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/esbuild-linux-64": { "version": "0.14.47", "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz", @@ -13270,198 +12726,6 @@ "node": ">=12" } }, - "node_modules/esbuild-linux-arm": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz", - "integrity": "sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz", - "integrity": "sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz", - "integrity": "sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz", - "integrity": "sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz", - "integrity": "sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-s390x": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz", - "integrity": "sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-netbsd-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz", - "integrity": "sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-openbsd-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz", - "integrity": "sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-sunos-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz", - "integrity": "sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-32": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz", - "integrity": "sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz", - "integrity": "sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz", - "integrity": "sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/escalade": { "version": "3.1.1", "license": "MIT", @@ -15366,19 +14630,6 @@ "node": ">=0.4" } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/fun-animal-names": { "version": "0.1.1", "license": "MIT", @@ -16646,23 +15897,6 @@ "version": "1.0.4", "license": "BSD-3-Clause" }, - "node_modules/iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" - }, - "engines": { - "node": "^8.11.2 || >=10" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "license": "MIT", @@ -23691,13 +22925,6 @@ "version": "1.2.14", "license": "MIT" }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "dev": true, - "optional": true - }, "node_modules/node-environment-flags": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", @@ -30190,21 +29417,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", "dev": true, @@ -33165,38 +32377,6 @@ "node": ">= 14" } }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/verror/node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "optional": true - }, "node_modules/vfile": { "version": "2.3.0", "license": "MIT", @@ -33821,9 +33001,10 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -35883,111 +35064,6 @@ "@emotion/hash": { "version": "0.8.0" }, - "@esbuild/android-arm": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz", - "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==", - "dev": true, - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz", - "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==", - "dev": true, - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz", - "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz", - "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz", - "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz", - "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz", - "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz", - "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz", - "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz", - "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz", - "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz", - "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz", - "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==", - "dev": true, - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz", - "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz", - "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==", - "dev": true, - "optional": true - }, "@esbuild/linux-x64": { "version": "0.16.17", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz", @@ -35995,48 +35071,6 @@ "dev": true, "optional": true }, - "@esbuild/netbsd-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz", - "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==", - "dev": true, - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz", - "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==", - "dev": true, - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz", - "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==", - "dev": true, - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz", - "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==", - "dev": true, - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz", - "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==", - "dev": true, - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.16.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz", - "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==", - "dev": true, - "optional": true - }, "@eslint/eslintrc": { "version": "0.4.3", "dev": true, @@ -38850,17 +37884,6 @@ "version": "4.0.0", "dev": true }, - "@types/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", - "dev": true, - "optional": true, - "requires": { - "@types/node": "*", - "xmlbuilder": ">=11.0.1" - } - }, "@types/prettier": { "version": "2.7.2", "dev": true @@ -38995,13 +38018,6 @@ "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", "dev": true }, - "@types/verror": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", - "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", - "dev": true, - "optional": true - }, "@types/ws": { "version": "8.5.4", "dev": true, @@ -41286,17 +40302,6 @@ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", "dev": true }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "optional": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, "clipboardy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", @@ -41703,29 +40708,6 @@ "yaml": "^1.10.0" } }, - "crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "optional": true, - "requires": { - "buffer": "^5.1.0" - }, - "dependencies": { - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "optional": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - } - } - }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -42405,23 +41387,6 @@ } } }, - "dmg-license": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", - "dev": true, - "optional": true, - "requires": { - "@types/plist": "^3.0.1", - "@types/verror": "^1.10.3", - "ajv": "^6.10.0", - "crc": "^3.8.0", - "iconv-corefoundation": "^1.1.7", - "plist": "^3.0.4", - "smart-buffer": "^4.0.2", - "verror": "^1.10.0" - } - }, "dns-equal": { "version": "1.0.0", "dev": true @@ -42587,9 +41552,9 @@ } }, "electron": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.1.0.tgz", - "integrity": "sha512-wz5s4N6V7zyKm4YQmXJImFoxO1Doai32ShYm0FzOLPBMwLMdQBV+REY+j1opRx0KJ9xJEIdjYgcA8OSw6vx3pA==", + "version": "22.3.21", + "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.21.tgz", + "integrity": "sha512-9JzWgvehRrqA30M7RvWCwwbyq2EgPUSbCFaqyZGnG0B52m4ayB8H+uFNIKXyWuyFwAEPDpQW5cGwCzXir1HuLA==", "dev": true, "requires": { "@electron/get": "^2.0.0", @@ -43134,55 +42099,6 @@ "esbuild-windows-arm64": "0.14.47" } }, - "esbuild-android-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz", - "integrity": "sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==", - "dev": true, - "optional": true - }, - "esbuild-android-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz", - "integrity": "sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==", - "dev": true, - "optional": true - }, - "esbuild-darwin-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz", - "integrity": "sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==", - "dev": true, - "optional": true - }, - "esbuild-darwin-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz", - "integrity": "sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz", - "integrity": "sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz", - "integrity": "sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-32": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz", - "integrity": "sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==", - "dev": true, - "optional": true - }, "esbuild-linux-64": { "version": "0.14.47", "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz", @@ -43190,90 +42106,6 @@ "dev": true, "optional": true }, - "esbuild-linux-arm": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz", - "integrity": "sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz", - "integrity": "sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==", - "dev": true, - "optional": true - }, - "esbuild-linux-mips64le": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz", - "integrity": "sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==", - "dev": true, - "optional": true - }, - "esbuild-linux-ppc64le": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz", - "integrity": "sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==", - "dev": true, - "optional": true - }, - "esbuild-linux-riscv64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz", - "integrity": "sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==", - "dev": true, - "optional": true - }, - "esbuild-linux-s390x": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz", - "integrity": "sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==", - "dev": true, - "optional": true - }, - "esbuild-netbsd-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz", - "integrity": "sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==", - "dev": true, - "optional": true - }, - "esbuild-openbsd-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz", - "integrity": "sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==", - "dev": true, - "optional": true - }, - "esbuild-sunos-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz", - "integrity": "sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==", - "dev": true, - "optional": true - }, - "esbuild-windows-32": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz", - "integrity": "sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==", - "dev": true, - "optional": true - }, - "esbuild-windows-64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz", - "integrity": "sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==", - "dev": true, - "optional": true - }, - "esbuild-windows-arm64": { - "version": "0.14.47", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz", - "integrity": "sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==", - "dev": true, - "optional": true - }, "escalade": { "version": "3.1.1" }, @@ -44557,12 +43389,6 @@ "fs.realpath": { "version": "1.0.0" }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, "fun-animal-names": { "version": "0.1.1" }, @@ -45418,17 +44244,6 @@ "hyphenate-style-name": { "version": "1.0.4" }, - "iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", - "dev": true, - "optional": true, - "requires": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" - } - }, "iconv-lite": { "version": "0.6.3", "requires": { @@ -50509,13 +49324,6 @@ "noble-secp256k1": { "version": "1.2.14" }, - "node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "dev": true, - "optional": true - }, "node-environment-flags": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", @@ -54638,18 +53446,6 @@ "version": "3.0.0", "dev": true }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, "smart-buffer": { "version": "4.2.0", "dev": true @@ -56689,34 +55485,6 @@ "@vercel/static-build": "1.3.0" } }, - "verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "optional": true - } - } - }, "vfile": { "version": "2.3.0", "requires": { @@ -57127,7 +55895,9 @@ } }, "word-wrap": { - "version": "1.2.3", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "workbox-background-sync": { diff --git a/package.json b/package.json index cd65dd4da..3945b804f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jeremyckahn/farmhand", - "version": "1.17.6", + "version": "1.18.0", "publishConfig": { "access": "public" }, diff --git a/src/components/Farmhand/Farmhand.js b/src/components/Farmhand/Farmhand.js index aec94fdf5..267e99a37 100644 --- a/src/components/Farmhand/Farmhand.js +++ b/src/components/Farmhand/Farmhand.js @@ -56,17 +56,16 @@ import NotificationSystem, { } from '../NotificationSystem' import DebugMenu from '../DebugMenu' import theme from '../../mui-theme' +import { levelAchieved } from '../../utils/levelAchieved' import { computeMarketPositions, createNewField, doesMenuObstructStage, - farmProductsSold, generateCow, getAvailableShopInventory, getItemCurrentValue, getPeerMetadata, inventorySpaceRemaining, - levelAchieved, moneyTotal, nullArray, reduceByPersistedKeys, @@ -208,14 +207,15 @@ const applyPriceEvents = (valueAdjustments, priceCrashes, priceSurges) => { * totals of crops harvested. Keys are crop type IDs, values are the number of * that crop harvested. * @property {number} dayCount + * @property {number} experience * @property {string} farmName - * @property {boolean} hasBooted * @property {(?farmhand.plotContent)[][]} field * @property {farmhand.fieldMode} fieldMode * @property {Function?} getCowAccept https://github.com/dmotz/trystero#receiver * @property {Function?} getCowReject https://github.com/dmotz/trystero#receiver * @property {Function?} getCowTradeRequest https://github.com/dmotz/trystero#receiver * @property {Function?} getPeerMetadata https://github.com/dmotz/trystero#receiver + * @property {boolean} hasBooted * @property {number?} heartbeatTimeoutId * @property {Array.} historicalDailyLosses * @property {Array.} historicalDailyRevenue @@ -380,9 +380,7 @@ export default class Farmhand extends FarmhandReducers { } get levelEntitlements() { - return getLevelEntitlements( - levelAchieved(farmProductsSold(this.state.itemsSold)) - ) + return getLevelEntitlements(levelAchieved(this.state.experience)) } get shopInventory() { @@ -425,6 +423,7 @@ export default class Farmhand extends FarmhandReducers { cowTradeTimeoutId: -1, cropsHarvested: {}, dayCount: 0, + experience: 0, farmName: 'Unnamed', field: createNewField(), fieldMode: OBSERVE, @@ -502,6 +501,14 @@ export default class Farmhand extends FarmhandReducers { } } + async initializeNewGame() { + await this.incrementDay(true) + this.setState(() => ({ + historicalValueAdjustments: [], + })) + this.showNotification(LOAN_INCREASED`${STANDARD_LOAN_AMOUNT}`, 'info') + } + initInputHandlers() { const debouncedInputRate = 50 @@ -601,10 +608,7 @@ export default class Farmhand extends FarmhandReducers { }) }) } else { - // Initialize new game - await this.incrementDay(true) - this.setState(() => ({ historicalValueAdjustments: [] })) - this.showNotification(LOAN_INCREASED`${STANDARD_LOAN_AMOUNT}`, 'info') + await this.initializeNewGame() } this.syncToRoom().catch(errorCode => this.handleRoomSyncError(errorCode)) diff --git a/src/components/Field/Field.js b/src/components/Field/Field.js index f4c8dca7c..8d8c1bc3a 100644 --- a/src/components/Field/Field.js +++ b/src/components/Field/Field.js @@ -18,12 +18,8 @@ import Plot from '../Plot' import QuickSelect from '../QuickSelect' import { fieldMode } from '../../enums' import tools from '../../data/tools' -import { - doesInventorySpaceRemain, - farmProductsSold, - levelAchieved, - nullArray, -} from '../../utils' +import { levelAchieved } from '../../utils/levelAchieved' +import { doesInventorySpaceRemain, nullArray } from '../../utils' import { getLevelEntitlements } from '../../utils/getLevelEntitlements' import './Field.sass' @@ -56,10 +52,10 @@ if (tools.shovel) { } export const isInHoverRange = ({ + experience, fieldMode, hoveredPlotRangeSize, hoveredPlot: { x: hoveredPlotX, y: hoveredPlotY }, - itemsSold, x, y, }) => { @@ -74,7 +70,7 @@ export const isInHoverRange = ({ switch (fieldMode) { case SET_SPRINKLER: hoveredPlotRangeSizeToRender = getLevelEntitlements( - levelAchieved(farmProductsSold(itemsSold)) + levelAchieved(experience) ).sprinklerRange break @@ -131,10 +127,10 @@ export const MemoPlot = memo( ) MemoPlot.propTypes = { + experience: number.isRequired, fieldMode: string.isRequired, hoveredPlot: object.isRequired, hoveredPlotRangeSize: number.isRequired, - itemsSold: object.isRequired, plotContent: object, setHoveredPlot: func.isRequired, x: number.isRequired, @@ -214,13 +210,13 @@ FieldContentWrapper.propTypes = { export const FieldContent = ({ columns, + experience, field, fieldMode, handleCombineEnabledChange, hoveredPlot, hoveredPlotRangeSize, isCombineEnabled, - itemsSold, purchasedCombine, rows, setHoveredPlot, @@ -239,10 +235,10 @@ export const FieldContent = ({ { Field.propTypes = { columns: number.isRequired, + experience: number.isRequired, field: array.isRequired, fieldMode: string.isRequired, handleCombineEnabledChange: func.isRequired, @@ -433,7 +430,6 @@ Field.propTypes = { inventory: array.isRequired, inventoryLimit: number.isRequired, isCombineEnabled: bool.isRequired, - itemsSold: object.isRequired, purchasedCombine: number.isRequired, purchasedField: number.isRequired, rows: number.isRequired, diff --git a/src/components/Field/Field.test.js b/src/components/Field/Field.test.js index fdfd1ce8f..0297fd436 100644 --- a/src/components/Field/Field.test.js +++ b/src/components/Field/Field.test.js @@ -22,10 +22,10 @@ beforeEach(() => { { { hoveredPlotRangeSize: 0, fieldMode: fieldMode.OBSERVE, isCombineEnabled: false, - itemsSold: {}, purchasedCombine: 0, rows: 0, setHoveredPlot: noop, @@ -162,9 +162,9 @@ describe('isInHoverRange', () => { test('indicates when plot is not in hover range', () => { expect( isInHoverRange({ + experience: 0, hoveredPlotRangeSize: 2, hoveredPlot: { x: 1, y: 1 }, - itemsSold: {}, x: 4, y: 4, }) @@ -174,9 +174,9 @@ describe('isInHoverRange', () => { test('indicates when plot is in hover range', () => { expect( isInHoverRange({ + experience: 0, hoveredPlotRangeSize: 2, hoveredPlot: { x: 1, y: 1 }, - itemsSold: {}, x: 0, y: 0, }) @@ -187,10 +187,10 @@ describe('isInHoverRange', () => { test('indicates when plot is not in hover range', () => { expect( isInHoverRange({ + experience: 0, hoveredPlotRangeSize: 2, hoveredPlot: { x: 1, y: 1 }, fieldMode: fieldMode.OBSERVE, - itemsSold: {}, x: 4, y: 4, }) @@ -200,10 +200,10 @@ describe('isInHoverRange', () => { test('indicates when plot is in hover range', () => { expect( isInHoverRange({ + experience: 0, hoveredPlotRangeSize: 2, hoveredPlot: { x: 1, y: 1 }, fieldMode: fieldMode.OBSERVE, - itemsSold: {}, x: 0, y: 0, }) @@ -215,10 +215,10 @@ describe('isInHoverRange', () => { test('indicates when plot is not in hover range', () => { expect( isInHoverRange({ + experience: 0, hoveredPlotRangeSize: 2, hoveredPlot: { x: 1, y: 1 }, fieldMode: fieldMode.SET_SPRINKLER, - itemsSold: {}, x: 4, y: 4, }) @@ -228,10 +228,10 @@ describe('isInHoverRange', () => { test('indicates when plot is in hover range', () => { expect( isInHoverRange({ + experience: 0, hoveredPlotRangeSize: 2, hoveredPlot: { x: 1, y: 1 }, fieldMode: fieldMode.SET_SPRINKLER, - itemsSold: {}, x: 0, y: 0, }) @@ -243,10 +243,10 @@ describe('isInHoverRange', () => { test('indicates that all plots are in hover range', () => { expect( isInHoverRange({ + experience: 0, hoveredPlotRangeSize: 2, hoveredPlot: { x: 1, y: 1 }, fieldMode: fieldMode.SET_SCARECROW, - itemsSold: {}, x: 2, y: 2, }) @@ -254,10 +254,10 @@ describe('isInHoverRange', () => { expect( isInHoverRange({ + experience: 0, hoveredPlotRangeSize: 2, hoveredPlot: { x: 1, y: 1 }, fieldMode: fieldMode.SET_SCARECROW, - itemsSold: {}, x: 100, y: 100, }) diff --git a/src/components/Navigation/DayAndProgressContainer.js b/src/components/Navigation/DayAndProgressContainer.js new file mode 100644 index 000000000..bc4b2c3f7 --- /dev/null +++ b/src/components/Navigation/DayAndProgressContainer.js @@ -0,0 +1,74 @@ +import React from 'react' +import { number, object } from 'prop-types' + +import Box from '@material-ui/core/Box' +import CircularProgress from '@material-ui/core/CircularProgress' +import Tooltip from '@material-ui/core/Tooltip' + +import FarmhandContext from '../Farmhand/Farmhand.context' + +import { levelAchieved } from '../../utils/levelAchieved' +import { + experienceNeededForLevel, + integerString, + scaleNumber, +} from '../../utils' + +export function DayAndProgressContainer({ dayCount, experience, itemsSold }) { + const currentLevel = levelAchieved(experience) + + const levelPercent = scaleNumber( + experience, + experienceNeededForLevel(currentLevel), + experienceNeededForLevel(currentLevel + 1), + 0, + 100 + ) + + const tooltipText = `${integerString( + experienceNeededForLevel(currentLevel + 1) - experience + )} more experience points needed to reach level ${integerString( + currentLevel + 1 + )}` + + return ( +

+ Day {integerString(dayCount)}, level: + + + + + {integerString(currentLevel)} + + + +

+ ) +} + +DayAndProgressContainer.propTypes = { + dayCount: number.isRequired, + experience: number.isRequired, + itemsSold: object.isRequired, +} + +export default function Consumer(props) { + return ( + + {({ gameState, handlers }) => ( + + )} + + ) +} diff --git a/src/components/Navigation/Navigation.js b/src/components/Navigation/Navigation.js index a08f63940..f5c06cb8e 100644 --- a/src/components/Navigation/Navigation.js +++ b/src/components/Navigation/Navigation.js @@ -5,9 +5,7 @@ import AccountBalanceIcon from '@material-ui/icons/AccountBalance' import AssessmentIcon from '@material-ui/icons/Assessment' import BeenhereIcon from '@material-ui/icons/Beenhere.js' import BookIcon from '@material-ui/icons/Book' -import Box from '@material-ui/core/Box' import Button from '@material-ui/core/Button' -import CircularProgress from '@material-ui/core/CircularProgress' import Dialog from '@material-ui/core/Dialog' import DialogActions from '@material-ui/core/DialogActions' import DialogContent from '@material-ui/core/DialogContent' @@ -24,17 +22,13 @@ import Switch from '@material-ui/core/Switch' import TextField from '@material-ui/core/TextField' import Tooltip from '@material-ui/core/Tooltip' import Typography from '@material-ui/core/Typography' -import { array, bool, func, number, object, string } from 'prop-types' +import { array, bool, func, number, string } from 'prop-types' import FarmhandContext from '../Farmhand/Farmhand.context' import { doesInventorySpaceRemain, - farmProductSalesVolumeNeededForLevel, - farmProductsSold, integerString, inventorySpaceConsumed, - levelAchieved, - scaleNumber, } from '../../utils' import { dialogView } from '../../enums' import { INFINITE_STORAGE_LIMIT, STAGE_TITLE_MAP } from '../../constants' @@ -49,6 +43,8 @@ import SettingsView from '../SettingsView' import StatsView from '../StatsView' import KeybindingsView from '../KeybindingsView' +import DayAndProgressContainer from './DayAndProgressContainer' + import './Navigation.sass' const FarmNameDisplay = ({ farmName, handleFarmNameUpdate }) => { @@ -205,7 +201,6 @@ export const Navigation = ({ activePlayers, blockInput, currentDialogView, - dayCount, farmName, handleActivePlayerButtonClick, handleClickDialogViewButton, @@ -217,22 +212,12 @@ export const Navigation = ({ handleViewChange, inventory, inventoryLimit, - itemsSold, isDialogViewOpen, isOnline, room, stageFocus, viewList, - totalFarmProductsSold = farmProductsSold(itemsSold), - currentLevel = levelAchieved(totalFarmProductsSold), - levelPercent = scaleNumber( - totalFarmProductsSold, - farmProductSalesVolumeNeededForLevel(currentLevel), - farmProductSalesVolumeNeededForLevel(currentLevel + 1), - 0, - 100 - ), currentDialogViewLowerCase = currentDialogView.toLowerCase(), modalTitleId = `${currentDialogViewLowerCase}-modal-title`, modalContentId = `${currentDialogViewLowerCase}-modal-content`, @@ -241,31 +226,7 @@ export const Navigation = ({

Farmhand

v{process.env.REACT_APP_VERSION}

-

- Day {integerString(dayCount)}, level: - - - - - {integerString(currentLevel)} - - - -

+ { return (
  • @@ -28,10 +24,8 @@ const OnlinePeer = ({ subheader: (

    Day: {integerString(dayCount)}

    -

    - Level:{' '} - {integerString(levelAchieved(farmProductsSold(itemsSold)))} -

    + {/* TODO: Remove `?? 0` after 10/24 */} +

    Level: {integerString(levelAchieved(experience ?? 0))}

    Money: {moneyString(money)}

    ), @@ -53,8 +47,8 @@ OnlinePeer.propTypes = { peer: shape({ cowOfferedForTrade: object, dayCount: number.isRequired, + experience: number.isRequired, id: string.isRequired, - itemsSold: object.isRequired, money: number.isRequired, }).isRequired, } diff --git a/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.test.js b/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.test.js index 00299483e..978ed5558 100644 --- a/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.test.js +++ b/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.test.js @@ -12,7 +12,7 @@ beforeEach(() => { peer: { dayCount: 0, id: '', - itemsSold: {}, + experience: 1, money: 0, }, }} diff --git a/src/components/OnlinePeersView/OnlinePeersView.js b/src/components/OnlinePeersView/OnlinePeersView.js index 36afcc03a..7469a1c3f 100644 --- a/src/components/OnlinePeersView/OnlinePeersView.js +++ b/src/components/OnlinePeersView/OnlinePeersView.js @@ -9,7 +9,8 @@ import { array, number, object, string } from 'prop-types' import BailOutErrorBoundary from '../BailOutErrorBoundary' -import { getPlayerName, farmProductsSold, levelAchieved } from '../../utils' +import { levelAchieved } from '../../utils/levelAchieved' +import { getPlayerName } from '../../utils' import FarmhandContext from '../Farmhand/Farmhand.context' import CowCard from '../CowCard' @@ -58,7 +59,7 @@ const OnlinePeersView = ({ {sortBy(populatedPeers, [ peerId => // Use negative value to reverse sort order - -levelAchieved(farmProductsSold(peers[peerId].itemsSold || 0)), + -levelAchieved(peers[peerId].experience || 0), ]).map(peerId => ( diff --git a/src/components/Recipe/Recipe.js b/src/components/Recipe/Recipe.js index b68f59c19..dda0bfd76 100644 --- a/src/components/Recipe/Recipe.js +++ b/src/components/Recipe/Recipe.js @@ -8,13 +8,13 @@ import CardActions from '@material-ui/core/CardActions' import Typography from '@material-ui/core/Typography' import { array, func, number, object } from 'prop-types' +import { totalIngredientsInRecipe } from '../../utils/totalIngredientsInRecipe' import { canMakeRecipe, doesInventorySpaceRemain, dollarString, maxYieldOfRecipe, integerString, - totalIngredientsInRecipe, } from '../../utils' import { itemsMap } from '../../data/maps' import { craftedItems } from '../../img' diff --git a/src/components/StatsView/StatsView.js b/src/components/StatsView/StatsView.js index b330bf346..1e5c5a1eb 100644 --- a/src/components/StatsView/StatsView.js +++ b/src/components/StatsView/StatsView.js @@ -13,14 +13,14 @@ import sortBy from 'lodash.sortby' import { itemsMap } from '../../data/maps' import FarmhandContext from '../Farmhand/Farmhand.context' import { moneyString } from '../../utils/moneyString' +import { farmProductsSold } from '../../utils/farmProductsSold' +import { levelAchieved } from '../../utils/levelAchieved' import { - farmProductSalesVolumeNeededForLevel, - farmProductsSold, + experienceNeededForLevel, get7DayAverage, getProfit, getProfitRecord, integerString, - levelAchieved, moneyTotal, } from '../../utils' import { @@ -37,6 +37,7 @@ const ElevatedPaper = props => ( const StatsView = ({ cowsTraded, + experience, farmName, historicalDailyLosses, historicalDailyRevenue, @@ -51,7 +52,7 @@ const StatsView = ({ todaysRevenue, totalFarmProductsSold = farmProductsSold(itemsSold), - currentLevel = levelAchieved(totalFarmProductsSold), + currentLevel = levelAchieved(experience), }) => (
    @@ -94,12 +95,11 @@ const StatsView = ({ > - Sales Needed for Next Level + Experience Points Needed for Next Level {integerString( - farmProductSalesVolumeNeededForLevel(currentLevel + 1) - - totalFarmProductsSold + experienceNeededForLevel(currentLevel + 1) - experience )} diff --git a/src/components/UpgradePurchase/UpgradePurchase.js b/src/components/UpgradePurchase/UpgradePurchase.js index 0c2804350..b94587cef 100644 --- a/src/components/UpgradePurchase/UpgradePurchase.js +++ b/src/components/UpgradePurchase/UpgradePurchase.js @@ -8,11 +8,8 @@ import { array, func, number, object } from 'prop-types' import IngredientsList from '../IngredientsList' -import { - canMakeRecipe, - doesInventorySpaceRemain, - totalIngredientsInRecipe, -} from '../../utils' +import { totalIngredientsInRecipe } from '../../utils/totalIngredientsInRecipe' +import { canMakeRecipe, doesInventorySpaceRemain } from '../../utils' import { craftedItems } from '../../img' diff --git a/src/constants.js b/src/constants.js index 096db8f74..68609c143 100644 --- a/src/constants.js +++ b/src/constants.js @@ -143,6 +143,7 @@ export const PERSISTED_STATE_KEYS = [ 'cowsTraded', 'cropsHarvested', 'dayCount', + 'experience', 'farmName', 'field', 'historicalDailyLosses', @@ -190,8 +191,8 @@ export const PEER_METADATA_STATE_KEYS = [ 'cowsSold', 'cropsHarvested', 'dayCount', + 'experience', 'id', - 'itemsSold', 'money', 'pendingPeerMessages', 'version', @@ -255,3 +256,24 @@ export const WEEDS_SPAWN_CHANCE = 0.15 export const KEG_INTEREST_RATE = 0.02 export const KEG_SPOILAGE_RATE_MULTIPLIER = 0.001 + +// NOTE: not all of these are implemented yet, these are for all the currently +// planned experience rewards +export const EXPERIENCE_VALUES = { + COMPOSTER_ACQUIRED: 10, + CELLAR_ACQUIRED: 10, + CELLAR_EXPANDED: 5, + COW_BRED: 1, + COW_PEN_ACQUIRED: 10, + COW_PEN_EXPANDED: 5, + COW_TRADED: 1, + FIELD_EXPANDED: 5, + ITEM_FORGED: 3, + ITEM_SOLD: 1, + KEG_SOLD: 2, + LOAN_PAID_OFF: 25, + NEW_YEAR: 5, + RAINBOW_COW_BRED: 2, + RECIPE_CRAFTED: 2, + SMELTER_ACQUIRED: 10, +} diff --git a/src/data/achievements.js b/src/data/achievements.js index 1e4eb45a0..ac2a78c10 100644 --- a/src/data/achievements.js +++ b/src/data/achievements.js @@ -11,8 +11,13 @@ import { } from '../utils' import { memoize } from '../utils/memoize' import { findInField } from '../utils/findInField' +import { addExperience } from '../game-logic/reducers' import { cropLifeStage, standardCowColors } from '../enums' -import { COW_FEED_ITEM_ID, I_AM_RICH_BONUSES } from '../constants' +import { + COW_FEED_ITEM_ID, + EXPERIENCE_VALUES, + I_AM_RICH_BONUSES, +} from '../constants' import { itemsMap } from './maps' @@ -68,6 +73,15 @@ const achievements = [ reward: state => addMoney(state, reward), }))(), + ((reward = EXPERIENCE_VALUES.LOAN_PAID_OFF) => ({ + id: 'financial-freedom', + name: 'Financial Freedom', + description: 'Pay off your initial loan from the bank.', + rewardDescription: `${reward} experience points`, + condition: state => state.loanBalance === 0, + reward: state => addExperience(state, reward), + }))(), + ((goal = 10000) => ({ id: 'unlock-crop-price-guide', name: 'Prove Yourself as a Farmer', diff --git a/src/data/achievements.test.js b/src/data/achievements.test.js index d383602c8..a9064d744 100644 --- a/src/data/achievements.test.js +++ b/src/data/achievements.test.js @@ -118,3 +118,24 @@ describe('gold-digger', () => { expect(ingot).toEqual({ id: 'gold-ingot', quantity: 1 }) }) }) + +describe('financial-freedom', () => { + const achievement = achievementsMap['financial-freedom'] + let state + + beforeEach(() => { + state = { + loanBalance: 100, + } + }) + + test('is not achievemed when loan balance is greater than 0', () => { + expect(achievement.condition(state)).toEqual(false) + }) + + test('is achievemented when the loan balance is at 0', () => { + state.loanBalance = 0 + + expect(achievement.condition(state)).toEqual(true) + }) +}) diff --git a/src/game-logic/reducers/addExperience.js b/src/game-logic/reducers/addExperience.js new file mode 100644 index 000000000..4a2ae853b --- /dev/null +++ b/src/game-logic/reducers/addExperience.js @@ -0,0 +1,22 @@ +import { levelAchieved } from '../../utils/levelAchieved' + +import { processLevelUp } from './processLevelUp' + +/** + * @param {farmhand.state} state + * @param {number} amount + * @returns {farmhand.state} + */ +export const addExperience = (state, amount) => { + const { experience } = state + const oldLevel = levelAchieved(experience) + + let newState = { + ...state, + experience: experience + amount, + } + + newState = processLevelUp(newState, oldLevel) + + return newState +} diff --git a/src/game-logic/reducers/addExperience.test.js b/src/game-logic/reducers/addExperience.test.js new file mode 100644 index 000000000..a948cd222 --- /dev/null +++ b/src/game-logic/reducers/addExperience.test.js @@ -0,0 +1,30 @@ +import { experienceNeededForLevel } from '../../utils' + +import { addExperience } from './addExperience' + +describe('addExperience', () => { + let gameState + + beforeEach(() => { + gameState = { + experience: 0, + inventory: [], + showNotifications: true, + todaysNotifications: [], + } + }) + + it('adds experience to current experience', () => { + const newState = addExperience(gameState, 10) + + expect(newState.experience).toEqual(10) + }) + + it('process a level up when enough experience is achieved', () => { + const newState = addExperience(gameState, experienceNeededForLevel(2)) + + expect(newState.todaysNotifications[0].message).toEqual( + expect.stringContaining('You reached **level 2!**') + ) + }) +}) diff --git a/src/game-logic/reducers/generatePriceEvents.js b/src/game-logic/reducers/generatePriceEvents.js index de02d3a4a..b3b40f803 100644 --- a/src/game-logic/reducers/generatePriceEvents.js +++ b/src/game-logic/reducers/generatePriceEvents.js @@ -1,9 +1,8 @@ +import { levelAchieved } from '../../utils/levelAchieved' import { - farmProductsSold, filterItemIdsToSeeds, getPriceEventForCrop, getRandomUnlockedCrop, - levelAchieved, } from '../../utils' import { getLevelEntitlements } from '../../utils/getLevelEntitlements' import { PRICE_EVENT_CHANCE } from '../../constants' @@ -29,7 +28,7 @@ export const generatePriceEvents = state => { // less-than check. if (random() < PRICE_EVENT_CHANCE) { const { items: unlockedItems } = getLevelEntitlements( - levelAchieved(farmProductsSold(state.itemsSold)) + levelAchieved(state.experience) ) const cropItem = getRandomUnlockedCrop( diff --git a/src/game-logic/reducers/index.js b/src/game-logic/reducers/index.js index 7053daec3..ae38907ad 100644 --- a/src/game-logic/reducers/index.js +++ b/src/game-logic/reducers/index.js @@ -4,6 +4,7 @@ */ export * from './addCowToInventory' +export * from './addExperience' export * from './addItemToInventory' export * from './addPeer' export * from './addRevenue' diff --git a/src/game-logic/reducers/processLevelUp.js b/src/game-logic/reducers/processLevelUp.js index 3bc37daef..7793bc57b 100644 --- a/src/game-logic/reducers/processLevelUp.js +++ b/src/game-logic/reducers/processLevelUp.js @@ -1,9 +1,8 @@ import { levels } from '../../data/levels' +import { levelAchieved } from '../../utils/levelAchieved' import { - farmProductsSold, getRandomLevelUpReward, getRandomLevelUpRewardQuantity, - levelAchieved, unlockTool, } from '../../utils' import { getLevelEntitlements } from '../../utils/getLevelEntitlements' @@ -19,8 +18,8 @@ import { showNotification } from './showNotification' * @returns {farmhand.state} */ export const processLevelUp = (state, oldLevel) => { - const { itemsSold, selectedItemId } = state - const newLevel = levelAchieved(farmProductsSold(itemsSold)) + const { experience, selectedItemId } = state + const newLevel = levelAchieved(experience) // Loop backwards so that the notifications appear in descending order. for (let i = newLevel; i > oldLevel; i--) { @@ -47,9 +46,7 @@ export const processLevelUp = (state, oldLevel) => { levelObject.increasesSprinklerRange && selectedItemId === SPRINKLER_ITEM_ID ) { - const { sprinklerRange } = getLevelEntitlements( - levelAchieved(farmProductsSold(itemsSold)) - ) + const { sprinklerRange } = getLevelEntitlements(levelAchieved(experience)) if (sprinklerRange > state.hoveredPlotRangeSize) { state = { diff --git a/src/game-logic/reducers/processLevelUp.test.js b/src/game-logic/reducers/processLevelUp.test.js index 012267e55..a61dd65d1 100644 --- a/src/game-logic/reducers/processLevelUp.test.js +++ b/src/game-logic/reducers/processLevelUp.test.js @@ -1,6 +1,6 @@ import { LEVEL_GAINED_NOTIFICATION } from '../../templates' import { toolLevel } from '../../enums' -import { farmProductSalesVolumeNeededForLevel } from '../../utils' +import { experienceNeededForLevel } from '../../utils' jest.mock('../../data/achievements') jest.mock('../../data/maps') @@ -20,10 +20,8 @@ describe('processLevelUp', () => { })) const { todaysNotifications } = jest.requireActual('./').processLevelUp( { + experience: experienceNeededForLevel(3), inventory: [], - itemsSold: { - 'sample-crop-1': farmProductSalesVolumeNeededForLevel(3), - }, todaysNotifications: [], }, 1 @@ -65,10 +63,8 @@ describe('processLevelUp', () => { const { hoveredPlotRangeSize } = jest.requireActual('./').processLevelUp( { + experience: experienceNeededForLevel(2), hoveredPlotRangeSize: 1, - itemsSold: { - 'sample-crop-1': farmProductSalesVolumeNeededForLevel(2), - }, selectedItemId: 'sprinkler', todaysNotifications: [], }, diff --git a/src/game-logic/reducers/processSprinklers.js b/src/game-logic/reducers/processSprinklers.js index 92ff18ba2..db4522b47 100644 --- a/src/game-logic/reducers/processSprinklers.js +++ b/src/game-logic/reducers/processSprinklers.js @@ -1,10 +1,6 @@ import { itemType } from '../../enums' -import { - farmProductsSold, - getPlotContentType, - getRangeCoords, - levelAchieved, -} from '../../utils' +import { levelAchieved } from '../../utils/levelAchieved' +import { getPlotContentType, getRangeCoords } from '../../utils' import { getLevelEntitlements } from '../../utils/getLevelEntitlements' import { setWasWatered } from './helpers' @@ -15,13 +11,11 @@ import { modifyFieldPlotAt } from './modifyFieldPlotAt' * @returns {farmhand.state} */ export const processSprinklers = state => { - const { field, itemsSold } = state + const { field, experience } = state const crops = new Map() let modifiedField = [...field] - const { sprinklerRange } = getLevelEntitlements( - levelAchieved(farmProductsSold(itemsSold)) - ) + const { sprinklerRange } = getLevelEntitlements(levelAchieved(experience)) field.forEach((row, plotY) => { row.forEach((plot, plotX) => { diff --git a/src/game-logic/reducers/sellItem.js b/src/game-logic/reducers/sellItem.js index 8feb6a42c..6067f1c97 100644 --- a/src/game-logic/reducers/sellItem.js +++ b/src/game-logic/reducers/sellItem.js @@ -1,20 +1,20 @@ import { itemsMap } from '../../data/maps' +import { isItemAFarmProduct } from '../../utils/isItemAFarmProduct' +import { levelAchieved } from '../../utils/levelAchieved' import { castToMoney, - farmProductsSold, getAdjustedItemValue, getResaleValue, getSalePriceMultiplier, - isItemAFarmProduct, isItemSoldInShop, - levelAchieved, moneyTotal, } from '../../utils' -import { LOAN_GARNISHMENT_RATE } from '../../constants' +import { LOAN_GARNISHMENT_RATE, EXPERIENCE_VALUES } from '../../constants' import { SOLD_ITEM_PEER_NOTIFICATION } from '../../templates' import { decrementItemFromInventory } from './decrementItemFromInventory' import { processLevelUp } from './processLevelUp' +import { addExperience } from './addExperience' import { addRevenue } from './addRevenue' import { updateLearnedRecipes } from './updateLearnedRecipes' import { adjustLoan } from './adjustLoan' @@ -35,11 +35,12 @@ export const sellItem = (state, { id }, howMany = 1) => { const item = itemsMap[id] const { completedAchievements, + experience, itemsSold, money: initialMoney, valueAdjustments, } = state - const oldLevel = levelAchieved(farmProductsSold(itemsSold)) + const oldLevel = levelAchieved(experience) let { loanBalance } = state const adjustedItemValue = isItemSoldInShop(item) @@ -47,7 +48,9 @@ export const sellItem = (state, { id }, howMany = 1) => { : getAdjustedItemValue(valueAdjustments, id) const saleIsGarnished = isItemAFarmProduct(item) - let saleValue = 0 + let saleValue = 0, + experienceGained = 0, + salePriceMultiplier = 1 for (let i = 0; i < howMany; i++) { const loanGarnishment = saleIsGarnished @@ -57,9 +60,10 @@ export const sellItem = (state, { id }, howMany = 1) => { ) : 0 - const salePriceMultiplier = isItemAFarmProduct(item) - ? getSalePriceMultiplier(completedAchievements) - : 1 + if (isItemAFarmProduct(item)) { + salePriceMultiplier = getSalePriceMultiplier(completedAchievements) + experienceGained += EXPERIENCE_VALUES.ITEM_SOLD + } const garnishedProfit = adjustedItemValue * salePriceMultiplier - loanGarnishment @@ -82,6 +86,8 @@ export const sellItem = (state, { id }, howMany = 1) => { state = addRevenue({ ...state, money: initialMoney }, saleValue) } + state = addExperience(state, experienceGained) + state = { ...state, itemsSold: newItemsSold, diff --git a/src/game-logic/reducers/sellItem.test.js b/src/game-logic/reducers/sellItem.test.js index 84c0830eb..2f52b89ca 100644 --- a/src/game-logic/reducers/sellItem.test.js +++ b/src/game-logic/reducers/sellItem.test.js @@ -128,9 +128,10 @@ describe('sellItem', () => { let state describe('item is not a farm product', () => { - test('sale is not garnished', () => { + beforeEach(() => { state = sellItem( { + experience: 0, inventory: [testItem({ id: 'sample-crop-1-seed', quantity: 3 })], itemsSold: {}, loanBalance: 100, @@ -144,12 +145,18 @@ describe('sellItem', () => { testItem({ id: 'sample-crop-1-seed' }), 3 ) + }) + test('sale is not garnished', () => { expect(state.loanBalance).toEqual(100) expect(state.money).toEqual(130) expect(state.revenue).toEqual(0) expect(state.todaysRevenue).toEqual(0) }) + + test('experience is not gained', () => { + expect(state.experience).toEqual(0) + }) }) describe('item is a farm product', () => { @@ -182,6 +189,7 @@ describe('sellItem', () => { beforeEach(() => { state = sellItem( { + experience: 0, inventory: [testItem({ id: 'sample-crop-1', quantity: 3 })], itemsSold: {}, loanBalance: 1.5, @@ -211,6 +219,10 @@ describe('sellItem', () => { { message: LOAN_PAYOFF``, severity: 'success' }, ]) }) + + test('experience is gained', () => { + expect(state.experience).toEqual(3) + }) }) }) }) diff --git a/src/game-logic/reducers/sellKeg.js b/src/game-logic/reducers/sellKeg.js index 7cfcc04dc..60a4450b3 100644 --- a/src/game-logic/reducers/sellKeg.js +++ b/src/game-logic/reducers/sellKeg.js @@ -5,22 +5,16 @@ */ import { itemsMap } from '../../data/maps' -import { - castToMoney, - farmProductsSold, - getSalePriceMultiplier, - levelAchieved, - moneyTotal, -} from '../../utils' -import { LOAN_GARNISHMENT_RATE } from '../../constants' +import { castToMoney, getSalePriceMultiplier, moneyTotal } from '../../utils' +import { EXPERIENCE_VALUES, LOAN_GARNISHMENT_RATE } from '../../constants' import { SOLD_FERMENTED_ITEM_PEER_NOTIFICATION } from '../../templates' import { getKegValue } from '../../utils/getKegValue' -import { processLevelUp } from './processLevelUp' +import { addExperience } from './addExperience' import { addRevenue } from './addRevenue' -import { updateLearnedRecipes } from './updateLearnedRecipes' import { adjustLoan } from './adjustLoan' import { removeKegFromCellar } from './removeKegFromCellar' +import { updateLearnedRecipes } from './updateLearnedRecipes' import { prependPendingPeerMessage } from './index' @@ -38,7 +32,6 @@ export const sellKeg = (state, keg) => { itemsSold, money: initialMoney, } = state - const oldLevel = levelAchieved(farmProductsSold(itemsSold)) let { loanBalance } = state let saleValue = 0 @@ -79,8 +72,7 @@ export const sellKeg = (state, keg) => { // money needs to be passed in explicitly here because state.money gets // mutated above and addRevenue needs its initial value. state = addRevenue({ ...state, money: initialMoney }, saleValue) - - state = processLevelUp(state, oldLevel) + state = addExperience(state, EXPERIENCE_VALUES.KEG_SOLD) state = removeKegFromCellar(state, keg.id) // NOTE: This notification will need to be revisited to support Wine sales. diff --git a/src/index.js b/src/index.js index ad2a41788..80a5461f9 100644 --- a/src/index.js +++ b/src/index.js @@ -206,7 +206,7 @@ /** * @typedef farmhand.peerMetadata - * @type {Pick & { cowOfferedForTrade?: farmhand.offeredCow }} + * @type {Pick & { cowOfferedForTrade?: farmhand.offeredCow }} */ /** diff --git a/src/utils/farmProductsSold.js b/src/utils/farmProductsSold.js new file mode 100644 index 000000000..ad44d7c19 --- /dev/null +++ b/src/utils/farmProductsSold.js @@ -0,0 +1,17 @@ +import { itemsMap } from '../data/maps' + +import { memoize } from './memoize' +import { isItemAFarmProduct } from './isItemAFarmProduct' + +export const farmProductsSold = memoize( + /** + * @param {Record} itemsSold + * @returns {number} + */ + itemsSold => + Object.entries(itemsSold).reduce( + (sum, [itemId, numberSold]) => + sum + (isItemAFarmProduct(itemsMap[itemId]) ? numberSold : 0), + 0 + ) +) diff --git a/src/utils/index.js b/src/utils/index.js index 5b2a44370..5747ec3d4 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -83,11 +83,12 @@ import { } from '../constants' import { random } from '../common/utils' -import { memoize } from './memoize' +import { farmProductsSold } from './farmProductsSold' import { getCropLifecycleDuration } from './getCropLifecycleDuration' -import { getItemBaseValue } from './getItemBaseValue' import { getInventoryQuantityMap } from './getInventoryQuantityMap' +import { getItemBaseValue } from './getItemBaseValue' import { getLevelEntitlements } from './getLevelEntitlements' +import { memoize } from './memoize' const Jimp = configureJimp({ types: [jimpPng], @@ -289,24 +290,6 @@ export const getPlotContentType = ({ itemId }) => export const doesPlotContainCrop = plot => plot !== null && getPlotContentType(plot) === itemType.CROP -/** - * @param {farmhand.item} item - * @returns {boolean} - */ -export const isItemAGrownCrop = item => - Boolean(item.type === itemType.CROP && !item.growsInto) - -/** - * @param {farmhand.item} item - * @returns {boolean} - */ -export const isItemAFarmProduct = item => - Boolean( - isItemAGrownCrop(item) || - item.type === itemType.MILK || - item.type === itemType.CRAFTED_ITEM - ) - export const getLifeStageRange = memoize(( /** @type {farmhand.cropTimetable} */ cropTimetable ) => @@ -791,31 +774,11 @@ export const findCowById = memoize( (cowInventory, id) => cowInventory.find(cow => id === cow.id) ) -export const farmProductsSold = memoize( - /** - * @param {Record} itemsSold - * @returns {number} - */ - itemsSold => - Object.entries(itemsSold).reduce( - (sum, [itemId, numberSold]) => - sum + (isItemAFarmProduct(itemsMap[itemId]) ? numberSold : 0), - 0 - ) -) - -/** - * @param {number} farmProductsSold - * @returns {number} - */ -export const levelAchieved = farmProductsSold => - Math.floor(Math.sqrt(farmProductsSold) / 10) + 1 - /** * @param {number} targetLevel * @returns {number} */ -export const farmProductSalesVolumeNeededForLevel = targetLevel => +export const experienceNeededForLevel = targetLevel => ((targetLevel - 1) * 10) ** 2 /** @@ -986,6 +949,10 @@ export const transformStateDataForImport = state => { const rejectedKeys = ['version'] rejectedKeys.forEach(rejectedKey => delete sanitizedState[rejectedKey]) + if (sanitizedState.experience === 0) { + sanitizedState.experience = farmProductsSold(sanitizedState.itemsSold) + } + return sanitizedState } @@ -1200,5 +1167,3 @@ export const isOctober = () => new Date().getMonth() === 9 * @returns {boolean} */ export const isDecember = () => new Date().getMonth() === 11 - -export { default as totalIngredientsInRecipe } from './totalIngredientsInRecipe' diff --git a/src/utils/index.test.js b/src/utils/index.test.js index 6a75a6d2b..aabac0929 100644 --- a/src/utils/index.test.js +++ b/src/utils/index.test.js @@ -25,14 +25,17 @@ import { MALE_COW_WEIGHT_MULTIPLIER, } from '../constants' +import { levelAchieved } from './levelAchieved' +import { farmProductsSold } from './farmProductsSold' +import { isItemAFarmProduct } from './isItemAFarmProduct' + import { canMakeRecipe, castToMoney, computeMarketPositions, chooseRandom, dollarString, - farmProductSalesVolumeNeededForLevel, - farmProductsSold, + experienceNeededForLevel, generateCow, generateOffspringCow, get7DayAverage, @@ -53,12 +56,11 @@ import { getRangeCoords, getSalePriceMultiplier, integerString, - isItemAFarmProduct, - levelAchieved, maxYieldOfRecipe, moneyTotal, percentageString, randomChoice, + transformStateDataForImport, } from './index' jest.mock('../data/maps') @@ -768,21 +770,30 @@ describe('farmProductsSold', () => { }) describe('levelAchieved', () => { - test('calculates achieved level', () => { - expect(levelAchieved(0)).toEqual(1) - expect(levelAchieved(100)).toEqual(2) - expect(levelAchieved(150)).toEqual(2) - expect(levelAchieved(400)).toEqual(3) - expect(levelAchieved(980100)).toEqual(100) - }) + const cases = [ + [1, 0], + [2, 100], + [2, 150], + [3, 400], + [100, 980100], + ] + + test.each(cases)( + `returns level %p for %p experience`, + (expectedLevel, experience) => { + expect(levelAchieved(experience)).toEqual(expectedLevel) + } + ) }) -describe('farmProductSalesVolumeNeededForLevel', () => { - test('calculates farm sales volume that will meet level requirements', () => { - expect(farmProductSalesVolumeNeededForLevel(1)).toEqual(0) - expect(farmProductSalesVolumeNeededForLevel(2)).toEqual(100) - expect(farmProductSalesVolumeNeededForLevel(3)).toEqual(400) - expect(farmProductSalesVolumeNeededForLevel(100)).toEqual(980100) +describe('experienceNeededForLevel', () => { + test.each([ + [0, 1], + [100, 2], + [400, 3], + [980100, 100], + ])('it returns %s experience for level %s', (experienceNeeded, levelNum) => { + expect(experienceNeededForLevel(levelNum)).toEqual(experienceNeeded) }) }) @@ -1016,3 +1027,52 @@ describe('getCowImage', () => { expect(image).toEqual(animals.cow.rainbow) }) }) + +describe('transformStateDataForImport', () => { + let state + + beforeEach(() => { + state = { + dayCount: 100, + experience: 10, + inventoryLimit: 1000, + loanBalance: 100, + money: 1234, + version: 1, + } + }) + + test('it returns a sanitized state without version', () => { + const sanitizedState = transformStateDataForImport(state) + + expect(sanitizedState).toEqual({ + dayCount: 100, + experience: 10, + inventoryLimit: 1000, + loanBalance: 100, + money: 1234, + }) + }) + + test('it calculates experience from itemsSold if experience is 0', () => { + state.experience = 0 + state.itemsSold = { + 'sample-crop-1': 5, + 'sample-crop-1-seed': 10, + } + + const sanitizedState = transformStateDataForImport(state) + + expect(sanitizedState).toEqual({ + dayCount: 100, + experience: 5, + inventoryLimit: 1000, + itemsSold: { + 'sample-crop-1': 5, + 'sample-crop-1-seed': 10, + }, + loanBalance: 100, + money: 1234, + }) + }) +}) diff --git a/src/utils/isItemAFarmProduct.js b/src/utils/isItemAFarmProduct.js new file mode 100644 index 000000000..108d8bd87 --- /dev/null +++ b/src/utils/isItemAFarmProduct.js @@ -0,0 +1,12 @@ +import { itemType } from '../enums' + +import { isItemAGrownCrop } from './isItemAGrownCrop' + +const FARM_PRODUCT_TYPES = [itemType.MILK, itemType.CRAFTED_ITEM] + +/** + * @param {farmhand.item} item + * @returns {boolean} + */ +export const isItemAFarmProduct = item => + Boolean(isItemAGrownCrop(item) || FARM_PRODUCT_TYPES.includes(item.type)) diff --git a/src/utils/isItemAGrownCrop.js b/src/utils/isItemAGrownCrop.js new file mode 100644 index 000000000..990f8144c --- /dev/null +++ b/src/utils/isItemAGrownCrop.js @@ -0,0 +1,8 @@ +import { itemType } from '../enums' + +/** + * @param {farmhand.item} item + * @returns {boolean} + */ +export const isItemAGrownCrop = item => + Boolean(item.type === itemType.CROP && !item.growsInto) diff --git a/src/utils/levelAchieved.js b/src/utils/levelAchieved.js new file mode 100644 index 000000000..81fc1d10f --- /dev/null +++ b/src/utils/levelAchieved.js @@ -0,0 +1,9 @@ +/** + * @param {{ + * experience?: number, + * }} props + * @returns {number} + */ +export function levelAchieved(experience = 0) { + return Math.floor(Math.sqrt(experience) / 10) + 1 +} diff --git a/src/utils/totalIngredientsInRecipe.js b/src/utils/totalIngredientsInRecipe.js index 068a1c8a3..dd8a7c35f 100644 --- a/src/utils/totalIngredientsInRecipe.js +++ b/src/utils/totalIngredientsInRecipe.js @@ -1,4 +1,4 @@ -export default function totalIngredientsInRecipe(recipe, amount = 1) { +export function totalIngredientsInRecipe(recipe, amount = 1) { return ( amount * Object.values(recipe.ingredients).reduce( diff --git a/src/utils/totalIngredientsInRecipe.test.js b/src/utils/totalIngredientsInRecipe.test.js index b4d1d7931..6d1050168 100644 --- a/src/utils/totalIngredientsInRecipe.test.js +++ b/src/utils/totalIngredientsInRecipe.test.js @@ -1,4 +1,4 @@ -import totalIngredientsInRecipe from './totalIngredientsInRecipe' +import { totalIngredientsInRecipe } from './totalIngredientsInRecipe' describe('totalIngredientsInRecipe', () => { test('it can total a single ingredient upgrade', () => {