@jeremyckahn/farmhand
Version:
A farming game
289 lines (249 loc) • 7.43 kB
JavaScript
/** @typedef {import('../components/Farmhand/Farmhand.js').default} Farmhand */
import { cowTradeRejectionReason } from '../enums.js'
import { EXPERIENCE_VALUES } from '../constants.js'
import { COW_TRADED_NOTIFICATION } from '../templates.js'
import {
PROGRESS_SAVED_MESSAGE,
REQUESTED_COW_TRADE_UNAVAILABLE,
UNKNOWN_COW_TRADE_FAILURE,
} from '../strings.js'
import { sleep } from '../utils/index.js'
import {
addCowToInventory,
changeCowAutomaticHugState,
removeCowFromInventory,
showNotification,
} from '../game-logic/reducers/index.js'
import { addExperience } from '../game-logic/reducers/addExperience.js'
/**
* @param {import('../components/Farmhand/Farmhand.js').default} farmhand
* @param {farmhand.peerMetadata} peerMetadata
* @param {string} peerId
*/
export const handlePeerMetadataRequest = (farmhand, peerMetadata, peerId) => {
farmhand.updatePeer(farmhand, peerMetadata, peerId)
}
/**
* Handles another player's initiation of a cow trade request.
* @param {Farmhand} farmhand
* @param {Object} cowTradeRequestPayload
* @param {farmhand.cow} cowTradeRequestPayload.cowOffered
* @param {farmhand.cow} cowTradeRequestPayload.cowRequested
* @param {string} peerId
*/
export const handleCowTradeRequest = async (
farmhand,
{ cowOffered, cowRequested },
peerId
) => {
let wasTradeSuccessful = false
farmhand.setState(
state => {
const {
allowCustomPeerCowNames,
cowIdOfferedForTrade,
cowsTraded,
cowInventory,
playerId,
isAwaitingCowTradeRequest,
peers,
sendCowAccept,
sendCowReject,
} = state
if (!sendCowAccept || !sendCowReject) {
console.error('Peer connection not set up correctly')
return null
}
const cowToTradeAway = cowInventory.find(
({ id }) => id === cowIdOfferedForTrade
)
if (
isAwaitingCowTradeRequest ||
cowRequested.id !== cowIdOfferedForTrade ||
!cowToTradeAway
) {
sendCowReject(
{
reason: cowTradeRejectionReason.REQUESTED_COW_UNAVAILABLE,
},
peerId
)
return null
}
const updatedCowOffered = {
...cowOffered,
timesTraded:
cowOffered.originalOwnerId === playerId
? cowOffered.timesTraded
: cowOffered.timesTraded + 1,
}
const [, peerMetadata] =
Object.entries(peers).find(
([, peer]) => peer?.playerId === updatedCowOffered.ownerId
) ?? []
if (!peerMetadata) {
console.error(`No data for peer ${updatedCowOffered.ownerId}`)
return null
}
state = changeCowAutomaticHugState(state, cowToTradeAway, false)
state = removeCowFromInventory(state, cowToTradeAway)
state = addCowToInventory(state, {
...updatedCowOffered,
ownerId: playerId,
})
state = showNotification(
state,
COW_TRADED_NOTIFICATION(
'',
cowToTradeAway,
cowOffered,
playerId,
allowCustomPeerCowNames
),
'success'
)
sendCowAccept({ ...cowToTradeAway, isUsingHuggingMachine: false }, peerId)
wasTradeSuccessful = true
return {
...state,
cowIdOfferedForTrade: updatedCowOffered.id,
cowsTraded:
updatedCowOffered.originalOwnerId === playerId
? cowsTraded
: cowsTraded + 1,
isAwaitingCowTradeRequest: true,
selectedCowId: updatedCowOffered.id,
peers: {
...peers,
[peerId]: {
...peerMetadata,
cowOfferedForTrade: {
...cowToTradeAway,
ownerId: peerMetadata.playerId,
},
},
},
}
},
async () => {
if (!wasTradeSuccessful) return
// Allow any pending state updates to complete so that an invalid state
// is not persisted
await sleep(750)
await farmhand.persistState()
farmhand.showNotification(PROGRESS_SAVED_MESSAGE, 'info')
farmhand.setState(() => ({
isAwaitingCowTradeRequest: false,
}))
}
)
}
/**
* @param {Farmhand} farmhand
* @param {farmhand.cow} cowReceived
* @param {string} peerId
*/
export const handleCowTradeRequestAccept = (farmhand, cowReceived, peerId) => {
let wasTradeSuccessful = false
farmhand.setState(
state => {
const {
allowCustomPeerCowNames,
cowIdOfferedForTrade,
cowInventory,
cowsTraded,
cowTradeTimeoutId,
playerId,
peers,
} = state
const cowTradedAway = cowInventory.find(
({ id }) => id === cowIdOfferedForTrade
)
if (!cowTradedAway) {
console.error(
`handleCowTradeRequestAccept: cow with ID ${cowIdOfferedForTrade} is no longer available`
)
state = showNotification(state, UNKNOWN_COW_TRADE_FAILURE, 'error')
return {
...state,
isAwaitingCowTradeRequest: false,
}
}
const peerEntry = Object.entries(peers).find(
([, { playerId }]) => playerId === cowReceived.ownerId
)
const [, peerMetadata] = peerEntry || []
const didOriginallyOwnReceivedCow =
cowReceived.originalOwnerId === playerId
const updatedCowReceived = {
...cowReceived,
timesTraded: didOriginallyOwnReceivedCow
? cowReceived.timesTraded
: cowReceived.timesTraded + 1,
}
state = removeCowFromInventory(state, cowTradedAway)
state = addCowToInventory(state, {
...updatedCowReceived,
ownerId: playerId,
})
state = showNotification(
state,
COW_TRADED_NOTIFICATION(
'',
cowTradedAway,
updatedCowReceived,
playerId,
allowCustomPeerCowNames
),
'success'
)
if (!didOriginallyOwnReceivedCow) {
state = addExperience(state, EXPERIENCE_VALUES.COW_TRADED)
}
clearTimeout(cowTradeTimeoutId)
wasTradeSuccessful = true
return {
...state,
cowsTraded:
updatedCowReceived.originalOwnerId === playerId
? cowsTraded
: cowsTraded + 1,
cowTradeTimeoutId: null,
isAwaitingCowTradeRequest: false,
cowIdOfferedForTrade: updatedCowReceived.id,
selectedCowId: updatedCowReceived.id,
peers: {
...peers,
[peerId]: {
...peerMetadata,
cowOfferedForTrade: {
...cowTradedAway,
ownerId: peerMetadata.playerId,
},
},
},
}
},
async () => {
if (!wasTradeSuccessful) return
await farmhand.persistState()
farmhand.showNotification(PROGRESS_SAVED_MESSAGE, 'info')
}
)
}
/**
* @param {Farmhand} farmhand
* @param {Object} cowTradeRejectionPayload
* @param {string} cowTradeRejectionPayload.reason
*/
export const handleCowTradeRequestReject = (farmhand, { reason }) => {
const { cowTradeTimeoutId } = farmhand.state
if (typeof cowTradeTimeoutId === 'number') {
clearTimeout(cowTradeTimeoutId)
}
farmhand.setState({
cowTradeTimeoutId: null,
isAwaitingCowTradeRequest: false,
})
farmhand.showNotification(REQUESTED_COW_TRADE_UNAVAILABLE, 'error')
}