99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import type { PartyMember } from '../game'
|
|
|
|
export type NavigateAction = 'navigateLeft' | 'navigateRight' | 'navigateUp' | 'navigateDown'
|
|
|
|
type TargetOptions = {
|
|
livingOnly?: boolean
|
|
}
|
|
|
|
function canTarget(member: PartyMember, options: TargetOptions) {
|
|
return !options.livingOnly || member.health > 0
|
|
}
|
|
|
|
export function selectRelativePartyTarget(
|
|
party: PartyMember[],
|
|
selectedId: string,
|
|
direction: -1 | 1,
|
|
options: TargetOptions = {},
|
|
) {
|
|
const candidates = party.filter((member) => canTarget(member, options))
|
|
if (candidates.length === 0) return null
|
|
const currentIndex = candidates.findIndex((member) => member.id === selectedId)
|
|
const nextIndex = currentIndex < 0
|
|
? 0
|
|
: (currentIndex + direction + candidates.length) % candidates.length
|
|
return candidates[nextIndex]?.id ?? null
|
|
}
|
|
|
|
export function selectDirectionalPartyTarget(
|
|
party: PartyMember[],
|
|
selectedId: string,
|
|
action: NavigateAction,
|
|
columns: number,
|
|
options: TargetOptions = {},
|
|
) {
|
|
const currentIndex = party.findIndex((member) => member.id === selectedId)
|
|
if (currentIndex < 0) {
|
|
return party.find((member) => canTarget(member, options))?.id ?? null
|
|
}
|
|
const currentRow = Math.floor(currentIndex / columns)
|
|
const currentColumn = currentIndex % columns
|
|
const horizontal = action === 'navigateLeft' || action === 'navigateRight'
|
|
const candidate = party
|
|
.map((member, index) => ({
|
|
member,
|
|
index,
|
|
row: Math.floor(index / columns),
|
|
column: index % columns,
|
|
}))
|
|
.filter(({ member, index, row, column }) => {
|
|
if (!canTarget(member, options) || index === currentIndex) return false
|
|
if (action === 'navigateLeft') return row === currentRow && column < currentColumn
|
|
if (action === 'navigateRight') return row === currentRow && column > currentColumn
|
|
if (action === 'navigateUp') return row < currentRow
|
|
return row > currentRow
|
|
})
|
|
.sort((left, right) => {
|
|
const leftPrimary = horizontal
|
|
? Math.abs(left.column - currentColumn)
|
|
: Math.abs(left.row - currentRow)
|
|
const rightPrimary = horizontal
|
|
? Math.abs(right.column - currentColumn)
|
|
: Math.abs(right.row - currentRow)
|
|
const leftSecondary = horizontal ? 0 : Math.abs(left.column - currentColumn)
|
|
const rightSecondary = horizontal ? 0 : Math.abs(right.column - currentColumn)
|
|
return leftPrimary - rightPrimary || leftSecondary - rightSecondary
|
|
})[0]
|
|
return candidate?.member.id ?? null
|
|
}
|
|
|
|
export function selectDirectPartyTarget(
|
|
party: PartyMember[],
|
|
slot: number,
|
|
options: TargetOptions & {
|
|
targetGroup?: number
|
|
groupSize?: number
|
|
} = {},
|
|
) {
|
|
const groupSize = options.groupSize ?? 6
|
|
const index = slot + (options.targetGroup ?? 0) * groupSize
|
|
const member = party[index]
|
|
return member && canTarget(member, options) ? member.id : null
|
|
}
|
|
|
|
export function nextTargetGroupSelection(
|
|
party: PartyMember[],
|
|
selectedId: string,
|
|
currentGroup: number,
|
|
groupSize = 6,
|
|
) {
|
|
const groupCount = Math.max(1, Math.ceil(party.length / groupSize))
|
|
const nextGroup = (currentGroup + 1) % groupCount
|
|
const selectedIndex = party.findIndex((member) => member.id === selectedId)
|
|
const slot = selectedIndex < 0 ? 0 : selectedIndex % groupSize
|
|
return {
|
|
group: nextGroup,
|
|
selectedId: party[slot + nextGroup * groupSize]?.id ?? null,
|
|
}
|
|
}
|