@@ -1,6 +1,7 @@
|
||||
import { AnimatePresence, motion } from 'motion/react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { normalizePlayerProgressionState } from '../data/playerProgression';
|
||||
import {
|
||||
resolveAttributeSchema,
|
||||
resolveCharacterAttributeProfile,
|
||||
@@ -57,8 +58,10 @@ import {
|
||||
} from './CharacterInfoHelpers';
|
||||
import {
|
||||
CharacterAttributeGrid,
|
||||
CharacterIdentityBadges,
|
||||
CharacterSkillsList,
|
||||
MultiplierContributionList,
|
||||
PlayerLevelProgress,
|
||||
StatusRow,
|
||||
} from './CharacterInfoShared';
|
||||
import type { GameCanvasEntitySelection } from './GameCanvas';
|
||||
@@ -69,6 +72,7 @@ interface CharacterPanelProps {
|
||||
worldType: WorldType | null;
|
||||
customWorldProfile?: CustomWorldProfile | null;
|
||||
playerCharacter: Character;
|
||||
playerProgression?: GameState['playerProgression'] | null;
|
||||
playerHp: number;
|
||||
playerMaxHp: number;
|
||||
playerMana: number;
|
||||
@@ -97,6 +101,7 @@ type PartyMember = {
|
||||
mana: number;
|
||||
maxMana: number;
|
||||
isLeader: boolean;
|
||||
levelText: string | null;
|
||||
};
|
||||
|
||||
type EquipmentRow = {
|
||||
@@ -140,6 +145,7 @@ export function CharacterPanel({
|
||||
worldType,
|
||||
customWorldProfile = null,
|
||||
playerCharacter,
|
||||
playerProgression = null,
|
||||
playerHp,
|
||||
playerMaxHp,
|
||||
playerMana,
|
||||
@@ -157,6 +163,10 @@ export function CharacterPanel({
|
||||
const [selectedContributionLabel, setSelectedContributionLabel] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const normalizedPlayerProgression =
|
||||
normalizePlayerProgressionState(playerProgression);
|
||||
const leaderLevelText = `Lv.${normalizedPlayerProgression.level}`;
|
||||
const companionReferenceLevelText = `参考 Lv.${normalizedPlayerProgression.level}`;
|
||||
|
||||
const partyMembers = useMemo<PartyMember[]>(
|
||||
() => [
|
||||
@@ -165,28 +175,32 @@ export function CharacterPanel({
|
||||
npcId: null,
|
||||
renderState: null,
|
||||
character: playerCharacter,
|
||||
roleLabel: '闃熼暱',
|
||||
roleLabel: '\u961f\u957f',
|
||||
hp: playerHp,
|
||||
maxHp: playerMaxHp,
|
||||
mana: playerMana,
|
||||
maxMana: playerMaxMana,
|
||||
isLeader: true,
|
||||
levelText: leaderLevelText,
|
||||
},
|
||||
...companionRenderStates.map((companion) => ({
|
||||
id: companion.npcId,
|
||||
npcId: companion.npcId,
|
||||
renderState: companion,
|
||||
character: companion.character,
|
||||
roleLabel: '鍚岃',
|
||||
roleLabel: '\u540c\u884c',
|
||||
hp: companion.hp,
|
||||
maxHp: companion.maxHp,
|
||||
mana: companion.mana,
|
||||
maxMana: companion.maxMana,
|
||||
isLeader: false,
|
||||
levelText: companionReferenceLevelText,
|
||||
})),
|
||||
],
|
||||
[
|
||||
companionReferenceLevelText,
|
||||
companionRenderStates,
|
||||
leaderLevelText,
|
||||
playerCharacter,
|
||||
playerHp,
|
||||
playerMaxHp,
|
||||
@@ -257,15 +271,16 @@ export function CharacterPanel({
|
||||
: null;
|
||||
const selectedMemberArcState =
|
||||
selectedMember && !selectedMember.isLeader
|
||||
? companionArcStates.find(
|
||||
? (companionArcStates.find(
|
||||
(arcState) => arcState.characterId === selectedMember.character.id,
|
||||
) ?? null
|
||||
) ?? null)
|
||||
: null;
|
||||
const selectedMemberResolution =
|
||||
selectedMember && !selectedMember.isLeader
|
||||
? companionResolutions.find(
|
||||
(resolution) => resolution.characterId === selectedMember.character.id,
|
||||
) ?? null
|
||||
? (companionResolutions.find(
|
||||
(resolution) =>
|
||||
resolution.characterId === selectedMember.character.id,
|
||||
) ?? null)
|
||||
: null;
|
||||
const selectedMemberPublicBackstory =
|
||||
selectedMember && !selectedMember.isLeader && selectedMemberAffinity != null
|
||||
@@ -410,11 +425,12 @@ export function CharacterPanel({
|
||||
{member.character.title}
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-[10px] ${member.isLeader ? 'bg-amber-500/10 text-amber-100' : 'bg-sky-500/10 text-sky-100'}`}
|
||||
>
|
||||
{member.roleLabel}
|
||||
</span>
|
||||
<CharacterIdentityBadges
|
||||
roleLabel={member.roleLabel}
|
||||
levelText={member.levelText}
|
||||
roleTone={member.isLeader ? 'amber' : 'sky'}
|
||||
className="shrink-0 justify-end"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2.5 space-y-2.5">
|
||||
<StatusRow
|
||||
@@ -591,8 +607,15 @@ export function CharacterPanel({
|
||||
<div className="mt-1 truncate text-sm font-semibold text-white">
|
||||
{selectedMember.character.name}
|
||||
</div>
|
||||
<div className="mt-1 flex items-center gap-2 text-[10px] tracking-[0.2em] text-zinc-500">
|
||||
<span>{selectedMember.character.title}</span>
|
||||
<div className="mt-1 text-[10px] tracking-[0.2em] text-zinc-500">
|
||||
{selectedMember.character.title}
|
||||
</div>
|
||||
<div className="mt-2 flex flex-wrap items-center gap-2">
|
||||
<CharacterIdentityBadges
|
||||
roleLabel={selectedMember.roleLabel}
|
||||
levelText={selectedMember.levelText}
|
||||
roleTone={selectedMember.isLeader ? 'amber' : 'sky'}
|
||||
/>
|
||||
<span className="rounded-full border border-white/10 bg-black/20 px-2 py-0.5 text-[9px] text-zinc-200">
|
||||
{getGenderLabel(selectedMember.character.gender)}
|
||||
</span>
|
||||
@@ -617,7 +640,9 @@ export function CharacterPanel({
|
||||
<div className="flex h-36 w-full max-w-[15rem] items-end justify-center overflow-hidden rounded-2xl border border-white/10 bg-black/20 sm:h-40">
|
||||
{selectedMember.character.visual ? (
|
||||
<MedievalNpcAnimator
|
||||
visualSpec={buildMedievalNpcVisualFromCustomWorldVisual(selectedMember.character.visual)}
|
||||
visualSpec={buildMedievalNpcVisualFromCustomWorldVisual(
|
||||
selectedMember.character.visual,
|
||||
)}
|
||||
scale={2.08}
|
||||
/>
|
||||
) : (
|
||||
@@ -652,6 +677,22 @@ export function CharacterPanel({
|
||||
状态
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{selectedMember.isLeader && (
|
||||
<div className="rounded-xl border border-amber-300/18 bg-amber-500/8 px-3 py-3">
|
||||
<div className="mb-2 text-[10px] uppercase tracking-[0.18em] text-amber-100/75">
|
||||
等级
|
||||
</div>
|
||||
<PlayerLevelProgress
|
||||
level={normalizedPlayerProgression.level}
|
||||
currentLevelXp={
|
||||
normalizedPlayerProgression.currentLevelXp
|
||||
}
|
||||
xpToNextLevel={
|
||||
normalizedPlayerProgression.xpToNextLevel
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<StatusRow
|
||||
label={resourceLabels.hp}
|
||||
current={selectedMember.hp}
|
||||
|
||||
Reference in New Issue
Block a user