@jeremyckahn/farmhand 1.23.2 → 1.23.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +1 -0
  2. package/dist/assets/{index-DNPchtK9.js → index-Dihg7UqQ.js} +15 -15
  3. package/dist/assets/{index-DNPchtK9.js.map → index-Dihg7UqQ.js.map} +1 -1
  4. package/dist/index.html +1 -1
  5. package/dist/service-worker.js +1 -1
  6. package/dist/service-worker.js.map +1 -1
  7. package/package.json +4 -5
  8. package/src/common/services/randomNumber.test.ts +61 -1
  9. package/src/common/services/randomNumber.ts +36 -1
  10. package/src/common/utils.ts +22 -13
  11. package/src/components/AccountingView/AccountingView.tsx +4 -12
  12. package/src/components/Achievement/Achievement.tsx +0 -13
  13. package/src/components/AchievementsView/AchievementsView.tsx +2 -7
  14. package/src/components/AnimatedNumber/AnimatedNumber.tsx +0 -6
  15. package/src/components/AppBar/AppBar.tsx +0 -10
  16. package/src/components/Cellar/Cellar.tsx +0 -2
  17. package/src/components/Cellar/CellarInventoryTabPanel.tsx +0 -6
  18. package/src/components/Cellar/FermentationTabPanel.tsx +0 -6
  19. package/src/components/Cellar/Keg.tsx +0 -5
  20. package/src/components/Cellar/TabPanel/index.tsx +0 -7
  21. package/src/components/Cellar/WinemakingTabPanel.tsx +0 -6
  22. package/src/components/ContextPane/ContextPane.tsx +0 -6
  23. package/src/components/CowCard/Bloodline/Bloodline.tsx +0 -5
  24. package/src/components/CowCard/CowCard.tsx +0 -26
  25. package/src/components/CowCard/Subheader/Subheader.tsx +11 -26
  26. package/src/components/CowPen/CowPen.tsx +4 -15
  27. package/src/components/CowPenContextMenu/CowPenContextMenu.tsx +0 -16
  28. package/src/components/CowPenContextMenu/TabPanel/index.tsx +0 -7
  29. package/src/components/Farmhand/Farmhand.context.tsx +4 -3
  30. package/src/components/Farmhand/Farmhand.tsx +4 -11
  31. package/src/components/Farmhand/FarmhandReducers.tsx +1 -2
  32. package/src/components/Farmhand/FarmhandService.test.ts +6 -9
  33. package/src/components/Farmhand/hooks/useFarmhandDayAdvancement.ts +2 -6
  34. package/src/components/Farmhand/hooks/useFarmhandNetwork.test.ts +2 -2
  35. package/src/components/Farmhand/hooks/useFarmhandNetwork.ts +15 -31
  36. package/src/components/Farmhand/useFarmhand.test.ts +7 -5
  37. package/src/components/Farmhand/useFarmhand.ts +12 -13
  38. package/src/components/FermentationRecipeList/FermentationRecipe.tsx +0 -5
  39. package/src/components/FermentationRecipeList/FermentationRecipeList.test.tsx +10 -11
  40. package/src/components/FermentationRecipeList/FermentationRecipeList.tsx +2 -3
  41. package/src/components/Field/Field.tsx +13 -48
  42. package/src/components/Forest/Forest.tsx +10 -7
  43. package/src/components/ForestPlot/ForestPlot.tsx +5 -5
  44. package/src/components/ForestQuickSelect/ForestQuickSelect.tsx +0 -12
  45. package/src/components/Home/Home.tsx +0 -6
  46. package/src/components/Home/SnowBackground.tsx +1 -1
  47. package/src/components/IngredientsList/IngredientsList.tsx +0 -7
  48. package/src/components/Inventory/Inventory.test.tsx +4 -4
  49. package/src/components/Inventory/Inventory.tsx +3 -11
  50. package/src/components/Item/Item.tsx +0 -21
  51. package/src/components/ItemList/ItemList.tsx +4 -11
  52. package/src/components/KeybindingsView/KeybindingsView.tsx +0 -2
  53. package/src/components/LogView/LogView.tsx +0 -6
  54. package/src/components/Navigation/DayAndProgressContainer.tsx +0 -7
  55. package/src/components/Navigation/Navigation.tsx +0 -23
  56. package/src/components/NotificationSystem/NotificationSystem.test.tsx +4 -1
  57. package/src/components/NotificationSystem/NotificationSystem.tsx +5 -12
  58. package/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.test.tsx +2 -2
  59. package/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.tsx +0 -11
  60. package/src/components/OnlinePeersView/OnlinePeersView.tsx +0 -10
  61. package/src/components/Plot/Plot.tsx +3 -3
  62. package/src/components/PriceEventView/PriceEventView.tsx +0 -6
  63. package/src/components/ProgressBar/ProgressBar.tsx +0 -5
  64. package/src/components/QuantityInput/QuantityInput.tsx +0 -9
  65. package/src/components/QuickSelect/QuickSelect.tsx +0 -10
  66. package/src/components/Recipe/Recipe.tsx +0 -9
  67. package/src/components/RecipeList/RecipeList.tsx +0 -6
  68. package/src/components/SearchBar/SearchBar.tsx +0 -6
  69. package/src/components/SettingsView/RandomSeedInput.test.tsx +2 -4
  70. package/src/components/SettingsView/RandomSeedInput.tsx +5 -8
  71. package/src/components/SettingsView/SettingsView.test.tsx +21 -0
  72. package/src/components/SettingsView/SettingsView.tsx +24 -16
  73. package/src/components/Shop/Shop.tsx +0 -20
  74. package/src/components/Shop/TabPanel/index.tsx +0 -7
  75. package/src/components/Stage/Stage.tsx +11 -20
  76. package/src/components/StatsView/StatsView.tsx +0 -15
  77. package/src/components/TierPurchase/TierPurchase.tsx +0 -12
  78. package/src/components/Toolbelt/Toolbelt.tsx +5 -10
  79. package/src/components/UpgradePurchase/UpgradePurchase.tsx +0 -10
  80. package/src/components/WineRecipeList/WineRecipe.test.tsx +2 -2
  81. package/src/components/WineRecipeList/WineRecipe.tsx +0 -5
  82. package/src/components/WineRecipeList/WineRecipeList.tsx +2 -3
  83. package/src/components/Workshop/ForgeTabPanel.tsx +0 -8
  84. package/src/components/Workshop/KitchenTabPanel.tsx +0 -7
  85. package/src/components/Workshop/RecyclingTabPanel.tsx +0 -7
  86. package/src/components/Workshop/TabPanel/index.tsx +0 -7
  87. package/src/components/Workshop/WoodChipperTabPanel.tsx +0 -7
  88. package/src/components/Workshop/Workshop.tsx +0 -9
  89. package/src/components/Workshop/getUpgradesAvailable.ts +1 -1
  90. package/src/constants.ts +18 -13
  91. package/src/data/crops/grape.ts +13 -15
  92. package/src/data/maps.test.ts +3 -4
  93. package/src/data/maps.ts +27 -29
  94. package/src/enums.ts +47 -19
  95. package/src/factories/OreFactory.ts +2 -1
  96. package/src/farmhand.d.ts +2 -3
  97. package/src/game-logic/reducers/applyPrecipitation.ts +4 -4
  98. package/src/game-logic/reducers/generatePriceEvents.ts +6 -3
  99. package/src/game-logic/reducers/harvestForestPlot.ts +1 -2
  100. package/src/game-logic/reducers/makeFermentationRecipe.ts +2 -3
  101. package/src/game-logic/reducers/minePlot.test.ts +2 -2
  102. package/src/game-logic/reducers/minePlot.ts +1 -1
  103. package/src/game-logic/reducers/processCowFertilizerProduction.test.ts +16 -14
  104. package/src/game-logic/reducers/processFeedingCows.test.ts +2 -5
  105. package/src/game-logic/reducers/purchaseCow.ts +2 -7
  106. package/src/game-logic/reducers/updateAchievements.ts +1 -1
  107. package/src/game-logic/reducers/updateFinancialRecords.test.ts +21 -25
  108. package/src/game-logic/reducers/updateFinancialRecords.ts +2 -5
  109. package/src/game-logic/reducers/updateInventoryRecordsForNextDay.test.ts +12 -14
  110. package/src/handlers/ui-events.test.ts +44 -42
  111. package/src/handlers/ui-events.tsx +10 -0
  112. package/src/hooks/usePrefersReducedMotion.ts +10 -6
  113. package/src/mui-theme.ts +4 -3
  114. package/src/scripts/generate-crop-table.ts +3 -2
  115. package/src/styles/sx.ts +29 -25
  116. package/src/test-utils/index.ts +1 -0
  117. package/src/utils/chooseRandom.ts +4 -1
  118. package/src/utils/chooseRandomIndex.ts +6 -2
  119. package/src/utils/getCowImage.ts +22 -16
  120. package/src/utils/getMaxYieldOfFermentationRecipe.ts +2 -2
  121. package/src/utils/getPeerMetadata.ts +1 -1
  122. package/src/utils/getPlotImage.test.ts +14 -14
  123. package/src/utils/getPlotImage.ts +10 -10
  124. package/src/utils/getRandomUnlockedCrop.ts +5 -3
  125. package/src/utils/getRandomizedLifespan.test.ts +2 -8
  126. package/src/utils/getValueAdjustmentStream.test.ts +9 -0
  127. package/src/utils/getValueAdjustmentStream.ts +5 -0
  128. package/src/utils/isItemAFarmProduct.ts +2 -2
  129. package/src/utils/shouldPrecipitateToday.ts +3 -1
  130. package/src/utils/shouldStormToday.ts +2 -1
  131. package/src/utils/transformStateDataForImport.ts +3 -5
