@jeremyckahn/farmhand 1.23.2 → 1.23.4
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 +5 -0
- package/dist/assets/index-4ynbl-GC.js +392 -0
- package/dist/assets/index-4ynbl-GC.js.map +1 -0
- package/dist/assets/index-CaYdR-lC.css +1 -0
- package/dist/assets/jersey-10-latin-400-normal-DrL73VYW.woff2 +0 -0
- package/dist/assets/jersey-10-latin-ext-400-normal-uIKqQSaG.woff2 +0 -0
- package/dist/assets/jersey-20-latin-400-normal-bNi78tgO.woff2 +0 -0
- package/dist/assets/jersey-20-latin-ext-400-normal-BO3lmWef.woff2 +0 -0
- package/dist/assets/jersey-25-latin-400-normal-BuYJnnt7.woff2 +0 -0
- package/dist/assets/jersey-25-latin-ext-400-normal-CP5Jl3WN.woff2 +0 -0
- package/dist/index.html +2 -2
- package/dist/service-worker.js +1 -1
- package/dist/service-worker.js.map +1 -1
- package/package.json +7 -12
- 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 +7 -13
- package/src/components/Achievement/Achievement.tsx +2 -15
- package/src/components/AchievementsView/AchievementsView.tsx +2 -7
- package/src/components/AnimatedNumber/AnimatedNumber.tsx +0 -6
- package/src/components/AppBar/AppBar.tsx +21 -17
- 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 -6
- package/src/components/CowCard/CowCard.tsx +7 -39
- package/src/components/CowCard/Subheader/Subheader.tsx +21 -44
- package/src/components/CowPen/Cow.tsx +5 -11
- package/src/components/CowPen/CowPen.test.tsx +4 -4
- package/src/components/CowPen/CowPen.tsx +5 -16
- package/src/components/CowPenContextMenu/CowPenContextMenu.tsx +2 -18
- package/src/components/CowPenContextMenu/TabPanel/index.tsx +0 -7
- package/src/components/Farmhand/Farmhand.context.tsx +4 -3
- package/src/components/Farmhand/Farmhand.tsx +11 -15
- 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 +24 -15
- 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 +15 -50
- 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 +2 -7
- 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 +5 -12
- package/src/components/Item/Item.tsx +11 -29
- package/src/components/ItemList/ItemList.tsx +4 -11
- package/src/components/KeybindingsView/KeybindingsView.tsx +0 -2
- package/src/components/LogView/LogView.tsx +6 -28
- package/src/components/Navigation/DayAndProgressContainer.tsx +0 -8
- package/src/components/Navigation/Navigation.tsx +12 -33
- package/src/components/NotificationSystem/NotificationSystem.test.tsx +45 -3
- package/src/components/NotificationSystem/NotificationSystem.tsx +45 -32
- 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/PixelIcon/createPixelIcon.test.tsx +59 -0
- package/src/components/PixelIcon/createPixelIcon.tsx +97 -0
- package/src/components/PixelIcon/icons.ts +445 -0
- package/src/components/PixelIcon/index.ts +3 -0
- package/src/components/Plot/Plot.tsx +3 -3
- package/src/components/PriceEventView/PriceEventView.tsx +0 -6
- package/src/components/ProgressBar/ProgressBar.tsx +9 -8
- package/src/components/QuantityInput/QuantityInput.tsx +6 -11
- 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 +4 -10
- 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 -18
- 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 -18
- package/src/components/TierPurchase/TierPurchase.tsx +0 -12
- package/src/components/Toolbelt/Toolbelt.tsx +5 -10
- package/src/components/UpdateNotifier/UpdateNotifier.tsx +33 -5
- 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 +6 -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/showNotification.test.ts +20 -0
- package/src/game-logic/reducers/showNotification.ts +4 -2
- 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 +18 -3
- package/src/hooks/usePrefersReducedMotion.ts +10 -6
- package/src/index.tsx +0 -2
- package/src/mui-theme.ts +258 -37
- package/src/scripts/generate-crop-table.ts +3 -2
- package/src/setupTests.ts +18 -12
- package/src/strings.ts +1 -1
- package/src/styles/fonts.ts +73 -0
- package/src/styles/pixel.ts +151 -0
- package/src/styles/sx.ts +46 -33
- package/src/styles/tokens.ts +11 -0
- 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 -27
- 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
- package/dist/assets/francois-one-latin-400-normal-DD1rFI7H.woff2 +0 -0
- package/dist/assets/francois-one-latin-400-normal-DNXs7uOx.woff +0 -0
- package/dist/assets/francois-one-latin-ext-400-normal-CwRdWRZD.woff2 +0 -0
- package/dist/assets/francois-one-latin-ext-400-normal-JvkpT0Av.woff +0 -0
- package/dist/assets/francois-one-vietnamese-400-normal-BLIKeG5Y.woff2 +0 -0
- package/dist/assets/francois-one-vietnamese-400-normal-CuWeHlXs.woff +0 -0
- package/dist/assets/index-CNcrTYN-.css +0 -1
- package/dist/assets/index-DNPchtK9.js +0 -844
- package/dist/assets/index-DNPchtK9.js.map +0 -1
- package/dist/assets/public-sans-latin-400-normal-8Rpg0ruU.woff2 +0 -0
- package/dist/assets/public-sans-latin-400-normal-SBbinRkI.woff +0 -0
- package/dist/assets/public-sans-latin-ext-400-normal-B02wwOdZ.woff +0 -0
- package/dist/assets/public-sans-latin-ext-400-normal-mk90oQqJ.woff2 +0 -0
- package/dist/assets/public-sans-vietnamese-400-normal-BO1meqcx.woff +0 -0
- package/dist/assets/public-sans-vietnamese-400-normal-TxZB8OSn.woff2 +0 -0
- package/src/fontsource.d.ts +0 -3
package/src/styles/sx.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { lighten, Theme } from '@mui/material/styles/index.js'
|
|
2
2
|
|
|
3
|
+
import { pixelBevel, pixelDropShadowFilter, pixelFrameSx, px } from './pixel.js'
|
|
3
4
|
import { breakpoints, colors, layout } from './tokens.js'
|
|
4
5
|
|
|
5
6
|
// NOTE: These are plain object literals (not typed as SxProps<Theme>) so
|
|
@@ -26,17 +27,23 @@ export const squareImgSx = {
|
|
|
26
27
|
|
|
27
28
|
// Replaces the `card-style` mixin. Merge cardStyleSelectedSx on top of this
|
|
28
29
|
// when the card's `is-selected` state is active.
|
|
30
|
+
//
|
|
31
|
+
// Cards are drawn as pixel art panels with a 9-slice frame (see pixel.ts).
|
|
32
|
+
// Use `backgroundColor` rather than the `background` shorthand when
|
|
33
|
+
// overriding the color of anything built on this: the shorthand resets the
|
|
34
|
+
// `backgroundClip` that keeps the frame's corners and shadow transparent.
|
|
29
35
|
export const cardStyleSx = (theme: Theme) => ({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
...pixelFrameSx({ outline: colors.cardOutline, shadow: true }),
|
|
37
|
+
backgroundColor: colors.cardBackground,
|
|
38
|
+
boxShadow: pixelBevel(),
|
|
39
|
+
transition: theme.transitions.create('background-color', {
|
|
33
40
|
duration: theme.transitions.duration.enteringScreen,
|
|
34
41
|
easing: theme.transitions.easing.easeOut,
|
|
35
42
|
}),
|
|
36
43
|
})
|
|
37
44
|
|
|
38
45
|
export const cardStyleSelectedSx = {
|
|
39
|
-
|
|
46
|
+
backgroundColor: lighten(colors.cardBackground, 0.4),
|
|
40
47
|
} as const
|
|
41
48
|
|
|
42
49
|
// Replaces the `center-tabs` mixin.
|
|
@@ -50,8 +57,11 @@ export const centerTabsSx = {
|
|
|
50
57
|
marginTop: '4em',
|
|
51
58
|
},
|
|
52
59
|
'& [role="tabpanel"]': {
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
...pixelFrameSx({ outline: colors.cardOutline }),
|
|
61
|
+
// The tabs above this panel already draw the shared top edge.
|
|
62
|
+
borderTopWidth: 0,
|
|
63
|
+
borderImageWidth: `0 ${px(1)} ${px(1)}`,
|
|
64
|
+
backgroundColor: 'rgba(255, 255, 255, 0.5)',
|
|
55
65
|
padding: '0.5em 0.5em 0.5em',
|
|
56
66
|
'& .card-list > li:last-child': {
|
|
57
67
|
marginBottom: 0,
|
|
@@ -61,7 +71,7 @@ export const centerTabsSx = {
|
|
|
61
71
|
|
|
62
72
|
// Replaces the `sprite-shadow` mixin.
|
|
63
73
|
export const spriteShadowSx = {
|
|
64
|
-
filter:
|
|
74
|
+
filter: pixelDropShadowFilter,
|
|
65
75
|
} as const
|
|
66
76
|
|
|
67
77
|
// Shared by QuickSelect and ForestQuickSelect: both render the same fixed
|
|
@@ -85,34 +95,36 @@ export const quickSelectSx = (theme: Theme, isMenuOpen: boolean) => ({
|
|
|
85
95
|
display: isMenuOpen ? 'none' : undefined,
|
|
86
96
|
},
|
|
87
97
|
},
|
|
88
|
-
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
99
|
+
{
|
|
100
|
+
left: 'auto',
|
|
101
|
+
maxHeight: 'calc(100vh - 20em)',
|
|
102
|
+
minWidth: '4em',
|
|
103
|
+
overflow: 'auto',
|
|
104
|
+
right: '0.75em',
|
|
105
|
+
top: '9em',
|
|
106
|
+
transform: 'none',
|
|
107
|
+
bottom: '8em',
|
|
108
|
+
},
|
|
98
109
|
'@media (orientation: portrait)': {
|
|
99
110
|
display: isMenuOpen ? 'none' : undefined,
|
|
100
111
|
},
|
|
101
112
|
'& .MuiGrid-root': {
|
|
102
|
-
borderRadius: '0.25em',
|
|
103
113
|
overflowX: 'scroll',
|
|
104
114
|
overflowY: 'hidden',
|
|
105
115
|
padding: '0.5em',
|
|
106
116
|
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',
|
|
117
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
118
|
+
{
|
|
119
|
+
overflowX: 'hidden',
|
|
120
|
+
flexDirection: 'column',
|
|
115
121
|
},
|
|
122
|
+
'& > *': {
|
|
123
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
124
|
+
{
|
|
125
|
+
alignItems: 'center',
|
|
126
|
+
width: '3em',
|
|
127
|
+
},
|
|
116
128
|
},
|
|
117
129
|
},
|
|
118
130
|
'& .Toolbelt': {
|
|
@@ -122,12 +134,13 @@ export const quickSelectSx = (theme: Theme, isMenuOpen: boolean) => ({
|
|
|
122
134
|
},
|
|
123
135
|
'& .MuiDivider-root': {
|
|
124
136
|
margin: '0 0.5em',
|
|
125
|
-
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
[`@media (orientation: landscape) and (min-height: ${breakpoints.largePhone}px)`]:
|
|
138
|
+
{
|
|
139
|
+
margin: '0.5em 0',
|
|
140
|
+
height: '1px',
|
|
141
|
+
width: '100%',
|
|
142
|
+
borderRightWidth: 0,
|
|
143
|
+
borderBottomWidth: 'thin',
|
|
144
|
+
},
|
|
132
145
|
},
|
|
133
146
|
})
|
package/src/styles/tokens.ts
CHANGED
|
@@ -11,6 +11,10 @@ export const colors = {
|
|
|
11
11
|
error: '#ce0000',
|
|
12
12
|
success: '#2e7d32',
|
|
13
13
|
cardBackground: '#ffe3a1',
|
|
14
|
+
cardOutline: '#8a5c00',
|
|
15
|
+
dialogOutline: '#1f4f78',
|
|
16
|
+
neutralOutline: '#2b2118',
|
|
17
|
+
disabledOutline: 'rgba(0, 0, 0, 0.26)',
|
|
14
18
|
// Matches the legacy Sass `color.adjust($card-background, $lightness: -10%)`
|
|
15
19
|
// for cards nested inside other cards. MUI's `darken()` helper uses a
|
|
16
20
|
// different (multiplicative) algorithm and produces a visibly duller color.
|
|
@@ -40,6 +44,13 @@ export const breakpoints = {
|
|
|
40
44
|
xl: 1920,
|
|
41
45
|
} as const
|
|
42
46
|
|
|
47
|
+
// Pixel fonts. `display` is used for headings, buttons and other UI labels;
|
|
48
|
+
// `body` is used for running text.
|
|
49
|
+
export const fonts = {
|
|
50
|
+
display: '"Farmhand Display", sans-serif',
|
|
51
|
+
body: '"Farmhand Body", sans-serif',
|
|
52
|
+
} as const
|
|
53
|
+
|
|
43
54
|
export const layout = {
|
|
44
55
|
cardMaxWidth: 550,
|
|
45
56
|
sidebarWidth: '22em',
|
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
|
@@ -17,17 +17,6 @@ const Jimp = configureJimp({
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
const colorizeCowTemplate = (() => {
|
|
20
|
-
// TODO: Remove unused cowImageWidth, cowImageHeight, and
|
|
21
|
-
// cowImageFactoryCanvas variables.
|
|
22
|
-
// Instantiating a canvas directly in the module scope also poses runtime
|
|
23
|
-
// risks in non-browser/SSR environments.
|
|
24
|
-
const cowImageWidth = 48
|
|
25
|
-
const cowImageHeight = 48
|
|
26
|
-
const cowImageFactoryCanvas = document.createElement('canvas')
|
|
27
|
-
|
|
28
|
-
cowImageFactoryCanvas.setAttribute('height', String(cowImageHeight))
|
|
29
|
-
cowImageFactoryCanvas.setAttribute('width', String(cowImageWidth))
|
|
30
|
-
|
|
31
20
|
const cachedCowImages: Record<string, string> = {}
|
|
32
21
|
|
|
33
22
|
// https://stackoverflow.com/a/5624139
|
|
@@ -65,23 +54,29 @@ const colorizeCowTemplate = (() => {
|
|
|
65
54
|
)
|
|
66
55
|
const image = await Jimp.read(cowTemplateBuffer)
|
|
67
56
|
|
|
68
|
-
image.scan(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
57
|
+
image.scan(
|
|
58
|
+
0,
|
|
59
|
+
0,
|
|
60
|
+
image.bitmap.width,
|
|
61
|
+
image.bitmap.height,
|
|
62
|
+
function (x, y) {
|
|
63
|
+
const { r, g, b } = Jimp.intToRGBA(image.getPixelColor(x, y))
|
|
64
|
+
|
|
65
|
+
// rgb(102, 102, 102) represents the color to replace in the template
|
|
66
|
+
// source images (#666).
|
|
67
|
+
if (r === 102 && g === 102 && b === 102) {
|
|
68
|
+
const cowColorRgb = hexToRgb(COW_COLORS_HEX_MAP[color])
|
|
69
|
+
const colorNumber = Jimp.rgbaToInt(
|
|
70
|
+
cowColorRgb.r,
|
|
71
|
+
cowColorRgb.g,
|
|
72
|
+
cowColorRgb.b,
|
|
73
|
+
255
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
image.setPixelColor(colorNumber, x, y)
|
|
77
|
+
}
|
|
83
78
|
}
|
|
84
|
-
|
|
79
|
+
)
|
|
85
80
|
|
|
86
81
|
cachedCowImages[imageKey] = await image.getBase64Async(Jimp.MIME_PNG)
|
|
87
82
|
|
|
@@ -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
|
|
@@ -37,10 +37,8 @@ export const transformStateDataForImport = (
|
|
|
37
37
|
// TODO: Add defensive check safeguards for sanitizedState.cowBreedingPen
|
|
38
38
|
// and sanitizedState.cowInventory to prevent TypeError crashes during
|
|
39
39
|
// corrupt/legacy state imports.
|
|
40
|
-
const {
|
|
41
|
-
|
|
42
|
-
cowId2,
|
|
43
|
-
} = sanitizedState.cowBreedingPen as farmhand.state['cowBreedingPen']
|
|
40
|
+
const { cowId1, cowId2 } =
|
|
41
|
+
sanitizedState.cowBreedingPen as farmhand.state['cowBreedingPen']
|
|
44
42
|
|
|
45
43
|
const cowPenIdMap = (sanitizedState.cowInventory as farmhand.cow[]).reduce(
|
|
46
44
|
(acc: Record<string, farmhand.cow>, cow: farmhand.cow) => {
|
|
@@ -108,5 +106,5 @@ export const transformStateDataForImport = (
|
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
return
|
|
109
|
+
return sanitizedState as unknown as farmhand.state
|
|
112
110
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--animate-duration:1s;--animate-delay:1s;--animate-repeat:1}.animated{animation-duration:var(--animate-duration);animation-fill-mode:both}.animated.infinite{animation-iteration-count:infinite}.animated.repeat-1{animation-iteration-count:var(--animate-repeat)}.animated.repeat-2{animation-iteration-count:calc(var(--animate-repeat) * 2)}.animated.repeat-3{animation-iteration-count:calc(var(--animate-repeat) * 3)}.animated.delay-1s{animation-delay:var(--animate-delay)}.animated.delay-2s{animation-delay:calc(var(--animate-delay) * 2)}.animated.delay-3s{animation-delay:calc(var(--animate-delay) * 3)}.animated.delay-4s{animation-delay:calc(var(--animate-delay) * 4)}.animated.delay-5s{animation-delay:calc(var(--animate-delay) * 5)}.animated.faster{animation-duration:calc(var(--animate-duration) / 2)}.animated.fast{animation-duration:calc(var(--animate-duration) * .8)}.animated.slow{animation-duration:calc(var(--animate-duration) * 2)}.animated.slower{animation-duration:calc(var(--animate-duration) * 3)}@media print,(prefers-reduced-motion:reduce){.animated{transition-duration:1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important}.animated[class*=Out]{opacity:0}}@keyframes heartBeat{0%{transform:scale(1)}14%{transform:scale(1.3)}28%{transform:scale(1)}42%{transform:scale(1.3)}70%{transform:scale(1)}}.heartBeat{animation-name:heartBeat;animation-duration:calc(var(--animate-duration) * 1.3);animation-timing-function:ease-in-out}@font-face{font-family:Francois One;font-style:normal;font-display:swap;font-weight:400;src:url(./francois-one-vietnamese-400-normal-BLIKeG5Y.woff2)format("woff2"),url(./francois-one-vietnamese-400-normal-CuWeHlXs.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Francois One;font-style:normal;font-display:swap;font-weight:400;src:url(./francois-one-latin-ext-400-normal-CwRdWRZD.woff2)format("woff2"),url(./francois-one-latin-ext-400-normal-JvkpT0Av.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Francois One;font-style:normal;font-display:swap;font-weight:400;src:url(./francois-one-latin-400-normal-DD1rFI7H.woff2)format("woff2"),url(./francois-one-latin-400-normal-DNXs7uOx.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Public Sans;font-style:normal;font-display:swap;font-weight:400;src:url(./public-sans-vietnamese-400-normal-TxZB8OSn.woff2)format("woff2"),url(./public-sans-vietnamese-400-normal-BO1meqcx.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Public Sans;font-style:normal;font-display:swap;font-weight:400;src:url(./public-sans-latin-ext-400-normal-mk90oQqJ.woff2)format("woff2"),url(./public-sans-latin-ext-400-normal-B02wwOdZ.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Public Sans;font-style:normal;font-display:swap;font-weight:400;src:url(./public-sans-latin-400-normal-8Rpg0ruU.woff2)format("woff2"),url(./public-sans-latin-400-normal-SBbinRkI.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|