@jeremyckahn/farmhand
Version:
A farming game
304 lines (290 loc) • 10.1 kB
JavaScript
import React, { useState } from 'react'
import { array, func, number, object } from 'prop-types'
import Button from '@mui/material/Button/index.js'
import Card from '@mui/material/Card/index.js'
import CardActions from '@mui/material/CardActions/index.js'
import CardContent from '@mui/material/CardContent/index.js'
import CardHeader from '@mui/material/CardHeader/index.js'
import Tab from '@mui/material/Tab/index.js'
import Tabs from '@mui/material/Tabs/index.js'
import Typography from '@mui/material/Typography/index.js'
import FarmhandContext from '../Farmhand/Farmhand.context.js'
import { features } from '../../config.js'
import { moneyString } from '../../utils/moneyString.js'
import {
dollarString,
getCostOfNextStorageExpansion,
integerString,
} from '../../utils/index.js'
import { memoize } from '../../utils/memoize.js'
import { items } from '../../img/index.js'
import { itemType, stageFocusType, toolType } from '../../enums.js'
import {
INFINITE_STORAGE_LIMIT,
PURCHASEABLE_CELLARS,
PURCHASEABLE_COMBINES,
PURCHASEABLE_COMPOSTERS,
PURCHASEABLE_COW_PENS,
PURCHASEABLE_FIELD_SIZES,
PURCHASABLE_FOREST_SIZES,
PURCHASEABLE_SMELTERS,
STORAGE_EXPANSION_AMOUNT,
} from '../../constants.js'
import Inventory from '../Inventory/index.js'
import TierPurchase from '../TierPurchase/index.js'
import { TabPanel, a11yProps } from './TabPanel/index.js'
import './Shop.sass'
/**
* @param {Array.<farmhand.item>} shopInventory
* @returns {Object.<'seeds' | 'fieldTools', Array.<farmhand.item>>}
*/
const categorizeShopInventory = memoize(shopInventory =>
shopInventory.reduce(
(acc, inventoryItem) => {
acc[inventoryItem.type === itemType.CROP ? 'seeds' : 'fieldTools'].push(
inventoryItem
)
return acc
},
{ seeds: [], fieldTools: [] }
)
)
export const Shop = ({
handleCombinePurchase,
handleComposterPurchase,
handleCowPenPurchase,
handleCellarPurchase,
handleFieldPurchase,
handleForestPurchase,
handleSmelterPurchase,
handleStorageExpansionPurchase,
inventoryLimit,
levelEntitlements,
money,
purchasedCombine,
purchasedComposter,
purchasedCowPen,
purchasedCellar,
purchasedField,
purchasedForest,
purchasedSmelter,
shopInventory,
toolLevels,
storageUpgradeCost = getCostOfNextStorageExpansion(inventoryLimit),
}) => {
const [currentTab, setCurrentTab] = useState(0)
const { seeds, fieldTools } = categorizeShopInventory(shopInventory)
const isForestUnlocked =
levelEntitlements.stageFocusType[stageFocusType.FOREST]
return (
<div className="Shop">
<Tabs
value={currentTab}
onChange={(_e, newTab) => setCurrentTab(newTab)}
aria-label="Shop tabs"
>
<Tab {...{ label: 'Seeds', ...a11yProps(0) }} />
<Tab {...{ label: 'Supplies', ...a11yProps(1) }} />
<Tab {...{ label: 'Upgrades', ...a11yProps(2) }} />
</Tabs>
<TabPanel value={currentTab} index={0}>
<Inventory
{...{
items: seeds,
isPurchaseView: true,
placeholder: 'Search seeds...',
}}
/>
</TabPanel>
<TabPanel value={currentTab} index={1}>
<Inventory
{...{
items: fieldTools,
isPurchaseView: true,
placeholder: 'Search supplies...',
}}
/>
</TabPanel>
<TabPanel value={currentTab} index={2}>
<ul className="card-list">
{inventoryLimit > INFINITE_STORAGE_LIMIT && (
<li>
<Card>
<CardHeader
{...{
avatar: (
<img
{...{ src: items['inventory-box'] }}
alt={'Inventory box'}
/>
),
title: 'Storage Unit',
subheader: (
<div>
<p>Price: {moneyString(storageUpgradeCost)}</p>
<p>
Current inventory space:{' '}
{integerString(inventoryLimit)}
</p>
</div>
),
}}
/>
<CardContent>
<Typography>
Purchase a Storage Unit to increase your inventory capacity
for {STORAGE_EXPANSION_AMOUNT} more items.
</Typography>
</CardContent>
<CardActions>
<Button
{...{
disabled: money < storageUpgradeCost,
color: 'primary',
onClick: handleStorageExpansionPurchase,
variant: 'contained',
}}
>
Buy
</Button>
</CardActions>
</Card>
</li>
)}
<li>
<TierPurchase
{...{
onBuyClick: handleFieldPurchase,
maxedOutPlaceholder:
"You've purchased the largest field available!",
purchasedTier: purchasedField,
renderTierLabel: ({ columns, price, rows }) =>
`${dollarString(price)}: ${columns} x ${rows}`,
tiers: PURCHASEABLE_FIELD_SIZES,
title: 'Expand field',
}}
/>
</li>
<li>
<TierPurchase
{...{
onBuyClick: handleCowPenPurchase,
maxedOutPlaceholder:
"You've purchased the largest cow pen available!",
purchasedTier: purchasedCowPen,
renderTierLabel: ({ cows, price }) =>
`${dollarString(price)}: ${cows} cow pen`,
tiers: PURCHASEABLE_COW_PENS,
title: 'Buy cow pen',
}}
/>
</li>
<li>
<TierPurchase
{...{
onBuyClick: handleCellarPurchase,
maxedOutPlaceholder:
"You've purchased the largest cellar available!",
purchasedTier: purchasedCellar,
renderTierLabel: ({ space, price }) =>
`${dollarString(price)}: Space for ${space} kegs`,
tiers: PURCHASEABLE_CELLARS,
title: 'Buy cellar',
}}
/>
</li>
{features.FOREST && isForestUnlocked ? (
<li>
<TierPurchase
{...{
onBuyClick: handleForestPurchase,
maxedOutPlaceholder:
"You've purchased the largest forest available!",
purchasedTier: purchasedForest,
renderTierLabel: ({ columns, price, rows }) =>
`${dollarString(price)}: ${columns} x ${rows}`,
tiers: PURCHASABLE_FOREST_SIZES,
title: 'Expand Forest',
}}
/>
</li>
) : null}
<li>
<TierPurchase
{...{
description:
'You can purchase a combine to automatically harvest your mature crops at the start of every day.',
onBuyClick: handleCombinePurchase,
maxedOutPlaceholder:
"You've purchased the best combine harvester available!",
purchasedTier: purchasedCombine,
renderTierLabel: ({ type, price }) =>
`${dollarString(price)}: ${type} combine harvester`,
tiers: PURCHASEABLE_COMBINES,
title: 'Buy combine harvester',
}}
/>
</li>
{toolLevels[toolType.SHOVEL] ? (
<li>
<TierPurchase
{...{
description:
'You can purchase a Smelter to convert ore into ingots and other useful items.',
onBuyClick: handleSmelterPurchase,
maxedOutPlaceholder: "You've already purchased the smelter!",
purchasedTier: purchasedSmelter,
renderTierLabel: ({ type, price }) =>
`${dollarString(price)}: ${type} Smelter`,
tiers: PURCHASEABLE_SMELTERS,
title: 'Buy smelter',
}}
/>
</li>
) : null}
<li>
<TierPurchase
{...{
description:
'You can purchase a Composter to turn weeds into fertilizer.',
onBuyClick: handleComposterPurchase,
maxedOutPlaceholder: "You've already purchased the composter!",
purchasedTier: purchasedComposter,
renderTierLabel: ({ type, price }) =>
`${dollarString(price)}: ${type} Composter`,
tiers: PURCHASEABLE_COMPOSTERS,
title: 'Buy composter',
}}
/>
</li>
</ul>
</TabPanel>
</div>
)
}
Shop.propTypes = {
handleCombinePurchase: func.isRequired,
handleCowPenPurchase: func.isRequired,
handleCellarPurchase: func.isRequired,
handleFieldPurchase: func.isRequired,
handleStorageExpansionPurchase: func.isRequired,
inventoryLimit: number.isRequired,
money: number.isRequired,
purchasedCowPen: number.isRequired,
purchasedCellar: number.isRequired,
purchasedField: number.isRequired,
purchasedSmelter: number.isRequired,
purchasedCombine: number.isRequired,
shopInventory: array.isRequired,
toolLevels: object.isRequired,
valueAdjustments: object.isRequired,
}
export default function Consumer(props) {
return (
<FarmhandContext.Consumer>
{({ gameState, handlers }) => (
<Shop {...{ ...gameState, ...handlers, ...props }} />
)}
</FarmhandContext.Consumer>
)
}