package/dist/index.html CHANGED
@@ -75,7 +75,7 @@
75
75
  gtag('config', analyticsID)
76
76
  })()
77
77
  </script>
78
- <script type="module" crossorigin src="./assets/index-DNPchtK9.js"></script>
78
+ <script type="module" crossorigin src="./assets/index-Dihg7UqQ.js"></script>
79
79
  <link rel="stylesheet" crossorigin href="./assets/index-CNcrTYN-.css">
80
80
  <link rel="manifest" href="./manifest.webmanifest"></head>
81
81
  <body>
@@ -1,3 +1,3 @@
1
- if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,o)=>{const r=e||("document"in self?document.currentScript.src:"")||location.href;if(s[r])return;let d={};const l=e=>i(e,r),t={module:{uri:r},exports:d,require:l};s[r]=Promise.all(n.map(e=>t[e]||l(e))).then(e=>(o(...e),d))}}define(["./workbox-2fbc6a65"],function(e){"use strict";self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.precacheAndRoute([{url:"index.html",revision:"80a42fe894ce3eed31600d8d77591744"},{url:"electron.js",revision:"b0456826f47539f6c1957c3add5eb959"},{url:"assets/workbox-window.prod.es5-Bd17z0YL.js",revision:null},{url:"assets/index-DNPchtK9.js",revision:null},{url:"assets/index-CNcrTYN-.css",revision:null},{url:"favicon.ico",revision:"445bb19d4a8fd46eb433dd43cbdce691"},{url:"logo192.png",revision:"70746d790b8990639d1390337d77d81a"},{url:"logo512.png",revision:"b044ab46b3f33f0e2d313d961bf1f654"},{url:"logoMaskable.png",revision:"23be58ccf16df9f43658aad3da30056d"},{url:"manifest.webmanifest",revision:"3ad071a00582451c9cdeb827b37b9ebe"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))});
1
+ if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,o)=>{const r=e||("document"in self?document.currentScript.src:"")||location.href;if(s[r])return;let d={};const l=e=>i(e,r),t={module:{uri:r},exports:d,require:l};s[r]=Promise.all(n.map(e=>t[e]||l(e))).then(e=>(o(...e),d))}}define(["./workbox-2fbc6a65"],function(e){"use strict";self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.precacheAndRoute([{url:"index.html",revision:"1a925f166515a9c6c4a510e04760d6d2"},{url:"electron.js",revision:"b0456826f47539f6c1957c3add5eb959"},{url:"assets/workbox-window.prod.es5-Bd17z0YL.js",revision:null},{url:"assets/index-Dihg7UqQ.js",revision:null},{url:"assets/index-CNcrTYN-.css",revision:null},{url:"favicon.ico",revision:"445bb19d4a8fd46eb433dd43cbdce691"},{url:"logo192.png",revision:"70746d790b8990639d1390337d77d81a"},{url:"logo512.png",revision:"b044ab46b3f33f0e2d313d961bf1f654"},{url:"logoMaskable.png",revision:"23be58ccf16df9f43658aad3da30056d"},{url:"manifest.webmanifest",revision:"3ad071a00582451c9cdeb827b37b9ebe"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))});
2
2
  //# sourceMappingURL=service-worker.js.map
3
3
  //# sourceMappingURL=service-worker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"service-worker.js","sources":["../../../../../tmp/06cb6b3e71b87fe0bf1a78c24727277a/service-worker.js"],"sourcesContent":["import {precacheAndRoute as workbox_precaching_precacheAndRoute} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-precaching/precacheAndRoute.mjs';\nimport {cleanupOutdatedCaches as workbox_precaching_cleanupOutdatedCaches} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-precaching/cleanupOutdatedCaches.mjs';\nimport {registerRoute as workbox_routing_registerRoute} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-routing/registerRoute.mjs';\nimport {NavigationRoute as workbox_routing_NavigationRoute} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-routing/NavigationRoute.mjs';\nimport {createHandlerBoundToURL as workbox_precaching_createHandlerBoundToURL} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-precaching/createHandlerBoundToURL.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\nself.addEventListener('message', (event) => {\n if (event.data && event.data.type === 'SKIP_WAITING') {\n self.skipWaiting();\n }\n});\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"index.html\",\n \"revision\": \"80a42fe894ce3eed31600d8d77591744\"\n },\n {\n \"url\": \"electron.js\",\n \"revision\": \"b0456826f47539f6c1957c3add5eb959\"\n },\n {\n \"url\": \"assets/workbox-window.prod.es5-Bd17z0YL.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-DNPchtK9.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-CNcrTYN-.css\",\n \"revision\": null\n },\n {\n \"url\": \"favicon.ico\",\n \"revision\": \"445bb19d4a8fd46eb433dd43cbdce691\"\n },\n {\n \"url\": \"logo192.png\",\n \"revision\": \"70746d790b8990639d1390337d77d81a\"\n },\n {\n \"url\": \"logo512.png\",\n \"revision\": \"b044ab46b3f33f0e2d313d961bf1f654\"\n },\n {\n \"url\": \"logoMaskable.png\",\n \"revision\": \"23be58ccf16df9f43658aad3da30056d\"\n },\n {\n \"url\": \"manifest.webmanifest\",\n \"revision\": \"3ad071a00582451c9cdeb827b37b9ebe\"\n }\n], {});\nworkbox_precaching_cleanupOutdatedCaches();workbox_routing_registerRoute(new workbox_routing_NavigationRoute(workbox_precaching_createHandlerBoundToURL(\"index.html\")));\n\n\n"],"names":["self","addEventListener","event","data","type","skipWaiting","workbox_precaching_precacheAndRoute","url","revision","workbox_precaching_cleanupOutdatedCaches","workbox_routing_registerRoute","workbox_routing_NavigationRoute","workbox_precaching_createHandlerBoundToURL"],"mappings":"inBAmBAA,KAAKC,iBAAiB,UAAYC,IAC5BA,EAAMC,MAA4B,iBAApBD,EAAMC,KAAKC,MAC3BJ,KAAKK,gBASTC,EAAAA,iBAAoC,CAClC,CACEC,IAAO,aACPC,SAAY,oCAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,6CACPC,SAAY,MAEd,CACED,IAAO,2BACPC,SAAY,MAEd,CACED,IAAO,4BACPC,SAAY,MAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,mBACPC,SAAY,oCAEd,CACED,IAAO,uBACPC,SAAY,qCAEb,CAAA,GACHC,EAAAA,wBAA2CC,EAAAA,cAA8B,IAAIC,EAAAA,gBAAgCC,0BAA2C"}
1
+ {"version":3,"file":"service-worker.js","sources":["../../../../../tmp/3f6b996c04d6ac4afbf39f21d035a4a0/service-worker.js"],"sourcesContent":["import {precacheAndRoute as workbox_precaching_precacheAndRoute} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-precaching/precacheAndRoute.mjs';\nimport {cleanupOutdatedCaches as workbox_precaching_cleanupOutdatedCaches} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-precaching/cleanupOutdatedCaches.mjs';\nimport {registerRoute as workbox_routing_registerRoute} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-routing/registerRoute.mjs';\nimport {NavigationRoute as workbox_routing_NavigationRoute} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-routing/NavigationRoute.mjs';\nimport {createHandlerBoundToURL as workbox_precaching_createHandlerBoundToURL} from '/home/runner/work/farmhand/farmhand/node_modules/workbox-precaching/createHandlerBoundToURL.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\nself.addEventListener('message', (event) => {\n if (event.data && event.data.type === 'SKIP_WAITING') {\n self.skipWaiting();\n }\n});\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"index.html\",\n \"revision\": \"1a925f166515a9c6c4a510e04760d6d2\"\n },\n {\n \"url\": \"electron.js\",\n \"revision\": \"b0456826f47539f6c1957c3add5eb959\"\n },\n {\n \"url\": \"assets/workbox-window.prod.es5-Bd17z0YL.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-Dihg7UqQ.js\",\n \"revision\": null\n },\n {\n \"url\": \"assets/index-CNcrTYN-.css\",\n \"revision\": null\n },\n {\n \"url\": \"favicon.ico\",\n \"revision\": \"445bb19d4a8fd46eb433dd43cbdce691\"\n },\n {\n \"url\": \"logo192.png\",\n \"revision\": \"70746d790b8990639d1390337d77d81a\"\n },\n {\n \"url\": \"logo512.png\",\n \"revision\": \"b044ab46b3f33f0e2d313d961bf1f654\"\n },\n {\n \"url\": \"logoMaskable.png\",\n \"revision\": \"23be58ccf16df9f43658aad3da30056d\"\n },\n {\n \"url\": \"manifest.webmanifest\",\n \"revision\": \"3ad071a00582451c9cdeb827b37b9ebe\"\n }\n], {});\nworkbox_precaching_cleanupOutdatedCaches();workbox_routing_registerRoute(new workbox_routing_NavigationRoute(workbox_precaching_createHandlerBoundToURL(\"index.html\")));\n\n\n"],"names":["self","addEventListener","event","data","type","skipWaiting","workbox_precaching_precacheAndRoute","url","revision","workbox_precaching_cleanupOutdatedCaches","workbox_routing_registerRoute","workbox_routing_NavigationRoute","workbox_precaching_createHandlerBoundToURL"],"mappings":"inBAmBAA,KAAKC,iBAAiB,UAAYC,IAC5BA,EAAMC,MAA4B,iBAApBD,EAAMC,KAAKC,MAC3BJ,KAAKK,gBASTC,EAAAA,iBAAoC,CAClC,CACEC,IAAO,aACPC,SAAY,oCAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,6CACPC,SAAY,MAEd,CACED,IAAO,2BACPC,SAAY,MAEd,CACED,IAAO,4BACPC,SAAY,MAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,cACPC,SAAY,oCAEd,CACED,IAAO,mBACPC,SAAY,oCAEd,CACED,IAAO,uBACPC,SAAY,qCAEb,CAAA,GACHC,EAAAA,wBAA2CC,EAAAA,cAA8B,IAAIC,EAAAA,gBAAgCC,0BAA2C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeremyckahn/farmhand",
3
- "version": "1.23.2",
3
+ "version": "1.23.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -68,7 +68,7 @@
68
68
  "browserslist": "^4.21.9",
69
69
  "cheerio": "^1.0.0-rc.3",
70
70
  "cross-env": "^7.0.2",
71
- "electron": "^39.8.4",
71
+ "electron": "^41.10.6",
72
72
  "electron-builder": "^26.0.12",
73
73
  "esbuild": "^0.28.1",
74
74
  "eslint": "^8.57.1",
@@ -87,8 +87,8 @@
87
87
  "mprocs": "^0.9.6",
88
88
  "nodemon": "^2.0.2",
89
89
  "npm-run-all": "^4.1.3",
90
- "prettier": "^1.13.7",
91
- "pretty-quick": "^2.0.1",
90
+ "prettier": "^3.5.3",
91
+ "pretty-quick": "^4.0.0",
92
92
  "serve": "^14.2.0",
93
93
  "typescript": "^6.0.2",
94
94
  "vercel": "^59.11.7",
@@ -134,7 +134,6 @@
134
134
  "notistack": "^2.0.8",
135
135
  "path": "npm:path-browserify@^1.0.1",
136
136
  "process": "^0.11.10",
137
- "prop-types": "^15.6.2",
138
137
  "react": "^17.0.2",
139
138
  "react-confetti": "6.1.0",
140
139
  "react-dom": "^17.0.2",
@@ -1,8 +1,68 @@
1
- import { randomNumberService } from './randomNumber.js'
1
+ import { randomStream } from '../../enums.js'
2
+
3
+ import { RandomNumberService, randomNumberService } from './randomNumber.js'
2
4
 
3
5
  const chance = 0.6
4
6
 
5
7
  describe('RandomNumberService', () => {
8
+ describe('generateRandomNumber', () => {
9
+ let service: RandomNumberService
10
+
11
+ beforeEach(() => {
12
+ service = new RandomNumberService()
13
+ })
14
+
15
+ test('returns queued numbers for a stream before random ones', () => {
16
+ const reference = new RandomNumberService()
17
+
18
+ reference.seedRandomNumber('123')
19
+ const firstSeeded = reference.generateRandomNumber()
20
+
21
+ service.seedRandomNumber('123')
22
+ service.queueRandomNumbers(randomStream.PRECIPITATION, [0.1, 0.2])
23
+
24
+ expect(service.generateRandomNumber(randomStream.PRECIPITATION)).toEqual(
25
+ 0.1
26
+ )
27
+ expect(service.generateRandomNumber(randomStream.PRECIPITATION)).toEqual(
28
+ 0.2
29
+ )
30
+ // Queued numbers don't consume seeded draws, so the seeded sequence
31
+ // resumes from its start
32
+ expect(service.generateRandomNumber(randomStream.PRECIPITATION)).toEqual(
33
+ firstSeeded
34
+ )
35
+ })
36
+
37
+ test('queued numbers only affect their own stream', () => {
38
+ vitest.spyOn(Math, 'random').mockReturnValue(0.42)
39
+ service.queueRandomNumbers(randomStream.PRECIPITATION, [0.1])
40
+
41
+ expect(service.generateRandomNumber(randomStream.STORM)).toEqual(0.42)
42
+ expect(service.generateRandomNumber()).toEqual(0.42)
43
+ expect(service.generateRandomNumber(randomStream.PRECIPITATION)).toEqual(
44
+ 0.1
45
+ )
46
+ })
47
+
48
+ test('unqueued stream draws come from the shared seeded sequence', () => {
49
+ const reference = new RandomNumberService()
50
+
51
+ reference.seedRandomNumber('123')
52
+ const shared = [
53
+ reference.generateRandomNumber(),
54
+ reference.generateRandomNumber(),
55
+ ]
56
+
57
+ service.seedRandomNumber('123')
58
+
59
+ expect([
60
+ service.generateRandomNumber(randomStream.PRECIPITATION),
61
+ service.generateRandomNumber(randomStream.STORM),
62
+ ]).toEqual(shared)
63
+ })
64
+ })
65
+
6
66
  describe('isRandomNumberLessThan', () => {
7
67
  test('it returns true when random number is below chance', () => {
8
68
  vitest
@@ -1,9 +1,19 @@
1
1
  import seedrandom from 'seedrandom'
2
2
  import globalWindow from 'global/window.js'
3
3
 
4
+ import { randomStream } from '../../enums.js'
5
+
4
6
  export class RandomNumberService {
5
7
  seededRandom: (() => number) | null = null
6
8
 
9
+ /**
10
+ * Values queued per stream name that are returned, in order, before any
11
+ * seeded or Math.random value. This lets tests force a specific outcome
12
+ * (e.g. rain tonight) without searching for a seed that happens to produce
13
+ * it. Exposed via the window.farmhand debug hook.
14
+ */
15
+ queuedNumbers: Map<randomStream, number[]> = new Map()
16
+
7
17
  constructor() {
8
18
  // The availability of window.location needs to be checked before accessing
9
19
  // its .search property. This code runs in both a browser and Node.js
@@ -21,10 +31,35 @@ export class RandomNumberService {
21
31
  this.seededRandom = seedrandom(seed)
22
32
  }
23
33
 
24
- generateRandomNumber(): number {
34
+ /**
35
+ * @param stream Optional name of the random number stream being drawn
36
+ * from, which lets queueRandomNumbers target it. Values that aren't queued
37
+ * come from the same seeded (or Math.random) sequence regardless of stream.
38
+ */
39
+ generateRandomNumber(stream?: randomStream): number {
40
+ if (stream !== undefined) {
41
+ const queuedNumber = this.queuedNumbers.get(stream)?.shift()
42
+
43
+ if (queuedNumber !== undefined) {
44
+ return queuedNumber
45
+ }
46
+ }
47
+
25
48
  return this.seededRandom ? this.seededRandom() : Math.random()
26
49
  }
27
50
 
51
+ /**
52
+ * @param stream Name of the random number stream to queue values for.
53
+ * @param numbers Values to return from the stream, in order, before it
54
+ * resumes producing random numbers.
55
+ */
56
+ queueRandomNumbers(stream: randomStream, numbers: number[]) {
57
+ this.queuedNumbers.set(stream, [
58
+ ...(this.queuedNumbers.get(stream) ?? []),
59
+ ...numbers,
60
+ ])
61
+ }
62
+
28
63
  unseedRandomNumber() {
29
64
  this.seededRandom = null
30
65
  }
@@ -1,25 +1,34 @@
1
1
  import { itemsMap } from '../data/maps.js'
2
+ import { randomStream } from '../enums.js'
3
+ import { getValueAdjustmentStream } from '../utils/getValueAdjustmentStream.js'
2
4
 
3
5
  import { randomNumberService } from './services/randomNumber.ts'
4
6
 
5
- export const random = () => {
6
- return randomNumberService.generateRandomNumber()
7
+ /**
8
+ * @param stream Optional name of the random number stream being drawn from.
9
+ * See RandomNumberService#generateRandomNumber.
10
+ */
11
+ export const random = (stream?: randomStream) => {
12
+ return randomNumberService.generateRandomNumber(stream)
7
13
  }
8
14
 
9
15
  export const generateValueAdjustments = (
10
16
  priceCrashes: Partial<Record<string, farmhand.priceEvent>> = {},
11
17
  priceSurges: Partial<Record<string, farmhand.priceEvent>> = {}
12
18
  ): Record<string, number> =>
13
- Object.keys(itemsMap).reduce((acc, key) => {
14
- if (itemsMap[key].doesPriceFluctuate) {
15
- if (priceCrashes[key]) {
16
- acc[key] = 0.5
17
- } else if (priceSurges[key]) {
18
- acc[key] = 1.5
19
- } else {
20
- acc[key] = random() + 0.5
19
+ Object.keys(itemsMap).reduce(
20
+ (acc, key) => {
21
+ if (itemsMap[key].doesPriceFluctuate) {
22
+ if (priceCrashes[key]) {
23
+ acc[key] = 0.5
24
+ } else if (priceSurges[key]) {
25
+ acc[key] = 1.5
26
+ } else {
27
+ acc[key] = random(getValueAdjustmentStream(key)) + 0.5
28
+ }
21
29
  }
22
- }
23
30
 
24
- return acc
25
- }, {} as Record<string, number>)
31
+ return acc
32
+ },
33
+ {} as Record<string, number>
34
+ )
@@ -6,7 +6,6 @@ import Button from '@mui/material/Button/index.js'
6
6
  import TextField from '@mui/material/TextField/index.js'
7
7
  import AccountBalanceIcon from '@mui/icons-material/AccountBalance.js'
8
8
  import NumberFormat from 'react-number-format'
9
- import { func, number } from 'prop-types'
10
9
 
11
10
  import FarmhandContext from '../Farmhand/Farmhand.context.js'
12
11
  import { moneyString } from '../../utils/moneyString.js'
@@ -65,10 +64,10 @@ const AccountingView = ({
65
64
  Math.min(loanBalance, money)
66
65
  )
67
66
 
68
- useEffect(() => setLoanInputValue(Math.min(loanBalance, money)), [
69
- loanBalance,
70
- money,
71
- ])
67
+ useEffect(
68
+ () => setLoanInputValue(Math.min(loanBalance, money)),
69
+ [loanBalance, money]
70
+ )
72
71
 
73
72
  return (
74
73
  <Div className="AccountingView">
@@ -156,13 +155,6 @@ const AccountingView = ({
156
155
  )
157
156
  }
158
157
 
159
- AccountingView.propTypes = {
160
- handleClickTakeOutLoanButton: func.isRequired,
161
- handleClickLoanPaydownButton: func.isRequired,
162
- loanBalance: number.isRequired,
163
- money: number.isRequired,
164
- }
165
-
166
158
  export default function Consumer(props: Partial<AccountingViewProps>) {
167
159
  return (
168
160
  <FarmhandContext.Consumer>
@@ -6,7 +6,6 @@ import Card from '@mui/material/Card/index.js'
6
6
  import CardHeader from '@mui/material/CardHeader/index.js'
7
7
  import CardContent from '@mui/material/CardContent/index.js'
8
8
  import BeenhereIcon from '@mui/icons-material/Beenhere.js'
9
- import { bool, object, shape, string } from 'prop-types'
10
9
 
11
10
  import FarmhandContext from '../Farmhand/Farmhand.context.js'
12
11
  import { Div } from '../Elements/index.js'
@@ -62,18 +61,6 @@ const Achievement = ({
62
61
  )
63
62
  }
64
63
 
65
- Achievement.propTypes = {
66
- achievement: shape({
67
- description: string.isRequired,
68
- id: string.isRequired,
69
- name: string.isRequired,
70
- rewardDescription: string.isRequired,
71
- }).isRequired,
72
- completedAchievements: object.isRequired,
73
- gameState: object.isRequired,
74
- isComplete: bool,
75
- }
76
-
77
64
  export default function Consumer(props: {
78
65
  achievement: farmhand.achievement
79
66
  completedAchievements?: Partial<Record<string, boolean>>
@@ -2,7 +2,6 @@ import React from 'react'
2
2
  import Accordion from '@mui/material/Accordion/index.js'
3
3
  import AccordionSummary from '@mui/material/AccordionSummary/index.js'
4
4
  import AccordionDetails from '@mui/material/AccordionDetails/index.js'
5
- import { object } from 'prop-types'
6
5
  import Divider from '@mui/material/Divider/index.js'
7
6
 
8
7
  import FarmhandContext from '../Farmhand/Farmhand.context.js'
@@ -23,8 +22,8 @@ const partitionAchievements = memoize(
23
22
  return acc
24
23
  },
25
24
  {
26
- complete: [] as typeof achievements[number][],
27
- incomplete: [] as typeof achievements[number][],
25
+ complete: [] as (typeof achievements)[number][],
26
+ incomplete: [] as (typeof achievements)[number][],
28
27
  }
29
28
  )
30
29
  )
@@ -84,10 +83,6 @@ const AchievementsView = ({
84
83
  </Div>
85
84
  )
86
85
 
87
- AchievementsView.propTypes = {
88
- completedAchievements: object.isRequired,
89
- }
90
-
91
86
  export default function Consumer(
92
87
  props: Partial<Parameters<typeof AchievementsView>[0]>
93
88
  ) {
@@ -1,7 +1,6 @@
1
1
  import React, { useEffect, useRef, useState } from 'react'
2
2
  // eslint-disable-next-line no-unused-vars
3
3
  import { tween, Tweenable } from 'shifty'
4
- import { func as funcProp, number as numberProp } from 'prop-types'
5
4
 
6
5
  import { usePrefersReducedMotion } from '../../hooks/usePrefersReducedMotion.js'
7
6
 
@@ -55,9 +54,4 @@ const AnimatedNumber = ({
55
54
  return <span className="AnimatedNumber">{formatter(displayedNumber)}</span>
56
55
  }
57
56
 
58
- AnimatedNumber.propTypes = {
59
- formatter: funcProp,
60
- number: numberProp.isRequired,
61
- }
62
-
63
57
  export default AnimatedNumber
@@ -1,7 +1,6 @@
1
1
  import React, { useEffect, useRef, useState } from 'react'
2
2
  // eslint-disable-next-line no-unused-vars
3
3
  import { tween, Tweenable } from 'shifty'
4
- import { array, bool, func, number, string } from 'prop-types'
5
4
 
6
5
  import { default as MuiAppBar } from '@mui/material/AppBar/index.js'
7
6
  import Toolbar from '@mui/material/Toolbar/index.js'
@@ -192,15 +191,6 @@ export const AppBar = ({
192
191
  </MuiAppBar>
193
192
  )
194
193
 
195
- AppBar.propTypes = {
196
- dayCount: number.isRequired,
197
- handleClickNotificationIndicator: func.isRequired,
198
- money: number.isRequired,
199
- showNotifications: bool.isRequired,
200
- todaysNotifications: array.isRequired,
201
- viewTitle: string.isRequired,
202
- }
203
-
204
194
  export default function Consumer(props: Partial<Parameters<typeof AppBar>[0]>) {
205
195
  return (
206
196
  <FarmhandContext.Consumer>
@@ -32,5 +32,3 @@ export const Cellar = () => {
32
32
  </Div>
33
33
  )
34
34
  }
35
-
36
- Cellar.propTypes = {}
@@ -1,5 +1,4 @@
1
1
  import React, { useContext, useState } from 'react'
2
- import { number } from 'prop-types'
3
2
  import Divider from '@mui/material/Divider/index.js'
4
3
  import Card from '@mui/material/Card/index.js'
5
4
  import CardContent from '@mui/material/CardContent/index.js'
@@ -98,8 +97,3 @@ Kegs that contain wine compound in value at a rate of ${WINE_INTEREST_RATE}% for
98
97
  </TabPanel>
99
98
  )
100
99
  }
101
-
102
- CellarInventoryTabPanel.propTypes = {
103
- currentTab: number.isRequired,
104
- index: number.isRequired,
105
- }
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { number } from 'prop-types'
3
2
  import Divider from '@mui/material/Divider/index.js'
4
3
  import Card from '@mui/material/Card/index.js'
5
4
  import CardContent from '@mui/material/CardContent/index.js'
@@ -37,8 +36,3 @@ export const FermentationTabPanel = ({
37
36
  </ul>
38
37
  </TabPanel>
39
38
  )
40
-
41
- FermentationTabPanel.propTypes = {
42
- currentTab: number.isRequired,
43
- index: number.isRequired,
44
- }
@@ -2,7 +2,6 @@ import Button from '@mui/material/Button/index.js'
2
2
  import Card from '@mui/material/Card/index.js'
3
3
  import CardActions from '@mui/material/CardActions/index.js'
4
4
  import CardHeader from '@mui/material/CardHeader/index.js'
5
- import { object } from 'prop-types'
6
5
  import { useContext } from 'react'
7
6
 
8
7
  import { itemsMap } from '../../data/maps.js'
@@ -119,7 +118,3 @@ export function Keg({ keg }: { keg: farmhand.keg }) {
119
118
  </Card>
120
119
  )
121
120
  }
122
-
123
- Keg.propTypes = {
124
- keg: object.isRequired,
125
- }
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { node, number } from 'prop-types'
3
2
 
4
3
  export const TabPanel = (
5
4
  props: React.PropsWithChildren<
@@ -21,12 +20,6 @@ export const TabPanel = (
21
20
  )
22
21
  }
23
22
 
24
- TabPanel.propTypes = {
25
- children: node,
26
- index: number.isRequired,
27
- value: number.isRequired,
28
- }
29
-
30
23
  export const a11yProps = (index: number) => ({
31
24
  id: `cellar-tab-${index}`,
32
25
  'aria-controls': `cellar-tabpanel-${index}`,
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { number } from 'prop-types'
3
2
  import Divider from '@mui/material/Divider/index.js'
4
3
  import Card from '@mui/material/Card/index.js'
5
4
  import CardContent from '@mui/material/CardContent/index.js'
@@ -37,8 +36,3 @@ export const WinemakingTabPanel = ({
37
36
  </ul>
38
37
  </TabPanel>
39
38
  )
40
-
41
- WinemakingTabPanel.propTypes = {
42
- currentTab: number.isRequired,
43
- index: number.isRequired,
44
- }
@@ -1,5 +1,4 @@
1
1
  import React, { memo } from 'react'
2
- import { array, string } from 'prop-types'
3
2
 
4
3
  import FarmhandContext from '../Farmhand/Farmhand.context.js'
5
4
  import Inventory from '../Inventory/index.js'
@@ -61,11 +60,6 @@ export const ContextPane = ({
61
60
  )
62
61
  }
63
62
 
64
- ContextPane.propTypes = {
65
- playerInventory: array.isRequired,
66
- stageFocus: string.isRequired,
67
- }
68
-
69
63
  export default function Consumer() {
70
64
  return (
71
65
  <FarmhandContext.Consumer>
@@ -1,5 +1,4 @@
1
1
  import React, { memo } from 'react'
2
- import { object } from 'prop-types'
3
2
  import Tooltip from '@mui/material/Tooltip/index.js'
4
3
 
5
4
  import { cowColors } from '../../../enums.js'
@@ -48,8 +47,4 @@ const Bloodline = ({
48
47
  </Ul>
49
48
  )
50
49
 
51
- Bloodline.propTypes = {
52
- colorsInBloodline: object.isRequired,
53
- }
54
-
55
50
  export default memo(Bloodline)
@@ -10,7 +10,6 @@ import TextField from '@mui/material/TextField/index.js'
10
10
  import Tooltip from '@mui/material/Tooltip/index.js'
11
11
  import Typography from '@mui/material/Typography/index.js'
12
12
  import classNames from 'classnames'
13
- import { array, bool, func, number, object, string } from 'prop-types'
14
13
  import { useIsMounted } from 'usehooks-ts'
15
14
 
16
15
  import { PURCHASEABLE_COW_PENS } from '../../constants.js'
@@ -374,31 +373,6 @@ export const CowCard = ({
374
373
  )
375
374
  }
376
375
 
377
- CowCard.propTypes = {
378
- allowCustomPeerCowNames: bool.isRequired,
379
- cow: object.isRequired,
380
- cowBreedingPen: object.isRequired,
381
- cowIdOfferedForTrade: string.isRequired,
382
- cowInventory: array.isRequired,
383
- debounced: object,
384
- handleCowAutomaticHugChange: func,
385
- handleCowBreedChange: func,
386
- handleCowHugClick: func,
387
- handleCowNameInputChange: func,
388
- handleCowOfferClick: func,
389
- handleCowPurchaseClick: func,
390
- handleCowWithdrawClick: func,
391
- handleCowSellClick: func,
392
- handleCowTradeClick: func,
393
- playerId: string.isRequired,
394
- inventory: array.isRequired,
395
- isCowOfferedForTradeByPeer: bool,
396
- isOnline: bool.isRequired,
397
- isSelected: bool,
398
- money: number.isRequired,
399
- purchasedCowPen: number.isRequired,
400
- }
401
-
402
376
  export default function Consumer(
403
377
  props: Pick<CowCardProps, 'cow' | 'isCowOfferedForTradeByPeer' | 'isSelected'>
404
378
  ) {
@@ -6,7 +6,6 @@ import FormControlLabel from '@mui/material/FormControlLabel/index.js'
6
6
  import Tooltip from '@mui/material/Tooltip/index.js'
7
7
  import Typography from '@mui/material/Typography/index.js'
8
8
  import classNames from 'classnames'
9
- import { array, bool, func, object, string } from 'prop-types'
10
9
 
11
10
  import { huggingMachine } from '../../../data/items.js'
12
11
  import { genders } from '../../../enums.js'
@@ -37,18 +36,17 @@ const getCowMapById = memoize((cowInventory: farmhand.state['cowInventory']) =>
37
36
  )
38
37
  )
39
38
 
40
- export interface SubheaderProps
41
- extends Pick<
42
- CowCardProps,
43
- | 'cow'
44
- | 'cowBreedingPen'
45
- | 'cowIdOfferedForTrade'
46
- | 'cowInventory'
47
- | 'handleCowAutomaticHugChange'
48
- | 'handleCowBreedChange'
49
- | 'huggingMachinesRemain'
50
- | 'playerId'
51
- > {
39
+ export interface SubheaderProps extends Pick<
40
+ CowCardProps,
41
+ | 'cow'
42
+ | 'cowBreedingPen'
43
+ | 'cowIdOfferedForTrade'
44
+ | 'cowInventory'
45
+ | 'handleCowAutomaticHugChange'
46
+ | 'handleCowBreedChange'
47
+ | 'huggingMachinesRemain'
48
+ | 'playerId'
49
+ > {
52
50
  canCowBeTradedFor: boolean
53
51
  cowValue: number
54
52
  isCowPurchased: boolean
@@ -210,16 +208,3 @@ const Subheader = ({
210
208
  }
211
209
 
212
210
  export default Subheader
213
-
214
- Subheader.propTypes = {
215
- canCowBeTradedFor: bool.isRequired,
216
- cow: object.isRequired,
217
- cowBreedingPen: object.isRequired,
218
- cowIdOfferedForTrade: string.isRequired,
219
- cowInventory: array.isRequired,
220
- handleCowAutomaticHugChange: func,
221
- handleCowBreedChange: func,
222
- huggingMachinesRemain: bool.isRequired,
223
- playerId: string.isRequired,
224
- isCowPurchased: bool,
225
- }
@@ -1,5 +1,4 @@
1
1
  import { Theme } from '@mui/material/styles/index.js'
2
- import { array, bool, func, string } from 'prop-types'
3
2
  import { useEffect } from 'react'
4
3
 
5
4
  import { fillSx } from '../../styles/sx.js'
@@ -10,11 +9,10 @@ import FarmhandContext from '../Farmhand/Farmhand.context.js'
10
9
  import { Cow } from './Cow.js'
11
10
  import { Tumbleweeds } from './Tumbleweeds.js'
12
11
 
13
- export interface CowPenProps
14
- extends Pick<
15
- farmhand.state,
16
- 'allowCustomPeerCowNames' | 'cowInventory' | 'playerId' | 'selectedCowId'
17
- > {
12
+ export interface CowPenProps extends Pick<
13
+ farmhand.state,
14
+ 'allowCustomPeerCowNames' | 'cowInventory' | 'playerId' | 'selectedCowId'
15
+ > {
18
16
  handleCowPenUnmount: () => void
19
17
  handleCowClick: (cow: farmhand.cow) => void
20
18
  }
@@ -170,15 +168,6 @@ export const CowPen = ({
170
168
  )
171
169
  }
172
170
 
173
- CowPen.propTypes = {
174
- allowCustomPeerCowNames: bool.isRequired,
175
- cowInventory: array.isRequired,
176
- handleCowClick: func.isRequired,
177
- handleCowPenUnmount: func.isRequired,
178
- playerId: string.isRequired,
179
- selectedCowId: string.isRequired,
180
- }
181
-
182
171
  export default function Consumer(props: Partial<CowPenProps>) {
183
172
  return (
184
173
  <FarmhandContext.Consumer>