import { animated } from "@react-spring/web"; import {ReactElement, useMemo} from "react"; import { evaluateResourceBarStyles, ResourceBarColors, ResourceBarStyles } from "./resource_bar"; import {isDefined} from "./type_check"; import { SpringyValueInterpolatables, useSpringyValue } from "./SpringyValueHook"; import "./CharacterStatus.css"; export enum CharacterHealth { Full = "Full", Healthy = "Healthy", Wounded = "Wounded", Crisis = "Crisis", Peril = "Peril", KO = "KO", } export enum CharacterTurnState { None = "None", Ready = "Ready", HighTurns = "HighTurns", Done = "Done", CantAct = "CantAct", KO = "KO", } export enum SPType { UltimaPoints = "Ultima", FabulaPoints = "Fabula", } export interface StatusEffect { name: string iconUrl: string } export interface Character { portraitUrl?: string name?: string level?: number hp?: number maxHp?: number health?: CharacterHealth mp?: number maxMp?: number ip?: number maxIp?: number sp?: number spType?: SPType turnsLeft?: number turnsTotal?: number canAct?: boolean statuses?: StatusEffect[] } const hpBarStyle: SpringyValueInterpolatables = { foreground: 'linear-gradient(to bottom, rgb(255, 255, 255, 0.1) 0%, rgb(0, 0, 0, 0) 30% 50%, rgb(0, 0, 0, 0.1) 80%, rgb(0, 0, 0, 0.2) 95%, rgb(0, 0, 0, 0.3) 100%)', barDirection: "to right", barColors: ({flashValue}: {flashValue: number}): ResourceBarColors => { return { emptiedColor: `rgb(${Math.min(1, Math.max(flashValue, 0)) * 55 + 55}, 55, 55)`, toEmptyColor: 'rgb(200, 0, 0)', toFillColor: 'rgb(150, 250, 250)', filledColor: 'rgb(0, 200, 0)', } }, } const mpBarStyle: SpringyValueInterpolatables = { foreground: 'linear-gradient(to bottom, rgb(255, 255, 255, 0.1) 0%, rgb(0, 0, 0, 0) 30% 50%, rgb(0, 0, 0, 0.1) 80%, rgb(0, 0, 0, 0.2) 95%, rgb(0, 0, 0, 0.3) 100%)', barColors: ({flashValue}: {flashValue: number}): ResourceBarColors => { return { emptiedColor: `rgb(${Math.min(1, Math.max(flashValue, 0)) * 55 + 55}, 55, 55)`, toEmptyColor: 'rgb(250, 250, 60)', toFillColor: 'rgb(150, 250, 250)', filledColor: 'rgb(0, 150, 255)', } }, } const ipBarStyle: SpringyValueInterpolatables = { foreground: 'linear-gradient(to bottom, rgb(255, 255, 255, 0.1) 0%, rgb(0, 0, 0, 0) 30% 50%, rgb(0, 0, 0, 0.1) 80%, rgb(0, 0, 0, 0.2) 95%, rgb(0, 0, 0, 0.3) 100%)', barColors: ({flashValue}: {flashValue: number}): ResourceBarColors => { return { emptiedColor: `rgb(${Math.min(1, Math.max(flashValue, 0)) * 55 + 55}, 55, 55)`, toEmptyColor: 'rgb(160, 55, 195)', toFillColor: 'rgb(250, 250, 60)', filledColor: 'rgb(255, 150, 55)', } }, } export function CharacterStatus({character}: {character: Character}): ReactElement { const {name, level, health} = character const {hp, maxHp} = character const {interpolate: hpInterpolate} = useSpringyValue({ current: hp, max: maxHp, flash: isDefined(maxHp) && isDefined(hp) && hp * 2 < maxHp && hp > 0, }) const {hpText, hpBarStyleInterpolated} = useMemo(() => { if (isDefined(hp) && isDefined(maxHp) && maxHp > 0) { return { hpText: hpInterpolate(({recentValue}) => `${Math.round(recentValue)}`), hpBarStyleInterpolated: evaluateResourceBarStyles(hpBarStyle, hpInterpolate), } } else { return {} } }, [hp, maxHp, hpInterpolate]) const {mp, maxMp} = character const {interpolate: mpInterpolate} = useSpringyValue({ current: mp, max: maxMp, flash: false, }) const {mpText, mpBarStyleInterpolated} = useMemo(() => { if (isDefined(mp) && isDefined(maxMp) && maxMp > 0) { return { mpText: mpInterpolate(({recentValue}) => `${Math.round(recentValue)}`), mpBarStyleInterpolated: evaluateResourceBarStyles(mpBarStyle, mpInterpolate), } } else { return {} } }, [mp, maxMp, mpInterpolate]) const {ip, maxIp} = character const {interpolate: ipInterpolate} = useSpringyValue({ current: ip, max: maxIp, flash: false, }) const {ipText, ipBarStyleInterpolated} = useMemo(() => { if (isDefined(ip) && isDefined(maxIp) && maxIp > 0) { return { ipText: ipInterpolate(({recentValue}) => `${Math.round(recentValue)}`), ipBarStyleInterpolated: evaluateResourceBarStyles(ipBarStyle, ipInterpolate), } } else { return {} } }, [ip, maxIp, ipInterpolate]) const {sp, spType} = character const {interpolate: spInterpolate} = useSpringyValue({ current: sp, flash: isDefined(spType) && isDefined(sp) && sp > 0, }) const {spText} = useMemo(() => { if (isDefined(sp) && isDefined(spType)) { return { spText: spInterpolate(({recentValue}) => recentValue.toFixed(0)) } } else { return {} } }, [sp, spType, spInterpolate]) const {turnsLeft, turnsTotal, canAct} = character const {turnsState, turnsText} = useMemo(() => { if (isDefined(turnsTotal) && hp === 0 && isDefined(maxHp) && maxHp > 0) { return { turnsState: CharacterTurnState.KO, } } else if (isDefined(turnsTotal) && (canAct === false || turnsTotal === 0)) { return { turnsState: CharacterTurnState.CantAct, } } else if (isDefined(turnsTotal) && turnsLeft === 0) { return { turnsState: CharacterTurnState.Done, } } else if (turnsTotal === 1 && turnsLeft === 1) { return { turnsState: CharacterTurnState.Ready, } } else if (isDefined(turnsTotal) && turnsTotal > 1 && isDefined(turnsLeft)) { return { turnsState: CharacterTurnState.HighTurns, turnsText: `${turnsLeft}` } } else { return { turnsState: CharacterTurnState.None } } }, [hp, maxHp, canAct, turnsLeft, turnsTotal]) return
{isDefined(turnsState) &&
{turnsText}
}
{isDefined(level) &&
Lv {level}
} {isDefined(name) &&
{name}
}
{isDefined(hpText) &&
{hpText}
} {isDefined(mpText) &&
{mpText}
} {isDefined(ipText) &&
{ipText}
} {isDefined(spText) && {spText} }
}