@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
|
@@ -48,8 +48,10 @@ describe('UI Event Handlers', () => {
|
|
|
48
48
|
isAwaitingCowTradeRequest: false,
|
|
49
49
|
currentDialogView: dialogView.NONE,
|
|
50
50
|
showHomeScreen: true,
|
|
51
|
+
notificationDuration: 6000,
|
|
51
52
|
},
|
|
52
53
|
setState: vi.fn(),
|
|
54
|
+
props: { localforage: { setItem: vi.fn().mockResolvedValue(undefined) } },
|
|
53
55
|
purchaseItem: vi.fn(),
|
|
54
56
|
makeRecipe: vi.fn(),
|
|
55
57
|
makeFermentationRecipe: vi.fn(),
|
|
@@ -152,9 +154,8 @@ describe('UI Event Handlers', () => {
|
|
|
152
154
|
|
|
153
155
|
describe('handleViewChangeButtonClick', () => {
|
|
154
156
|
test('navigates to selected view', () => {
|
|
155
|
-
const handler =
|
|
156
|
-
mockContext
|
|
157
|
-
)
|
|
157
|
+
const handler =
|
|
158
|
+
uiEventHandlers.handleViewChangeButtonClick.bind(mockContext)
|
|
158
159
|
|
|
159
160
|
handler(stageFocusType.SHOP)
|
|
160
161
|
|
|
@@ -172,7 +173,7 @@ describe('UI Event Handlers', () => {
|
|
|
172
173
|
enablesFieldMode: fieldMode.PLANT,
|
|
173
174
|
})
|
|
174
175
|
|
|
175
|
-
handler(
|
|
176
|
+
handler(mockItem as unknown as Parameters<typeof handler>[0])
|
|
176
177
|
|
|
177
178
|
expect(mockContext.setState).toHaveBeenCalledWith({
|
|
178
179
|
fieldMode: fieldMode.PLANT,
|
|
@@ -183,12 +184,11 @@ describe('UI Event Handlers', () => {
|
|
|
183
184
|
|
|
184
185
|
describe('handleForestItemSelectClick', () => {
|
|
185
186
|
test('selects the sapling and switches to PLANT mode', () => {
|
|
186
|
-
const handler =
|
|
187
|
-
mockContext
|
|
188
|
-
)
|
|
187
|
+
const handler =
|
|
188
|
+
uiEventHandlers.handleForestItemSelectClick.bind(mockContext)
|
|
189
189
|
const mockItem = testItem({ id: 'test-sapling' })
|
|
190
190
|
|
|
191
|
-
handler(
|
|
191
|
+
handler(mockItem as unknown as Parameters<typeof handler>[0])
|
|
192
192
|
|
|
193
193
|
// Regression: selecting a sapling must back out of whatever field
|
|
194
194
|
// mode (e.g. CHOP, FERTILIZE) was previously active, or clicking an
|
|
@@ -219,7 +219,7 @@ describe('UI Event Handlers', () => {
|
|
|
219
219
|
test('opens menu when explicitly requested', () => {
|
|
220
220
|
const handler = uiEventHandlers.handleMenuToggle.bind(mockContext)
|
|
221
221
|
|
|
222
|
-
handler(
|
|
222
|
+
handler(true as unknown as Parameters<typeof handler>[0])
|
|
223
223
|
|
|
224
224
|
const stateUpdater = mockContext.setState.mock.calls[0][0]
|
|
225
225
|
const newState = stateUpdater({ isMenuOpen: false })
|
|
@@ -305,9 +305,8 @@ describe('UI Event Handlers', () => {
|
|
|
305
305
|
|
|
306
306
|
describe('handleClickDialogViewButton', () => {
|
|
307
307
|
test('displays requested dialog screen', () => {
|
|
308
|
-
const handler =
|
|
309
|
-
mockContext
|
|
310
|
-
)
|
|
308
|
+
const handler =
|
|
309
|
+
uiEventHandlers.handleClickDialogViewButton.bind(mockContext)
|
|
311
310
|
|
|
312
311
|
handler(dialogView.FARMERS_LOG)
|
|
313
312
|
|
|
@@ -350,9 +349,8 @@ describe('UI Event Handlers', () => {
|
|
|
350
349
|
|
|
351
350
|
describe('handleFieldActionRangeChange', () => {
|
|
352
351
|
test('adjusts tool range preview size', () => {
|
|
353
|
-
const handler =
|
|
354
|
-
mockContext
|
|
355
|
-
)
|
|
352
|
+
const handler =
|
|
353
|
+
uiEventHandlers.handleFieldActionRangeChange.bind(mockContext)
|
|
356
354
|
|
|
357
355
|
handler(3)
|
|
358
356
|
|
|
@@ -413,9 +411,8 @@ describe('UI Event Handlers', () => {
|
|
|
413
411
|
|
|
414
412
|
describe('handleShowHomeScreenChange', () => {
|
|
415
413
|
test('enables home screen display', () => {
|
|
416
|
-
const handler =
|
|
417
|
-
mockContext
|
|
418
|
-
)
|
|
414
|
+
const handler =
|
|
415
|
+
uiEventHandlers.handleShowHomeScreenChange.bind(mockContext)
|
|
419
416
|
|
|
420
417
|
handler(null, true)
|
|
421
418
|
|
|
@@ -426,9 +423,8 @@ describe('UI Event Handlers', () => {
|
|
|
426
423
|
|
|
427
424
|
test('navigates away from home when disabling home screen while viewing it', () => {
|
|
428
425
|
mockContext.state.stageFocus = stageFocusType.HOME
|
|
429
|
-
const handler =
|
|
430
|
-
mockContext
|
|
431
|
-
)
|
|
426
|
+
const handler =
|
|
427
|
+
uiEventHandlers.handleShowHomeScreenChange.bind(mockContext)
|
|
432
428
|
|
|
433
429
|
handler(null, false)
|
|
434
430
|
|
|
@@ -439,6 +435,19 @@ describe('UI Event Handlers', () => {
|
|
|
439
435
|
})
|
|
440
436
|
})
|
|
441
437
|
|
|
438
|
+
describe('notification duration handlers', () => {
|
|
439
|
+
test('updates notification duration in milliseconds from slider seconds', () => {
|
|
440
|
+
const handler =
|
|
441
|
+
uiEventHandlers.handleNotificationDurationChange.bind(mockContext)
|
|
442
|
+
|
|
443
|
+
handler(new Event('change'), 8)
|
|
444
|
+
|
|
445
|
+
expect(mockContext.setState).toHaveBeenCalledWith({
|
|
446
|
+
notificationDuration: 8000,
|
|
447
|
+
})
|
|
448
|
+
})
|
|
449
|
+
})
|
|
450
|
+
|
|
442
451
|
describe('handleRNGSeedChange', () => {
|
|
443
452
|
test('applies custom random seed for reproducible gameplay', () => {
|
|
444
453
|
const handler = uiEventHandlers.handleRNGSeedChange.bind(mockContext)
|
|
@@ -465,9 +474,8 @@ describe('UI Event Handlers', () => {
|
|
|
465
474
|
|
|
466
475
|
describe('navigation handlers', () => {
|
|
467
476
|
test('moves to next view', () => {
|
|
468
|
-
const handler =
|
|
469
|
-
mockContext
|
|
470
|
-
)
|
|
477
|
+
const handler =
|
|
478
|
+
uiEventHandlers.handleClickNextMenuButton.bind(mockContext)
|
|
471
479
|
|
|
472
480
|
handler()
|
|
473
481
|
|
|
@@ -475,9 +483,8 @@ describe('UI Event Handlers', () => {
|
|
|
475
483
|
})
|
|
476
484
|
|
|
477
485
|
test('moves to previous view', () => {
|
|
478
|
-
const handler =
|
|
479
|
-
mockContext
|
|
480
|
-
)
|
|
486
|
+
const handler =
|
|
487
|
+
uiEventHandlers.handleClickPreviousMenuButton.bind(mockContext)
|
|
481
488
|
|
|
482
489
|
handler()
|
|
483
490
|
|
|
@@ -503,9 +510,8 @@ describe('UI Event Handlers', () => {
|
|
|
503
510
|
})
|
|
504
511
|
|
|
505
512
|
test('increases inventory capacity', () => {
|
|
506
|
-
const handler =
|
|
507
|
-
mockContext
|
|
508
|
-
)
|
|
513
|
+
const handler =
|
|
514
|
+
uiEventHandlers.handleStorageExpansionPurchase.bind(mockContext)
|
|
509
515
|
|
|
510
516
|
handler()
|
|
511
517
|
|
|
@@ -515,9 +521,8 @@ describe('UI Event Handlers', () => {
|
|
|
515
521
|
|
|
516
522
|
describe('loan handlers', () => {
|
|
517
523
|
test('reduces debt by payment amount', () => {
|
|
518
|
-
const handler =
|
|
519
|
-
mockContext
|
|
520
|
-
)
|
|
524
|
+
const handler =
|
|
525
|
+
uiEventHandlers.handleClickLoanPaydownButton.bind(mockContext)
|
|
521
526
|
|
|
522
527
|
handler(500)
|
|
523
528
|
|
|
@@ -525,9 +530,8 @@ describe('UI Event Handlers', () => {
|
|
|
525
530
|
})
|
|
526
531
|
|
|
527
532
|
test('borrows money increasing debt', () => {
|
|
528
|
-
const handler =
|
|
529
|
-
mockContext
|
|
530
|
-
)
|
|
533
|
+
const handler =
|
|
534
|
+
uiEventHandlers.handleClickTakeOutLoanButton.bind(mockContext)
|
|
531
535
|
|
|
532
536
|
handler(1000)
|
|
533
537
|
|
|
@@ -537,9 +541,8 @@ describe('UI Event Handlers', () => {
|
|
|
537
541
|
|
|
538
542
|
describe('utility handlers', () => {
|
|
539
543
|
test('resets all saved game data', () => {
|
|
540
|
-
const handler =
|
|
541
|
-
mockContext
|
|
542
|
-
)
|
|
544
|
+
const handler =
|
|
545
|
+
uiEventHandlers.handleClearPersistedDataClick.bind(mockContext)
|
|
543
546
|
|
|
544
547
|
handler()
|
|
545
548
|
|
|
@@ -555,9 +558,8 @@ describe('UI Event Handlers', () => {
|
|
|
555
558
|
})
|
|
556
559
|
|
|
557
560
|
test('shows or hides chat window', () => {
|
|
558
|
-
const handler =
|
|
559
|
-
mockContext
|
|
560
|
-
)
|
|
561
|
+
const handler =
|
|
562
|
+
uiEventHandlers.handleChatRoomOpenStateChange.bind(mockContext)
|
|
561
563
|
|
|
562
564
|
handler(true)
|
|
563
565
|
|
|
@@ -460,6 +460,16 @@ export default {
|
|
|
460
460
|
this.setState({ showNotifications: checked })
|
|
461
461
|
},
|
|
462
462
|
|
|
463
|
+
handleNotificationDurationChange(
|
|
464
|
+
this: Farmhand,
|
|
465
|
+
_event: React.SyntheticEvent | Event,
|
|
466
|
+
value: number | number[]
|
|
467
|
+
) {
|
|
468
|
+
if (typeof value === 'number') {
|
|
469
|
+
this.setState({ notificationDuration: value * 1000 })
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
|
|
463
473
|
handleClickNotificationIndicator(this: Farmhand) {
|
|
464
474
|
this.openDialogView(dialogView.FARMERS_LOG)
|
|
465
475
|
},
|
|
@@ -27,18 +27,22 @@ export const usePrefersReducedMotion = (): boolean => {
|
|
|
27
27
|
mediaQueryList.addEventListener('change', listener)
|
|
28
28
|
} else if ('addListener' in mediaQueryList) {
|
|
29
29
|
// Compatibility for legacy mediaQueryList implementations
|
|
30
|
-
;(
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
;(
|
|
31
|
+
mediaQueryList as unknown as {
|
|
32
|
+
addListener: (cb: (e: MediaQueryListEvent) => void) => void
|
|
33
|
+
}
|
|
34
|
+
).addListener(listener)
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
return () => {
|
|
36
38
|
if (mediaQueryList.removeEventListener) {
|
|
37
39
|
mediaQueryList.removeEventListener('change', listener)
|
|
38
40
|
} else if ('removeListener' in mediaQueryList) {
|
|
39
|
-
;(
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
;(
|
|
42
|
+
mediaQueryList as unknown as {
|
|
43
|
+
removeListener: (cb: (e: MediaQueryListEvent) => void) => void
|
|
44
|
+
}
|
|
45
|
+
).removeListener(listener)
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
}, [])
|
package/src/mui-theme.ts
CHANGED
|
@@ -49,9 +49,10 @@ const globalStyleOverrides = {
|
|
|
49
49
|
},
|
|
50
50
|
'input[type="number"]': { minWidth: '140px' },
|
|
51
51
|
th: { fontWeight: 'bold' },
|
|
52
|
-
'h1, h2, h3, h4, h5, h6, legend, td, th, .MuiTypography-h1, .MuiTypography-h2, .MuiTypography-h3, .MuiTypography-h4, .MuiTypography-h5, .MuiTypography-h6, .MuiButtonBase-root':
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
'h1, h2, h3, h4, h5, h6, legend, td, th, .MuiTypography-h1, .MuiTypography-h2, .MuiTypography-h3, .MuiTypography-h4, .MuiTypography-h5, .MuiTypography-h6, .MuiButtonBase-root':
|
|
53
|
+
{
|
|
54
|
+
fontFamily: '"Francois One", sans-serif',
|
|
55
|
+
},
|
|
55
56
|
'.danger-text': { color: colors.error },
|
|
56
57
|
'.success-text': { color: colors.success },
|
|
57
58
|
hr: { background: 'none' },
|
|
@@ -18,8 +18,9 @@ function getCropImage(
|
|
|
18
18
|
if (Array.isArray(seedItem.growsInto)) {
|
|
19
19
|
return `.imageId || cropItem.id
|
|
23
|
+
}.png)`
|
|
23
24
|
} else {
|
|
24
25
|
return ``
|
|
25
26
|
}
|
package/src/styles/sx.ts
CHANGED
|
@@ -85,16 +85,17 @@ export const quickSelectSx = (theme: Theme, isMenuOpen: boolean) => ({
|
|
|
85
85
|
display: isMenuOpen ? 'none' : undefined,
|
|
86
86
|
},
|
|
87
87
|
},
|
|
88
|
-
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
88
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
89
|
+
{
|
|
90
|
+
left: 'auto',
|
|
91
|
+
maxHeight: 'calc(100vh - 20em)',
|
|
92
|
+
minWidth: '4em',
|
|
93
|
+
overflow: 'auto',
|
|
94
|
+
right: '0.75em',
|
|
95
|
+
top: '9em',
|
|
96
|
+
transform: 'none',
|
|
97
|
+
bottom: '8em',
|
|
98
|
+
},
|
|
98
99
|
'@media (orientation: portrait)': {
|
|
99
100
|
display: isMenuOpen ? 'none' : undefined,
|
|
100
101
|
},
|
|
@@ -104,15 +105,17 @@ export const quickSelectSx = (theme: Theme, isMenuOpen: boolean) => ({
|
|
|
104
105
|
overflowY: 'hidden',
|
|
105
106
|
padding: '0.5em',
|
|
106
107
|
position: 'relative',
|
|
107
|
-
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
'& > *': {
|
|
112
|
-
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]: {
|
|
113
|
-
alignItems: 'center',
|
|
114
|
-
width: '3em',
|
|
108
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
109
|
+
{
|
|
110
|
+
overflowX: 'hidden',
|
|
111
|
+
flexDirection: 'column',
|
|
115
112
|
},
|
|
113
|
+
'& > *': {
|
|
114
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
115
|
+
{
|
|
116
|
+
alignItems: 'center',
|
|
117
|
+
width: '3em',
|
|
118
|
+
},
|
|
116
119
|
},
|
|
117
120
|
},
|
|
118
121
|
'& .Toolbelt': {
|
|
@@ -122,12 +125,13 @@ export const quickSelectSx = (theme: Theme, isMenuOpen: boolean) => ({
|
|
|
122
125
|
},
|
|
123
126
|
'& .MuiDivider-root': {
|
|
124
127
|
margin: '0 0.5em',
|
|
125
|
-
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
129
|
+
{
|
|
130
|
+
margin: '0.5em 0',
|
|
131
|
+
height: '1px',
|
|
132
|
+
width: '100%',
|
|
133
|
+
borderRightWidth: 0,
|
|
134
|
+
borderBottomWidth: 'thin',
|
|
135
|
+
},
|
|
132
136
|
},
|
|
133
137
|
})
|
package/src/test-utils/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { randomStream } from '../enums.js'
|
|
2
|
+
|
|
1
3
|
import { chooseRandomIndex } from './chooseRandomIndex.js'
|
|
2
4
|
|
|
3
|
-
export const chooseRandom = <T>(list: T[]): T =>
|
|
5
|
+
export const chooseRandom = <T>(list: T[], stream?: randomStream): T =>
|
|
6
|
+
list[chooseRandomIndex(list, stream)]
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { random } from '../common/utils.js'
|
|
2
|
+
import { randomStream } from '../enums.js'
|
|
2
3
|
|
|
3
|
-
export const chooseRandomIndex = <T>(
|
|
4
|
+
export const chooseRandomIndex = <T>(
|
|
5
|
+
list: T[],
|
|
6
|
+
stream?: randomStream
|
|
7
|
+
): number =>
|
|
4
8
|
// TODO: Fix statistical bias by using Math.floor(random() * list.length).
|
|
5
|
-
Math.round(random() * (list.length - 1))
|
|
9
|
+
Math.round(random(stream) * (list.length - 1))
|
package/src/utils/getCowImage.ts
CHANGED
|
@@ -65,23 +65,29 @@ const colorizeCowTemplate = (() => {
|
|
|
65
65
|
)
|
|
66
66
|
const image = await Jimp.read(cowTemplateBuffer)
|
|
67
67
|
|
|
68
|
-
image.scan(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
image.scan(
|
|
69
|
+
0,
|
|
70
|
+
0,
|
|
71
|
+
image.bitmap.width,
|
|
72
|
+
image.bitmap.height,
|
|
73
|
+
function (x, y) {
|
|
74
|
+
const { r, g, b } = Jimp.intToRGBA(image.getPixelColor(x, y))
|
|
75
|
+
|
|
76
|
+
// rgb(102, 102, 102) represents the color to replace in the template
|
|
77
|
+
// source images (#666).
|
|
78
|
+
if (r === 102 && g === 102 && b === 102) {
|
|
79
|
+
const cowColorRgb = hexToRgb(COW_COLORS_HEX_MAP[color])
|
|
80
|
+
const colorNumber = Jimp.rgbaToInt(
|
|
81
|
+
cowColorRgb.r,
|
|
82
|
+
cowColorRgb.g,
|
|
83
|
+
cowColorRgb.b,
|
|
84
|
+
255
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
image.setPixelColor(colorNumber, x, y)
|
|
88
|
+
}
|
|
83
89
|
}
|
|
84
|
-
|
|
90
|
+
)
|
|
85
91
|
|
|
86
92
|
cachedCowImages[imageKey] = await image.getBase64Async(Jimp.MIME_PNG)
|
|
87
93
|
|
|
@@ -12,8 +12,8 @@ export const getMaxYieldOfFermentationRecipe = (
|
|
|
12
12
|
): number => {
|
|
13
13
|
const {
|
|
14
14
|
[fermentationRecipe.id]: itemQuantityInInventory = 0,
|
|
15
|
-
[(itemsMap as Record<string, farmhand.item>).salt
|
|
16
|
-
|
|
15
|
+
[(itemsMap as Record<string, farmhand.item>).salt.id]:
|
|
16
|
+
saltQuantityInInventory = 0,
|
|
17
17
|
} = getInventoryQuantityMap(inventory)
|
|
18
18
|
|
|
19
19
|
const maxSaltYieldPotential = Math.floor(
|
|
@@ -8,7 +8,7 @@ import { getPlotContentFromItemId } from './getPlotContentFromItemId.js'
|
|
|
8
8
|
|
|
9
9
|
describe('getPlotImage', () => {
|
|
10
10
|
test('returns null when no plotContent is provided', () => {
|
|
11
|
-
expect(getPlotImage(
|
|
11
|
+
expect(getPlotImage(null as unknown as farmhand.plotContent, 0, 0)).toBe(
|
|
12
12
|
null
|
|
13
13
|
)
|
|
14
14
|
})
|
|
@@ -18,40 +18,40 @@ describe('getPlotImage', () => {
|
|
|
18
18
|
|
|
19
19
|
expect(
|
|
20
20
|
getPlotImage(
|
|
21
|
-
|
|
21
|
+
testCrop({
|
|
22
22
|
itemId,
|
|
23
23
|
daysWatered: 0,
|
|
24
|
-
}) as unknown
|
|
24
|
+
}) as unknown as farmhand.plotContent,
|
|
25
25
|
0,
|
|
26
26
|
0
|
|
27
27
|
)
|
|
28
28
|
).toBe(itemImages['carrot-seed'])
|
|
29
29
|
expect(
|
|
30
30
|
getPlotImage(
|
|
31
|
-
|
|
31
|
+
testCrop({
|
|
32
32
|
itemId,
|
|
33
33
|
daysWatered: 1,
|
|
34
|
-
}) as unknown
|
|
34
|
+
}) as unknown as farmhand.plotContent,
|
|
35
35
|
0,
|
|
36
36
|
0
|
|
37
37
|
)
|
|
38
38
|
).toBe(itemImages['carrot-seed'])
|
|
39
39
|
expect(
|
|
40
40
|
getPlotImage(
|
|
41
|
-
|
|
41
|
+
testCrop({
|
|
42
42
|
itemId,
|
|
43
43
|
daysWatered: 3,
|
|
44
|
-
}) as unknown
|
|
44
|
+
}) as unknown as farmhand.plotContent,
|
|
45
45
|
0,
|
|
46
46
|
0
|
|
47
47
|
)
|
|
48
48
|
).toBe(itemImages['carrot-growing-2'])
|
|
49
49
|
expect(
|
|
50
50
|
getPlotImage(
|
|
51
|
-
|
|
51
|
+
testCrop({
|
|
52
52
|
itemId,
|
|
53
53
|
daysWatered: 5,
|
|
54
|
-
}) as unknown
|
|
54
|
+
}) as unknown as farmhand.plotContent,
|
|
55
55
|
0,
|
|
56
56
|
0
|
|
57
57
|
)
|
|
@@ -61,9 +61,9 @@ describe('getPlotImage', () => {
|
|
|
61
61
|
test('returns item image for oreId', () => {
|
|
62
62
|
expect(
|
|
63
63
|
getPlotImage(
|
|
64
|
-
|
|
64
|
+
getPlotContentFromItemId(
|
|
65
65
|
silverOre.id
|
|
66
|
-
) as unknown
|
|
66
|
+
) as unknown as farmhand.plotContent,
|
|
67
67
|
0,
|
|
68
68
|
0
|
|
69
69
|
)
|
|
@@ -83,16 +83,16 @@ describe('getPlotImage', () => {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
expect(
|
|
86
|
-
getPlotImage(
|
|
86
|
+
getPlotImage(shoveledPlot as unknown as farmhand.shoveledPlot, 0, 0)
|
|
87
87
|
).toBe((itemImages as Record<string, string>)[silverOre.id])
|
|
88
88
|
})
|
|
89
89
|
|
|
90
90
|
test('returns item image for other content', () => {
|
|
91
91
|
expect(
|
|
92
92
|
getPlotImage(
|
|
93
|
-
|
|
93
|
+
getPlotContentFromItemId(
|
|
94
94
|
'sprinkler'
|
|
95
|
-
) as unknown
|
|
95
|
+
) as unknown as farmhand.plotContent,
|
|
96
96
|
0,
|
|
97
97
|
0
|
|
98
98
|
)
|
|
@@ -11,21 +11,21 @@ const { SEED, GROWING, GROWN } = cropLifeStage
|
|
|
11
11
|
const isPlotContent = (obj: unknown = {}): obj is farmhand.plotContent =>
|
|
12
12
|
Boolean(
|
|
13
13
|
obj &&
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
typeof obj === 'object' &&
|
|
15
|
+
'itemId' in obj &&
|
|
16
|
+
'fertilizerType' in obj &&
|
|
17
|
+
obj.itemId &&
|
|
18
|
+
obj.fertilizerType
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
const isShoveledPlot = (obj: unknown = {}): obj is farmhand.shoveledPlot =>
|
|
22
22
|
Boolean(
|
|
23
23
|
obj &&
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
typeof obj === 'object' &&
|
|
25
|
+
'isShoveled' in obj &&
|
|
26
|
+
'daysUntilClear' in obj &&
|
|
27
|
+
obj.isShoveled &&
|
|
28
|
+
obj.daysUntilClear
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
const isPlotContentACrop = (
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { itemsMap } from '../data/maps.js'
|
|
2
|
+
import { randomStream } from '../enums.js'
|
|
2
3
|
|
|
3
4
|
import { chooseRandom } from './chooseRandom.js'
|
|
4
5
|
import { chooseRandomIndex } from './chooseRandomIndex.js'
|
|
5
6
|
import { getFinalCropItemIdFromSeedItemId } from './getFinalCropItemIdFromSeedItemId.js'
|
|
6
7
|
|
|
7
8
|
export const getRandomUnlockedCrop = (
|
|
8
|
-
unlockedSeedItemIds: Array<string
|
|
9
|
+
unlockedSeedItemIds: Array<string>,
|
|
10
|
+
stream?: randomStream
|
|
9
11
|
): farmhand.item => {
|
|
10
|
-
const seedItemId = chooseRandom(unlockedSeedItemIds)
|
|
12
|
+
const seedItemId = chooseRandom(unlockedSeedItemIds, stream)
|
|
11
13
|
const seedItem = itemsMap[seedItemId]
|
|
12
14
|
const variationIdx = Array.isArray(seedItem.growsInto)
|
|
13
|
-
? chooseRandomIndex(seedItem.growsInto)
|
|
15
|
+
? chooseRandomIndex(seedItem.growsInto, stream)
|
|
14
16
|
: 0
|
|
15
17
|
|
|
16
18
|
const finalCropItemId = getFinalCropItemIdFromSeedItemId(
|
|
@@ -5,19 +5,13 @@ describe('getRandomizedLifespan', () => {
|
|
|
5
5
|
// First call: floor roll (max variance -> 200 * (1 - 1 * 0.05) = 190).
|
|
6
6
|
// All subsequent calls: extension roll of 0, which is immediately
|
|
7
7
|
// beaten by getTreeDeathChance(0) (0.05), so the extension is 0.
|
|
8
|
-
vitest
|
|
9
|
-
.spyOn(Math, 'random')
|
|
10
|
-
.mockReturnValueOnce(1)
|
|
11
|
-
.mockReturnValue(0)
|
|
8
|
+
vitest.spyOn(Math, 'random').mockReturnValueOnce(1).mockReturnValue(0)
|
|
12
9
|
|
|
13
10
|
expect(getRandomizedLifespan(200)).toBe(190)
|
|
14
11
|
})
|
|
15
12
|
|
|
16
13
|
test('floors at exactly the default when the first roll is 0', () => {
|
|
17
|
-
vitest
|
|
18
|
-
.spyOn(Math, 'random')
|
|
19
|
-
.mockReturnValueOnce(0)
|
|
20
|
-
.mockReturnValue(0)
|
|
14
|
+
vitest.spyOn(Math, 'random').mockReturnValueOnce(0).mockReturnValue(0)
|
|
21
15
|
|
|
22
16
|
expect(getRandomizedLifespan(200)).toBe(200)
|
|
23
17
|
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getValueAdjustmentStream } from './getValueAdjustmentStream.js'
|
|
2
|
+
|
|
3
|
+
describe('getValueAdjustmentStream', () => {
|
|
4
|
+
test('returns the price adjustment stream for an item', () => {
|
|
5
|
+
expect(getValueAdjustmentStream('carrot-seed')).toEqual(
|
|
6
|
+
'valueAdjustment:carrot-seed'
|
|
7
|
+
)
|
|
8
|
+
})
|
|
9
|
+
})
|
|
@@ -14,6 +14,6 @@ const FARM_PRODUCT_TYPES: itemType[] = [
|
|
|
14
14
|
export const isItemAFarmProduct = (item: farmhand.item): boolean =>
|
|
15
15
|
Boolean(
|
|
16
16
|
isItemAGrownCrop(item) ||
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
isItemAGrownFruit(item) ||
|
|
18
|
+
FARM_PRODUCT_TYPES.includes(item.type)
|
|
19
19
|
)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { random } from '../common/utils.js'
|
|
2
2
|
import { PRECIPITATION_CHANCE } from '../constants.js'
|
|
3
|
+
import { randomStream } from '../enums.js'
|
|
3
4
|
|
|
4
|
-
export const shouldPrecipitateToday = () =>
|
|
5
|
+
export const shouldPrecipitateToday = () =>
|
|
6
|
+
random(randomStream.PRECIPITATION) < PRECIPITATION_CHANCE
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { random } from '../common/utils.js'
|
|
2
2
|
import { STORM_CHANCE } from '../constants.js'
|
|
3
|
+
import { randomStream } from '../enums.js'
|
|
3
4
|
|
|
4
|
-
export const shouldStormToday = () => random() < STORM_CHANCE
|
|
5
|
+
export const shouldStormToday = () => random(randomStream.STORM) < STORM_CHANCE
|