@jeremyckahn/farmhand
Version:
A farming game
155 lines (141 loc) • 4.34 kB
JavaScript
import React, { useEffect, useState } from 'react'
import classNames from 'classnames'
import Button from '@mui/material/Button/index.js'
import Card from '@mui/material/Card/index.js'
import CardHeader from '@mui/material/CardHeader/index.js'
import CardContent from '@mui/material/CardContent/index.js'
import CardActions from '@mui/material/CardActions/index.js'
import Typography from '@mui/material/Typography/index.js'
import { array, func, number, object } from 'prop-types'
import { totalIngredientsInRecipe } from '../../utils/totalIngredientsInRecipe.js'
import {
canMakeRecipe,
doesInventorySpaceRemain,
dollarString,
maxYieldOfRecipe,
integerString,
} from '../../utils/index.js'
import { itemsMap } from '../../data/maps.js'
import { craftedItems } from '../../img/index.js'
import QuantityInput from '../QuantityInput/index.js'
import IngredientsList from '../IngredientsList/index.js'
import FarmhandContext from '../Farmhand/Farmhand.context.js'
import './Recipe.sass'
import { INFINITE_STORAGE_LIMIT } from '../../constants.js'
/**
* @param {object} props
* @param {Function} props.handleMakeRecipeClick
* @param {farmhand.state['inventory']} props.inventory
* @param {number} props.inventoryLimit
* @param {Record<string, number>} props.playerInventoryQuantities
* @param {farmhand.recipe} props.recipe
*/
const Recipe = ({
handleMakeRecipeClick,
inventory,
inventoryLimit,
playerInventoryQuantities,
recipe,
recipe: { id, name, description },
}) => {
const [quantity, setQuantity] = useState(1)
useEffect(() => {
setQuantity(
Math.min(maxYieldOfRecipe(recipe, inventory), Math.max(1, quantity))
)
}, [inventory, recipe, quantity])
// Fixes https://github.com/jeremyckahn/farmhand/issues/25
const spaceFreedByIngredientsConsumed =
quantity * totalIngredientsInRecipe(recipe)
const canBeMade =
quantity > 0 &&
canMakeRecipe(recipe, inventory, quantity) &&
// @ts-expect-error
doesInventorySpaceRemain({
inventory,
// Without the Infinity coercion, this would break recipes for unlimited
// inventoryLimits.
inventoryLimit:
(inventoryLimit === INFINITE_STORAGE_LIMIT
? Infinity
: inventoryLimit) + spaceFreedByIngredientsConsumed,
})
const handleMakeRecipe = () => {
if (canBeMade) {
handleMakeRecipeClick(recipe, quantity)
}
}
const maxQuantity = maxYieldOfRecipe(recipe, inventory)
return (
<Card
{...{
className: classNames('Recipe', { 'can-be-made': canBeMade }),
}}
>
<CardHeader
{...{
avatar: <img {...{ src: craftedItems[id], alt: name }} />,
title: name,
subheader: (
<>
<p>Sell price: {dollarString(itemsMap[id].value)}</p>
<p>
In Inventory: {integerString(playerInventoryQuantities[id])}
</p>
<IngredientsList
{...{
playerInventoryQuantities,
recipe: /** @type {globalThis.farmhand.recipe} */ (recipe),
}}
/>
</>
),
}}
/>
{description && (
<CardContent>
<Typography>{description}</Typography>
</CardContent>
)}
<CardActions>
<Button
{...{
className: 'make-recipe',
color: 'primary',
disabled: !canBeMade || !quantity,
onClick: handleMakeRecipe,
variant: 'contained',
}}
>
Make
</Button>
<QuantityInput
{...{
handleSubmit: handleMakeRecipe,
handleUpdateNumber: setQuantity,
maxQuantity,
setQuantity,
value: quantity,
}}
/>
</CardActions>
</Card>
)
}
Recipe.propTypes = {
handleMakeRecipeClick: func.isRequired,
inventory: array.isRequired,
inventoryLimit: number.isRequired,
playerInventoryQuantities: object.isRequired,
recipe: object.isRequired,
}
export { Recipe }
export default function Consumer(props) {
return (
<FarmhandContext.Consumer>
{({ gameState, handlers }) => (
<Recipe {...{ ...gameState, ...handlers, ...props }} />
)}
</FarmhandContext.Consumer>
)
}