updating buff and debuff icons. separated COA classes from WoW classes on the character creation screen

This commit is contained in:
phenom committed 2026-08-16 11:16:51 -04:00
1 parent b5c9bce1aa
commit c00f3211ea
39 files changed
+2037 -208

No files matched your search

+34 -13
View File
@@ -39,9 +39,11 @@ import { resolveContentUrl } from "../content/contentManager";
import {
selectCharacterBaseClip,
selectCharacterEventClip,
selectCharacterVerticalClip,
type CharacterCombatAnimationEvent,
type CharacterBaseMotion,
} from "../game/combatAnimation";
import type { CharacterVerticalMotion } from "../game/playerJump";
import { AvatarErrorBoundary } from "./AvatarErrorBoundary";
import {
ATTACK_PRESENTATION_DURATION_SECONDS,
@@ -77,6 +79,7 @@ export interface CharacterModelProps {
identity: AvatarIdentity | null | undefined;
active?: boolean;
movingRef?: MutableRefObject<boolean>;
verticalMotionRef?: MutableRefObject<CharacterVerticalMotion>;
attackPulseRef?: MutableRefObject<number>;
animationEventRef?: MutableRefObject<CharacterCombatAnimationEvent | null>;
onReady?: () => void;
@@ -88,6 +91,8 @@ export interface CharacterModelProps {
const EMPTY_EQUIPMENT: readonly InventoryItem[] = Object.freeze([]);
type CharacterLocomotionMotion = CharacterBaseMotion | Exclude<CharacterVerticalMotion, "grounded">;
type MappedMaterial = Material & { map?: Texture | null; color?: Color };
function configureMaterial(material: Material): void {
@@ -164,6 +169,7 @@ function LoadedCharacterModel({
identity,
manifest,
movingRef,
verticalMotionRef,
attackPulseRef,
animationEventRef,
onReady,
@@ -176,6 +182,7 @@ function LoadedCharacterModel({
identity: AvatarIdentity;
manifest: AvatarManifest;
movingRef?: MutableRefObject<boolean>;
verticalMotionRef?: MutableRefObject<CharacterVerticalMotion>;
attackPulseRef?: MutableRefObject<number>;
animationEventRef?: MutableRefObject<CharacterCombatAnimationEvent | null>;
onReady?: () => void;
@@ -190,7 +197,7 @@ function LoadedCharacterModel({
const overrideAction = useRef<AnimationAction | null>(null);
const overrideTerminal = useRef(false);
const overrideLooping = useRef(false);
const currentMotion = useRef<CharacterBaseMotion | null>(null);
const currentMotion = useRef<CharacterLocomotionMotion | null>(null);
const observedAttackPulse = useRef(attackPulseRef?.current ?? 0);
const equipmentAttackPulseRef = useRef(attackPulseRef?.current ?? 0);
const observedAnimationRevision = useRef(animationEventRef?.current?.revision ?? 0);
@@ -338,19 +345,27 @@ function LoadedCharacterModel({
?? gltf.animations[0]
);
const switchMotion = (motion: CharacterBaseMotion) => {
const switchMotion = (motion: CharacterLocomotionMotion, moving: boolean) => {
if (overrideAction.current) return;
if (currentMotion.current === motion) return;
const clip = selectClip(motion);
const verticalSelection = motion === "rising" || motion === "falling" || motion === "landing"
? selectCharacterVerticalClip(gltf.animations, motion, moving)
: null;
const fallbackMotion: CharacterBaseMotion = moving ? "run" : "idle";
const clip = verticalSelection?.clip ?? selectClip(
motion === "idle" || motion === "walk" || motion === "run" ? motion : fallbackMotion,
);
if (!clip) return;
const loop = verticalSelection?.loop ?? true;
const next = mixer.clipAction(clip, scene);
if (currentAction.current !== next) {
next.enabled = true;
next.clampWhenFinished = false;
next.setLoop(LoopRepeat, Number.POSITIVE_INFINITY).reset().fadeIn(0.16).play();
currentAction.current?.fadeOut(0.16);
currentAction.current = next;
}
next.enabled = true;
next.clampWhenFinished = !loop;
next.setLoop(loop ? LoopRepeat : LoopOnce, loop ? Number.POSITIVE_INFINITY : 1)
.reset()
.fadeIn(0.16)
.play();
if (currentAction.current !== next) currentAction.current?.fadeOut(0.16);
currentAction.current = next;
currentMotion.current = motion;
};
@@ -415,7 +430,7 @@ function LoadedCharacterModel({
};
useEffect(() => {
switchMotion("idle");
switchMotion("idle", false);
return () => {
mixer.stopAllAction();
mixer.uncacheRoot(scene);
@@ -439,6 +454,10 @@ function LoadedCharacterModel({
useFrame((_, delta) => {
if (!active) return;
const moving = !preview && Boolean(movingRef?.current);
const verticalMotion = preview ? "grounded" : verticalMotionRef?.current ?? "grounded";
const locomotionMotion: CharacterLocomotionMotion = verticalMotion === "grounded"
? moving ? "run" : "idle"
: verticalMotion;
const nextAnimationEvent = animationEventRef?.current;
if (nextAnimationEvent && nextAnimationEvent.revision !== observedAnimationRevision.current) {
observedAnimationRevision.current = nextAnimationEvent.revision;
@@ -455,7 +474,7 @@ function LoadedCharacterModel({
playAnimationEvent({ revision: nextAttackPulse, kind: "attack" });
}
}
switchMotion(moving ? "run" : "idle");
switchMotion(locomotionMotion, moving);
const safeDelta = Math.min(delta, 0.05);
mixer.update(safeDelta);
@@ -468,7 +487,7 @@ function LoadedCharacterModel({
&& !overrideLooping.current
&& !overrideAction.current.isRunning()
) clearOverride();
switchMotion(moving ? "run" : "idle");
switchMotion(locomotionMotion, moving);
const swing = attackSwingAt(attackElapsed.current);
attackElapsed.current = Math.min(
ATTACK_PRESENTATION_DURATION_SECONDS,
@@ -514,6 +533,7 @@ export function CharacterModel({
identity,
active = true,
movingRef,
verticalMotionRef,
attackPulseRef,
animationEventRef,
onReady,
@@ -546,6 +566,7 @@ export function CharacterModel({
identity={identity}
manifest={manifest}
movingRef={movingRef}
verticalMotionRef={verticalMotionRef}
attackPulseRef={attackPulseRef}
animationEventRef={animationEventRef}
onReady={onReady}