@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.
- package/README.md +1 -0
- package/dist/assets/{index-DNPchtK9.js → index-Dihg7UqQ.js} +15 -15
- package/dist/assets/{index-DNPchtK9.js.map → index-Dihg7UqQ.js.map} +1 -1
- package/dist/index.html +1 -1
- package/dist/service-worker.js +1 -1
- package/dist/service-worker.js.map +1 -1
- package/package.json +4 -5
- package/src/common/services/randomNumber.test.ts +61 -1
- package/src/common/services/randomNumber.ts +36 -1
- package/src/common/utils.ts +22 -13
- package/src/components/AccountingView/AccountingView.tsx +4 -12
- package/src/components/Achievement/Achievement.tsx +0 -13
- package/src/components/AchievementsView/AchievementsView.tsx +2 -7
- package/src/components/AnimatedNumber/AnimatedNumber.tsx +0 -6
- package/src/components/AppBar/AppBar.tsx +0 -10
- package/src/components/Cellar/Cellar.tsx +0 -2
- package/src/components/Cellar/CellarInventoryTabPanel.tsx +0 -6
- package/src/components/Cellar/FermentationTabPanel.tsx +0 -6
- package/src/components/Cellar/Keg.tsx +0 -5
- package/src/components/Cellar/TabPanel/index.tsx +0 -7
- package/src/components/Cellar/WinemakingTabPanel.tsx +0 -6
- package/src/components/ContextPane/ContextPane.tsx +0 -6
- package/src/components/CowCard/Bloodline/Bloodline.tsx +0 -5
- package/src/components/CowCard/CowCard.tsx +0 -26
- package/src/components/CowCard/Subheader/Subheader.tsx +11 -26
- package/src/components/CowPen/CowPen.tsx +4 -15
- package/src/components/CowPenContextMenu/CowPenContextMenu.tsx +0 -16
- package/src/components/CowPenContextMenu/TabPanel/index.tsx +0 -7
- package/src/components/Farmhand/Farmhand.context.tsx +4 -3
- package/src/components/Farmhand/Farmhand.tsx +4 -11
- package/src/components/Farmhand/FarmhandReducers.tsx +1 -2
- package/src/components/Farmhand/FarmhandService.test.ts +6 -9
- package/src/components/Farmhand/hooks/useFarmhandDayAdvancement.ts +2 -6
- package/src/components/Farmhand/hooks/useFarmhandNetwork.test.ts +2 -2
- package/src/components/Farmhand/hooks/useFarmhandNetwork.ts +15 -31
- package/src/components/Farmhand/useFarmhand.test.ts +7 -5
- package/src/components/Farmhand/useFarmhand.ts +12 -13
- package/src/components/FermentationRecipeList/FermentationRecipe.tsx +0 -5
- package/src/components/FermentationRecipeList/FermentationRecipeList.test.tsx +10 -11
- package/src/components/FermentationRecipeList/FermentationRecipeList.tsx +2 -3
- package/src/components/Field/Field.tsx +13 -48
- package/src/components/Forest/Forest.tsx +10 -7
- package/src/components/ForestPlot/ForestPlot.tsx +5 -5
- package/src/components/ForestQuickSelect/ForestQuickSelect.tsx +0 -12
- package/src/components/Home/Home.tsx +0 -6
- package/src/components/Home/SnowBackground.tsx +1 -1
- package/src/components/IngredientsList/IngredientsList.tsx +0 -7
- package/src/components/Inventory/Inventory.test.tsx +4 -4
- package/src/components/Inventory/Inventory.tsx +3 -11
- package/src/components/Item/Item.tsx +0 -21
- package/src/components/ItemList/ItemList.tsx +4 -11
- package/src/components/KeybindingsView/KeybindingsView.tsx +0 -2
- package/src/components/LogView/LogView.tsx +0 -6
- package/src/components/Navigation/DayAndProgressContainer.tsx +0 -7
- package/src/components/Navigation/Navigation.tsx +0 -23
- package/src/components/NotificationSystem/NotificationSystem.test.tsx +4 -1
- package/src/components/NotificationSystem/NotificationSystem.tsx +5 -12
- package/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.test.tsx +2 -2
- package/src/components/OnlinePeersView/OnlinePeer/OnlinePeer.tsx +0 -11
- package/src/components/OnlinePeersView/OnlinePeersView.tsx +0 -10
- package/src/components/Plot/Plot.tsx +3 -3
- package/src/components/PriceEventView/PriceEventView.tsx +0 -6
- package/src/components/ProgressBar/ProgressBar.tsx +0 -5
- package/src/components/QuantityInput/QuantityInput.tsx +0 -9
- package/src/components/QuickSelect/QuickSelect.tsx +0 -10
- package/src/components/Recipe/Recipe.tsx +0 -9
- package/src/components/RecipeList/RecipeList.tsx +0 -6
- package/src/components/SearchBar/SearchBar.tsx +0 -6
- package/src/components/SettingsView/RandomSeedInput.test.tsx +2 -4
- package/src/components/SettingsView/RandomSeedInput.tsx +5 -8
- package/src/components/SettingsView/SettingsView.test.tsx +21 -0
- package/src/components/SettingsView/SettingsView.tsx +24 -16
- package/src/components/Shop/Shop.tsx +0 -20
- package/src/components/Shop/TabPanel/index.tsx +0 -7
- package/src/components/Stage/Stage.tsx +11 -20
- package/src/components/StatsView/StatsView.tsx +0 -15
- package/src/components/TierPurchase/TierPurchase.tsx +0 -12
- package/src/components/Toolbelt/Toolbelt.tsx +5 -10
- package/src/components/UpgradePurchase/UpgradePurchase.tsx +0 -10
- package/src/components/WineRecipeList/WineRecipe.test.tsx +2 -2
- package/src/components/WineRecipeList/WineRecipe.tsx +0 -5
- package/src/components/WineRecipeList/WineRecipeList.tsx +2 -3
- package/src/components/Workshop/ForgeTabPanel.tsx +0 -8
- package/src/components/Workshop/KitchenTabPanel.tsx +0 -7
- package/src/components/Workshop/RecyclingTabPanel.tsx +0 -7
- package/src/components/Workshop/TabPanel/index.tsx +0 -7
- package/src/components/Workshop/WoodChipperTabPanel.tsx +0 -7
- package/src/components/Workshop/Workshop.tsx +0 -9
- package/src/components/Workshop/getUpgradesAvailable.ts +1 -1
- package/src/constants.ts +18 -13
- package/src/data/crops/grape.ts +13 -15
- package/src/data/maps.test.ts +3 -4
- package/src/data/maps.ts +27 -29
- package/src/enums.ts +47 -19
- package/src/factories/OreFactory.ts +2 -1
- package/src/farmhand.d.ts +2 -3
- package/src/game-logic/reducers/applyPrecipitation.ts +4 -4
- package/src/game-logic/reducers/generatePriceEvents.ts +6 -3
- package/src/game-logic/reducers/harvestForestPlot.ts +1 -2
- package/src/game-logic/reducers/makeFermentationRecipe.ts +2 -3
- package/src/game-logic/reducers/minePlot.test.ts +2 -2
- package/src/game-logic/reducers/minePlot.ts +1 -1
- package/src/game-logic/reducers/processCowFertilizerProduction.test.ts +16 -14
- package/src/game-logic/reducers/processFeedingCows.test.ts +2 -5
- package/src/game-logic/reducers/purchaseCow.ts +2 -7
- package/src/game-logic/reducers/updateAchievements.ts +1 -1
- package/src/game-logic/reducers/updateFinancialRecords.test.ts +21 -25
- package/src/game-logic/reducers/updateFinancialRecords.ts +2 -5
- package/src/game-logic/reducers/updateInventoryRecordsForNextDay.test.ts +12 -14
- package/src/handlers/ui-events.test.ts +44 -42
- package/src/handlers/ui-events.tsx +10 -0
- package/src/hooks/usePrefersReducedMotion.ts +10 -6
- package/src/mui-theme.ts +4 -3
- package/src/scripts/generate-crop-table.ts +3 -2
- package/src/styles/sx.ts +29 -25
- package/src/test-utils/index.ts +1 -0
- package/src/utils/chooseRandom.ts +4 -1
- package/src/utils/chooseRandomIndex.ts +6 -2
- package/src/utils/getCowImage.ts +22 -16
- package/src/utils/getMaxYieldOfFermentationRecipe.ts +2 -2
- package/src/utils/getPeerMetadata.ts +1 -1
- package/src/utils/getPlotImage.test.ts +14 -14
- package/src/utils/getPlotImage.ts +10 -10
- package/src/utils/getRandomUnlockedCrop.ts +5 -3
- package/src/utils/getRandomizedLifespan.test.ts +2 -8
- package/src/utils/getValueAdjustmentStream.test.ts +9 -0
- package/src/utils/getValueAdjustmentStream.ts +5 -0
- package/src/utils/isItemAFarmProduct.ts +2 -2
- package/src/utils/shouldPrecipitateToday.ts +3 -1
- package/src/utils/shouldStormToday.ts +2 -1
- package/src/utils/transformStateDataForImport.ts +3 -5
|
@@ -6,7 +6,6 @@ import Select from '@mui/material/Select/index.js'
|
|
|
6
6
|
import Tab from '@mui/material/Tab/index.js'
|
|
7
7
|
import Tabs from '@mui/material/Tabs/index.js'
|
|
8
8
|
import sortBy from 'lodash.sortby'
|
|
9
|
-
import { array, func, number, object, string } from 'prop-types'
|
|
10
9
|
import { useEffect, useState } from 'react'
|
|
11
10
|
|
|
12
11
|
import { PURCHASEABLE_COW_PENS } from '../../constants.js'
|
|
@@ -327,21 +326,6 @@ export const CowPenContextMenu = ({
|
|
|
327
326
|
)
|
|
328
327
|
}
|
|
329
328
|
|
|
330
|
-
CowPenContextMenu.propTypes = {
|
|
331
|
-
cowForSale: object.isRequired,
|
|
332
|
-
cowInventory: array.isRequired,
|
|
333
|
-
handleCowAutomaticHugChange: func.isRequired,
|
|
334
|
-
handleCowBreedChange: func.isRequired,
|
|
335
|
-
handleCowHugClick: func.isRequired,
|
|
336
|
-
handleCowNameInputChange: func.isRequired,
|
|
337
|
-
handleCowOfferClick: func.isRequired,
|
|
338
|
-
handleCowSelect: func.isRequired,
|
|
339
|
-
handleCowSellClick: func.isRequired,
|
|
340
|
-
handleCowWithdrawClick: func.isRequired,
|
|
341
|
-
purchasedCowPen: number.isRequired,
|
|
342
|
-
selectedCowId: string.isRequired,
|
|
343
|
-
}
|
|
344
|
-
|
|
345
329
|
export default function Consumer() {
|
|
346
330
|
return (
|
|
347
331
|
<FarmhandContext.Consumer>
|
|
@@ -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: `cow-context-tab-${index}`,
|
|
32
25
|
'aria-controls': `cow-context-tabpanel-${index}`,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import uiEventHandlers from '../../handlers/ui-events.js'
|
|
13
13
|
import { generateCow } from '../../utils/generateCow.js'
|
|
14
14
|
import { scarecrow } from '../../data/items.js'
|
|
15
|
+
import { NOTIFICATION_DURATION_DEFAULT } from '../../constants.js'
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* A utility type that transforms a type containing functions with an explicit `this` parameter
|
|
@@ -159,6 +160,7 @@ export const createContextData = (): ContextData => {
|
|
|
159
160
|
latestNotification: null,
|
|
160
161
|
newDayNotifications: [],
|
|
161
162
|
notificationLog: [],
|
|
163
|
+
notificationDuration: NOTIFICATION_DURATION_DEFAULT,
|
|
162
164
|
peers: {}, // empty object for now
|
|
163
165
|
peerRoom: null,
|
|
164
166
|
pendingPeerMessages: [],
|
|
@@ -217,8 +219,7 @@ export const createContextData = (): ContextData => {
|
|
|
217
219
|
}
|
|
218
220
|
}
|
|
219
221
|
|
|
220
|
-
const FarmhandContext: import('react').Context<ContextData> =
|
|
221
|
-
ContextData
|
|
222
|
-
>(createContextData())
|
|
222
|
+
const FarmhandContext: import('react').Context<ContextData> =
|
|
223
|
+
createContext<ContextData>(createContextData())
|
|
223
224
|
|
|
224
225
|
export default FarmhandContext
|
|
@@ -11,7 +11,6 @@ import Tooltip from '@mui/material/Tooltip/index.js'
|
|
|
11
11
|
import classNames from 'classnames'
|
|
12
12
|
import localforage from 'localforage'
|
|
13
13
|
import { SnackbarProvider } from 'notistack'
|
|
14
|
-
import { object } from 'prop-types'
|
|
15
14
|
import { GlobalHotKeys } from 'react-hotkeys'
|
|
16
15
|
import { Redirect } from 'react-router-dom'
|
|
17
16
|
|
|
@@ -103,9 +102,10 @@ const Farmhand = (props: FarmhandProps) => {
|
|
|
103
102
|
pointerEvents: 'none',
|
|
104
103
|
},
|
|
105
104
|
}),
|
|
106
|
-
[`@media (min-width: ${breakpoints.smallPhone}px) and (orientation: landscape)`]:
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
[`@media (min-width: ${breakpoints.smallPhone}px) and (orientation: landscape)`]:
|
|
106
|
+
{
|
|
107
|
+
paddingTop: '3em',
|
|
108
|
+
},
|
|
109
109
|
[`@media (min-width: ${breakpoints.sm}px)`]: {
|
|
110
110
|
paddingTop: '4em',
|
|
111
111
|
},
|
|
@@ -282,11 +282,4 @@ Farmhand.defaultProps = {
|
|
|
282
282
|
match: { path: '', params: {} },
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
Farmhand.propTypes = {
|
|
286
|
-
features: object,
|
|
287
|
-
history: object,
|
|
288
|
-
location: object,
|
|
289
|
-
match: object.isRequired,
|
|
290
|
-
}
|
|
291
|
-
|
|
292
285
|
export default Farmhand
|
|
@@ -193,8 +193,7 @@ export class FarmhandReducers extends Component<FarmhandProps, FarmhandState> {
|
|
|
193
193
|
|
|
194
194
|
for (const reducerName of reducerNames) {
|
|
195
195
|
const reducer = reducers[reducerName as keyof typeof reducers] as
|
|
196
|
-
|
|
197
|
-
| undefined
|
|
196
|
+
((state: farmhand.state, ...args: any[]) => farmhand.state) | undefined
|
|
198
197
|
|
|
199
198
|
if (!reducer) {
|
|
200
199
|
if (import.meta.env?.MODE === 'development') {
|
|
@@ -50,9 +50,8 @@ describe('FarmhandService', () => {
|
|
|
50
50
|
{ id: 'carrot-seed', quantity: 1 },
|
|
51
51
|
{ id: 'weed', quantity: 1 },
|
|
52
52
|
]
|
|
53
|
-
const plantableCropInventory =
|
|
54
|
-
inventory
|
|
55
|
-
)
|
|
53
|
+
const plantableCropInventory =
|
|
54
|
+
FarmhandService.getPlantableCropInventory(inventory)
|
|
56
55
|
|
|
57
56
|
expect(plantableCropInventory).toEqual([{ ...carrotSeed, quantity: 1 }])
|
|
58
57
|
})
|
|
@@ -64,9 +63,8 @@ describe('FarmhandService', () => {
|
|
|
64
63
|
{ id: 'apple-sapling', quantity: 1 },
|
|
65
64
|
{ id: 'weed', quantity: 1 },
|
|
66
65
|
]
|
|
67
|
-
const plantableTreeInventory =
|
|
68
|
-
inventory
|
|
69
|
-
)
|
|
66
|
+
const plantableTreeInventory =
|
|
67
|
+
FarmhandService.getPlantableTreeInventory(inventory)
|
|
70
68
|
|
|
71
69
|
expect(plantableTreeInventory).toEqual([{ ...appleSapling, quantity: 1 }])
|
|
72
70
|
})
|
|
@@ -76,9 +74,8 @@ describe('FarmhandService', () => {
|
|
|
76
74
|
{ id: 'apple-sapling', quantity: 1 },
|
|
77
75
|
{ id: 'not-a-real-item-id', quantity: 1 },
|
|
78
76
|
]
|
|
79
|
-
const plantableTreeInventory =
|
|
80
|
-
inventory
|
|
81
|
-
)
|
|
77
|
+
const plantableTreeInventory =
|
|
78
|
+
FarmhandService.getPlantableTreeInventory(inventory)
|
|
82
79
|
|
|
83
80
|
expect(plantableTreeInventory).toEqual([{ ...appleSapling, quantity: 1 }])
|
|
84
81
|
})
|
|
@@ -50,12 +50,8 @@ export const useFarmhandDayAdvancement = (
|
|
|
50
50
|
let serverValueAdjustments: Record<string, number> | undefined
|
|
51
51
|
|
|
52
52
|
if (stateRef.current.isOnline) {
|
|
53
|
-
const {
|
|
54
|
-
|
|
55
|
-
room,
|
|
56
|
-
todaysPurchases,
|
|
57
|
-
todaysStartingInventory,
|
|
58
|
-
} = stateRef.current
|
|
53
|
+
const { inventory, room, todaysPurchases, todaysStartingInventory } =
|
|
54
|
+
stateRef.current
|
|
59
55
|
|
|
60
56
|
const positions = computeMarketPositions(
|
|
61
57
|
todaysStartingInventory,
|
|
@@ -58,14 +58,14 @@ const setup = () => {
|
|
|
58
58
|
const sendCowTradeRequest = vitest.fn()
|
|
59
59
|
const showNotification = vitest.fn()
|
|
60
60
|
|
|
61
|
-
const fakeState =
|
|
61
|
+
const fakeState = {
|
|
62
62
|
peers: {
|
|
63
63
|
[peerId]: { playerId: ownerId } as farmhand.peerMetadata,
|
|
64
64
|
},
|
|
65
65
|
sendCowTradeRequest,
|
|
66
66
|
cowInventory: [cowOfferedForTrade],
|
|
67
67
|
cowIdOfferedForTrade: cowOfferedForTrade.id,
|
|
68
|
-
} as unknown
|
|
68
|
+
} as unknown as farmhand.state
|
|
69
69
|
|
|
70
70
|
const instanceProxyRef: React.MutableRefObject<any> = {
|
|
71
71
|
current: { state: { ...fakeState } },
|
|
@@ -83,12 +83,8 @@ export const useFarmhandNetwork = (
|
|
|
83
83
|
const tradeForPeerCow = useCallback(
|
|
84
84
|
(peerPlayerCow: farmhand.cow) => {
|
|
85
85
|
const latestState = instanceProxyRef.current?.state ?? state
|
|
86
|
-
const {
|
|
87
|
-
|
|
88
|
-
cowInventory,
|
|
89
|
-
peers,
|
|
90
|
-
sendCowTradeRequest,
|
|
91
|
-
} = latestState
|
|
86
|
+
const { cowIdOfferedForTrade, cowInventory, peers, sendCowTradeRequest } =
|
|
87
|
+
latestState
|
|
92
88
|
|
|
93
89
|
if (!sendCowTradeRequest) return
|
|
94
90
|
|
|
@@ -139,7 +135,7 @@ export const useFarmhandNetwork = (
|
|
|
139
135
|
|
|
140
136
|
setState(previous => ({
|
|
141
137
|
...previous,
|
|
142
|
-
cowTradeTimeoutId:
|
|
138
|
+
cowTradeTimeoutId: cowTradeTimeoutId as unknown as number,
|
|
143
139
|
isAwaitingCowTradeRequest: true,
|
|
144
140
|
}))
|
|
145
141
|
},
|
|
@@ -153,26 +149,20 @@ export const useFarmhandNetwork = (
|
|
|
153
149
|
return previous
|
|
154
150
|
})
|
|
155
151
|
|
|
156
|
-
const timeoutId =
|
|
152
|
+
const timeoutId = window.setTimeout(() => {
|
|
157
153
|
setState((previous: farmhand.state) => ({
|
|
158
154
|
...previous,
|
|
159
155
|
money: moneyTotal(previous.money, previous.activePlayers ?? 0),
|
|
160
156
|
}))
|
|
161
157
|
scheduleHeartbeat()
|
|
162
|
-
}, HEARTBEAT_INTERVAL_PERIOD) as unknown
|
|
158
|
+
}, HEARTBEAT_INTERVAL_PERIOD) as unknown as number
|
|
163
159
|
|
|
164
160
|
setState(previous => ({ ...previous, heartbeatTimeoutId: timeoutId }))
|
|
165
161
|
}, [setState])
|
|
166
162
|
|
|
167
163
|
const syncToRoom = useCallback(async () => {
|
|
168
|
-
const {
|
|
169
|
-
|
|
170
|
-
priceCrashes,
|
|
171
|
-
priceSurges,
|
|
172
|
-
room,
|
|
173
|
-
playerId,
|
|
174
|
-
peerRoom,
|
|
175
|
-
} = instanceProxyRef.current.state
|
|
164
|
+
const { isOnline, priceCrashes, priceSurges, room, playerId, peerRoom } =
|
|
165
|
+
instanceProxyRef.current.state
|
|
176
166
|
|
|
177
167
|
if (!isOnline) return
|
|
178
168
|
|
|
@@ -308,27 +298,22 @@ export const useFarmhandNetwork = (
|
|
|
308
298
|
boundReducersRef.current.removePeer(id)
|
|
309
299
|
)
|
|
310
300
|
|
|
311
|
-
const [
|
|
312
|
-
|
|
313
|
-
getPeerMetadataFunc,
|
|
314
|
-
] = statePeerRoom.makeAction('peerMetadata')
|
|
301
|
+
const [sendPeerMetadata, getPeerMetadataFunc] =
|
|
302
|
+
statePeerRoom.makeAction('peerMetadata')
|
|
315
303
|
|
|
316
304
|
getPeerMetadataFunc((...args: any[]) =>
|
|
317
305
|
handlePeerMetadataRequest(instanceProxyRef.current, args[0], args[1])
|
|
318
306
|
)
|
|
319
307
|
|
|
320
|
-
const [
|
|
321
|
-
|
|
322
|
-
getCowTradeRequest,
|
|
323
|
-
] = statePeerRoom.makeAction('cowTrade')
|
|
308
|
+
const [sendCowTradeRequest, getCowTradeRequest] =
|
|
309
|
+
statePeerRoom.makeAction('cowTrade')
|
|
324
310
|
|
|
325
311
|
getCowTradeRequest((...args: any[]) =>
|
|
326
312
|
handleCowTradeRequest(instanceProxyRef.current, args[0], args[1])
|
|
327
313
|
)
|
|
328
314
|
|
|
329
|
-
const [sendCowAccept, getCowAccept] =
|
|
330
|
-
'cowAccept'
|
|
331
|
-
)
|
|
315
|
+
const [sendCowAccept, getCowAccept] =
|
|
316
|
+
statePeerRoom.makeAction('cowAccept')
|
|
332
317
|
|
|
333
318
|
getCowAccept((...args: any[]) =>
|
|
334
319
|
handleCowTradeRequestAccept(
|
|
@@ -338,9 +323,8 @@ export const useFarmhandNetwork = (
|
|
|
338
323
|
)
|
|
339
324
|
)
|
|
340
325
|
|
|
341
|
-
const [sendCowReject, getCowReject] =
|
|
342
|
-
'cowReject'
|
|
343
|
-
)
|
|
326
|
+
const [sendCowReject, getCowReject] =
|
|
327
|
+
statePeerRoom.makeAction('cowReject')
|
|
344
328
|
|
|
345
329
|
getCowReject((...args: any[]) =>
|
|
346
330
|
handleCowTradeRequestReject(instanceProxyRef.current, args[0])
|
|
@@ -21,13 +21,15 @@ const setWindowHeight = (height: number) => {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const getIsMenuOpen = () =>
|
|
24
|
-
(
|
|
25
|
-
.
|
|
24
|
+
(window as unknown as { farmhand: { state: farmhand.state } }).farmhand.state
|
|
25
|
+
.isMenuOpen
|
|
26
26
|
|
|
27
27
|
const setIsMenuOpen = (isMenuOpen: boolean) =>
|
|
28
|
-
(
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
(
|
|
29
|
+
window as unknown as {
|
|
30
|
+
farmhand: { setState: (state: Partial<farmhand.state>) => void }
|
|
31
|
+
}
|
|
32
|
+
).farmhand.setState({ isMenuOpen })
|
|
31
33
|
|
|
32
34
|
// Simulates a sidebar input having focus, e.g. mid-typing, without depending
|
|
33
35
|
// on any particular Farmhand-rendered input existing in the current view.
|
|
@@ -5,10 +5,12 @@ import { v4 as uuid } from 'uuid'
|
|
|
5
5
|
|
|
6
6
|
import * as reducers from '../../game-logic/reducers/index.js'
|
|
7
7
|
import eventHandlers from '../../handlers/ui-events.js'
|
|
8
|
+
import { randomNumberService } from '../../common/services/randomNumber.js'
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
11
|
DEFAULT_ROOM,
|
|
11
12
|
INITIAL_STORAGE_LIMIT,
|
|
13
|
+
NOTIFICATION_DURATION_DEFAULT,
|
|
12
14
|
STAGE_TITLE_MAP,
|
|
13
15
|
STANDARD_LOAN_AMOUNT,
|
|
14
16
|
} from '../../constants.js'
|
|
@@ -16,6 +18,7 @@ import { scarecrow } from '../../data/items.js'
|
|
|
16
18
|
import {
|
|
17
19
|
dialogView,
|
|
18
20
|
fieldMode,
|
|
21
|
+
randomStream,
|
|
19
22
|
stageFocusType,
|
|
20
23
|
toolLevel,
|
|
21
24
|
toolType,
|
|
@@ -125,6 +128,7 @@ export const useFarmhand = (props: FarmhandProps) => {
|
|
|
125
128
|
latestNotification: null,
|
|
126
129
|
newDayNotifications: [],
|
|
127
130
|
notificationLog: [],
|
|
131
|
+
notificationDuration: NOTIFICATION_DURATION_DEFAULT,
|
|
128
132
|
peers: {},
|
|
129
133
|
peerRoom: null,
|
|
130
134
|
pendingPeerMessages: [],
|
|
@@ -252,12 +256,8 @@ export const useFarmhand = (props: FarmhandProps) => {
|
|
|
252
256
|
const instanceProxyRef = useRef<any>(null)
|
|
253
257
|
|
|
254
258
|
// Call sub-hooks
|
|
255
|
-
const {
|
|
256
|
-
|
|
257
|
-
closeDialogView,
|
|
258
|
-
focusNextView,
|
|
259
|
-
focusPreviousView,
|
|
260
|
-
} = useFarmhandNavigation(setState, viewList)
|
|
259
|
+
const { openDialogView, closeDialogView, focusNextView, focusPreviousView } =
|
|
260
|
+
useFarmhandNavigation(setState, viewList)
|
|
261
261
|
|
|
262
262
|
// Mirrors the current view into the URL hash's `view` query param, using
|
|
263
263
|
// pushState (not replaceState) so each in-app view change is a real,
|
|
@@ -351,15 +351,15 @@ export const useFarmhand = (props: FarmhandProps) => {
|
|
|
351
351
|
nextDayStateRef
|
|
352
352
|
)
|
|
353
353
|
|
|
354
|
-
const {
|
|
355
|
-
|
|
356
|
-
showRecipeLearnedNotifications,
|
|
357
|
-
} = useFarmhandNotifications(state, boundReducersRef)
|
|
354
|
+
const { showInventoryFullNotifications, showRecipeLearnedNotifications } =
|
|
355
|
+
useFarmhandNotifications(state, boundReducersRef)
|
|
358
356
|
|
|
359
357
|
// Instance proxy to mimic the legacy class "this" so ui-events.tsx can run unmodified
|
|
360
358
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
361
359
|
const instanceProxy = useMemo(
|
|
362
360
|
() => ({
|
|
361
|
+
queueRandomNumbers: (stream: randomStream, numbers: number[]) =>
|
|
362
|
+
randomNumberService.queueRandomNumbers(stream, numbers),
|
|
363
363
|
state,
|
|
364
364
|
props,
|
|
365
365
|
viewTitle,
|
|
@@ -582,9 +582,8 @@ export const useFarmhand = (props: FarmhandProps) => {
|
|
|
582
582
|
...persistedState,
|
|
583
583
|
})
|
|
584
584
|
const { isCombineEnabled, newDayNotifications } = sanitizedState
|
|
585
|
-
const restoredStageFocus =
|
|
586
|
-
sanitizedState
|
|
587
|
-
)
|
|
585
|
+
const restoredStageFocus =
|
|
586
|
+
getValidatedStageFocusFromHash(sanitizedState)
|
|
588
587
|
|
|
589
588
|
setState(previous => ({
|
|
590
589
|
...previous,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React, { useContext, useEffect, useState } from 'react'
|
|
2
|
-
import { object } from 'prop-types'
|
|
3
2
|
import Card from '@mui/material/Card/index.js'
|
|
4
3
|
import CardHeader from '@mui/material/CardHeader/index.js'
|
|
5
4
|
import CardActions from '@mui/material/CardActions/index.js'
|
|
@@ -141,7 +140,3 @@ export const FermentationRecipe = ({ item }: { item: farmhand.item }) => {
|
|
|
141
140
|
</Card>
|
|
142
141
|
)
|
|
143
142
|
}
|
|
144
|
-
|
|
145
|
-
FermentationRecipe.propTypes = {
|
|
146
|
-
item: object.isRequired,
|
|
147
|
-
}
|
|
@@ -30,7 +30,8 @@ const FermentationRecipeListStub = ({
|
|
|
30
30
|
} = {}) => {
|
|
31
31
|
const contextValue = createContextData()
|
|
32
32
|
|
|
33
|
-
contextValue.gameState.levelEntitlements =
|
|
33
|
+
contextValue.gameState.levelEntitlements =
|
|
34
|
+
levelEntitlements as farmhand.levelEntitlements
|
|
34
35
|
return (
|
|
35
36
|
<FarmhandContext.Provider value={contextValue}>
|
|
36
37
|
<FermentationRecipeList />
|
|
@@ -55,9 +56,8 @@ describe('FermentationRecipeList', () => {
|
|
|
55
56
|
|
|
56
57
|
render(<FermentationRecipeListStub levelEntitlements={levelEntitlements} />)
|
|
57
58
|
|
|
58
|
-
const cropsAvailableToFerment =
|
|
59
|
-
levelEntitlements
|
|
60
|
-
)
|
|
59
|
+
const cropsAvailableToFerment =
|
|
60
|
+
getCropsAvailableToFerment(levelEntitlements)
|
|
61
61
|
|
|
62
62
|
const header = screen.getByText(
|
|
63
63
|
`Available Fermentation Recipes (${cropsAvailableToFerment.length} / ${totalFermentableItems})`
|
|
@@ -68,9 +68,8 @@ describe('FermentationRecipeList', () => {
|
|
|
68
68
|
|
|
69
69
|
test('filters recipes based on search query', async () => {
|
|
70
70
|
const levelEntitlements = getLevelEntitlements(100)
|
|
71
|
-
const cropsAvailableToFerment =
|
|
72
|
-
levelEntitlements
|
|
73
|
-
)
|
|
71
|
+
const cropsAvailableToFerment =
|
|
72
|
+
getCropsAvailableToFerment(levelEntitlements)
|
|
74
73
|
|
|
75
74
|
render(<FermentationRecipeListStub levelEntitlements={levelEntitlements} />)
|
|
76
75
|
|
|
@@ -84,7 +83,8 @@ describe('FermentationRecipeList', () => {
|
|
|
84
83
|
|
|
85
84
|
const filteredCrops = cropsAvailableToFerment.filter(item => {
|
|
86
85
|
const itemWithName = item as farmhand.item & { name: string }
|
|
87
|
-
const fermentationRecipeName =
|
|
86
|
+
const fermentationRecipeName =
|
|
87
|
+
`Fermented ${itemWithName.name}`.toLowerCase()
|
|
88
88
|
|
|
89
89
|
return (
|
|
90
90
|
fermentationRecipeName.includes('apple') ||
|
|
@@ -113,9 +113,8 @@ describe('FermentationRecipeList', () => {
|
|
|
113
113
|
|
|
114
114
|
test('handles empty search query', async () => {
|
|
115
115
|
const levelEntitlements = getLevelEntitlements(100)
|
|
116
|
-
const cropsAvailableToFerment =
|
|
117
|
-
levelEntitlements
|
|
118
|
-
)
|
|
116
|
+
const cropsAvailableToFerment =
|
|
117
|
+
getCropsAvailableToFerment(levelEntitlements)
|
|
119
118
|
|
|
120
119
|
render(<FermentationRecipeListStub levelEntitlements={levelEntitlements} />)
|
|
121
120
|
|
|
@@ -28,7 +28,8 @@ export const FermentationRecipeList = () => {
|
|
|
28
28
|
|
|
29
29
|
const filteredCrops = cropsAvailableToFerment.filter(item => {
|
|
30
30
|
const itemWithName = item as farmhand.item & { name: string }
|
|
31
|
-
const fermentationRecipeName =
|
|
31
|
+
const fermentationRecipeName =
|
|
32
|
+
`${FERMENTED_CROP_NAME}${itemWithName.name}`.toLowerCase()
|
|
32
33
|
|
|
33
34
|
return searchTerms.every(
|
|
34
35
|
term =>
|
|
@@ -63,5 +64,3 @@ export const FermentationRecipeList = () => {
|
|
|
63
64
|
</>
|
|
64
65
|
)
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
-
FermentationRecipeList.propTypes = {}
|
|
@@ -10,7 +10,6 @@ import Switch from '@mui/material/Switch/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, element, func, number, object, string } from 'prop-types'
|
|
14
13
|
import React, { memo, useEffect, useState } from 'react'
|
|
15
14
|
import { GlobalHotKeys } from 'react-hotkeys'
|
|
16
15
|
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch'
|
|
@@ -271,10 +270,6 @@ export const FieldContentWrapper = ({
|
|
|
271
270
|
)
|
|
272
271
|
}
|
|
273
272
|
|
|
274
|
-
FieldContentWrapper.propTypes = {
|
|
275
|
-
fieldContent: element.isRequired,
|
|
276
|
-
}
|
|
277
|
-
|
|
278
273
|
export const FieldContent = ({
|
|
279
274
|
columns = 0,
|
|
280
275
|
experience,
|
|
@@ -339,20 +334,6 @@ export const FieldContent = ({
|
|
|
339
334
|
</>
|
|
340
335
|
)
|
|
341
336
|
|
|
342
|
-
FieldContent.propTypes = {
|
|
343
|
-
columns: number.isRequired,
|
|
344
|
-
experience: number.isRequired,
|
|
345
|
-
field: array.isRequired,
|
|
346
|
-
fieldMode: string.isRequired,
|
|
347
|
-
handleCombineEnabledChange: func.isRequired,
|
|
348
|
-
hoveredPlot: object.isRequired,
|
|
349
|
-
hoveredPlotRangeSize: number.isRequired,
|
|
350
|
-
isCombineEnabled: bool.isRequired,
|
|
351
|
-
purchasedCombine: number.isRequired,
|
|
352
|
-
rows: number.isRequired,
|
|
353
|
-
setHoveredPlot: func.isRequired,
|
|
354
|
-
}
|
|
355
|
-
|
|
356
337
|
const adjustableRangeFieldModes = new Set<string>([
|
|
357
338
|
CLEANUP,
|
|
358
339
|
FERTILIZE,
|
|
@@ -597,10 +578,11 @@ export const Field = (props: FieldProps) => {
|
|
|
597
578
|
backgroundColor: colorYellow,
|
|
598
579
|
cursor: 'pointer',
|
|
599
580
|
},
|
|
600
|
-
'&.harvest-mode.is-inventory-full .Plot.crop.can-be-harvested, &.cleanup-mode.is-inventory-full .Plot.is-replantable':
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
581
|
+
'&.harvest-mode.is-inventory-full .Plot.crop.can-be-harvested, &.cleanup-mode.is-inventory-full .Plot.is-replantable':
|
|
582
|
+
{
|
|
583
|
+
backgroundColor: colorRedDanger,
|
|
584
|
+
cursor: 'not-allowed',
|
|
585
|
+
},
|
|
604
586
|
'&.cleanup-mode .Plot.is-replantable': {
|
|
605
587
|
backgroundColor: colorGreenOk,
|
|
606
588
|
cursor: 'pointer',
|
|
@@ -609,14 +591,15 @@ export const Field = (props: FieldProps) => {
|
|
|
609
591
|
backgroundColor: colorGreenOk,
|
|
610
592
|
cursor: 'auto',
|
|
611
593
|
},
|
|
612
|
-
'&.set-sprinkler-mode:hover .Plot:hover, &.set-scarecrow-mode:hover .Plot:hover, &.set-lightning-rod-mode:hover .Plot:hover':
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
594
|
+
'&.set-sprinkler-mode:hover .Plot:hover, &.set-scarecrow-mode:hover .Plot:hover, &.set-lightning-rod-mode:hover .Plot:hover':
|
|
595
|
+
{
|
|
596
|
+
'&.is-empty img': { cursor: 'pointer', opacity: 0.5 },
|
|
597
|
+
'&:not(.is-empty)': {
|
|
598
|
+
backgroundColor: colorRedDanger,
|
|
599
|
+
backgroundImage: 'none',
|
|
600
|
+
cursor: 'not-allowed',
|
|
601
|
+
},
|
|
618
602
|
},
|
|
619
|
-
},
|
|
620
603
|
'&.set-sprinkler-mode:hover .Plot:hover.is-empty img': {
|
|
621
604
|
backgroundImage: `url(${sprinklerImg})`,
|
|
622
605
|
},
|
|
@@ -699,24 +682,6 @@ export const Field = (props: FieldProps) => {
|
|
|
699
682
|
)
|
|
700
683
|
}
|
|
701
684
|
|
|
702
|
-
Field.propTypes = {
|
|
703
|
-
columns: number.isRequired,
|
|
704
|
-
experience: number.isRequired,
|
|
705
|
-
field: array.isRequired,
|
|
706
|
-
fieldMode: string.isRequired,
|
|
707
|
-
handleCombineEnabledChange: func.isRequired,
|
|
708
|
-
handleFieldActionRangeChange: func.isRequired,
|
|
709
|
-
hoveredPlotRangeSize: number.isRequired,
|
|
710
|
-
inventory: array.isRequired,
|
|
711
|
-
inventoryLimit: number.isRequired,
|
|
712
|
-
isCombineEnabled: bool.isRequired,
|
|
713
|
-
isMenuOpen: bool,
|
|
714
|
-
purchasedCombine: number.isRequired,
|
|
715
|
-
purchasedField: number.isRequired,
|
|
716
|
-
rows: number.isRequired,
|
|
717
|
-
selectedItemId: string.isRequired,
|
|
718
|
-
}
|
|
719
|
-
|
|
720
685
|
export default function Consumer(props: Partial<FieldProps>) {
|
|
721
686
|
return (
|
|
722
687
|
<FarmhandContext.Consumer>
|
|
@@ -41,8 +41,9 @@ export const Forest = () => {
|
|
|
41
41
|
// Also caps by available width (2 * columns "slots": each column, each
|
|
42
42
|
// gap, and one slot split across the two side paddings) so columnGap and
|
|
43
43
|
// padding below - set to this same value - shrink along with the columns.
|
|
44
|
-
const maxPlotSize = `max(${MIN_PLOT_SIZE}, min(160px, calc((100vh - ${CHROME_HEIGHT_PX}px) / ${
|
|
45
|
-
|
|
44
|
+
const maxPlotSize = `max(${MIN_PLOT_SIZE}, min(160px, calc((100vh - ${CHROME_HEIGHT_PX}px) / ${
|
|
45
|
+
rows + OVERHANG_ROW_MULTIPLE
|
|
46
|
+
}), calc(100% / ${2 * Math.max(columns, 1)})))`
|
|
46
47
|
|
|
47
48
|
// The reserve itself, in the same unit as a plot. Applying this as
|
|
48
49
|
// one-sided paddingTop on .Forest (below) shifts the flexbox
|
|
@@ -80,17 +81,19 @@ export const Forest = () => {
|
|
|
80
81
|
// Paired with ForestPlot.tsx's translateX to give staggered rows a
|
|
81
82
|
// symmetric FOREST_ROW_STAGGER_OVERLAP_PX overlap on both sides -
|
|
82
83
|
// see that file's comment for the shared derivation.
|
|
83
|
-
columnGap: `calc(${maxPlotSize} - ${
|
|
84
|
-
FOREST_ROW_STAGGER_OVERLAP_PX
|
|
84
|
+
columnGap: `calc(${maxPlotSize} - ${
|
|
85
|
+
2 * FOREST_ROW_STAGGER_OVERLAP_PX
|
|
86
|
+
}px)`,
|
|
85
87
|
justifyContent: 'center',
|
|
86
88
|
width: '100%',
|
|
87
89
|
// ForestQuickSelect switches to a fixed right-side sidebar at this
|
|
88
90
|
// breakpoint (see quickSelectSx) - reserve its width so centering
|
|
89
91
|
// doesn't run the grid into it. Below this breakpoint the toolbelt
|
|
90
92
|
// is a bottom bar instead, so no reservation is needed there.
|
|
91
|
-
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
94
|
+
{
|
|
95
|
+
width: `calc(100% - ${layout.fieldSpaceForRightSideControls})`,
|
|
96
|
+
},
|
|
94
97
|
// Lower bound must be MIN_PLOT_SIZE, not 0, or columns shrink past
|
|
95
98
|
// it to still fit the container.
|
|
96
99
|
gridTemplateColumns: `repeat(${columns}, minmax(${MIN_PLOT_SIZE}, ${maxPlotSize}))`,
|
|
@@ -42,8 +42,8 @@ const getForestPlotBackgroundImage = (
|
|
|
42
42
|
fertilizerType === STANDARD
|
|
43
43
|
? plotStates['mulched-plot']
|
|
44
44
|
: fertilizerType === RAINBOW
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
? plotStates['rainbow-mulched-plot']
|
|
46
|
+
: null
|
|
47
47
|
|
|
48
48
|
return [
|
|
49
49
|
...(overlay ? [`url(${overlay})`] : []),
|
|
@@ -70,8 +70,8 @@ const getTreeTooltipText = (
|
|
|
70
70
|
treeLifeStage !== GROWN
|
|
71
71
|
? 'Growing...'
|
|
72
72
|
: fruitLifeStage === GROWN
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
? `Ready to pick! (${fruitYield} ${fruitItemName ?? 'fruit'})`
|
|
74
|
+
: 'Fruiting...'
|
|
75
75
|
|
|
76
76
|
if (!fertilizerType || fertilizerType === NONE) {
|
|
77
77
|
return growthText
|
|
@@ -310,7 +310,7 @@ export const ForestPlot = ({
|
|
|
310
310
|
<Typography>
|
|
311
311
|
{getChopYieldText(
|
|
312
312
|
chopWoodRange,
|
|
313
|
-
isFruitRipe ? item?.name ?? null : null
|
|
313
|
+
isFruitRipe ? (item?.name ?? null) : null
|
|
314
314
|
)}
|
|
315
315
|
</Typography>
|
|
316
316
|
</>
|