<!--
Auto-generated by: https://github.com/threlte/threlte/tree/main/packages/gltf
Command: npx @threlte/gltf@3.0.1 ./static/characters/character-female-b.glb -s -t -u
-->
<script module lang="ts">
	export type ActionName =
		| 'static'
		| 'idle'
		| 'walk'
		| 'sprint'
		| 'jump'
		| 'fall'
		| 'crouch'
		| 'sit'
		| 'drive'
		| 'die'
		| 'pick-up'
		| 'emote-yes'
		| 'emote-no'
		| 'holding-right'
		| 'holding-left'
		| 'holding-both'
		| 'holding-right-shoot'
		| 'holding-left-shoot'
		| 'holding-both-shoot'
		| 'attack-melee-right'
		| 'attack-melee-left'
		| 'attack-kick-right'
		| 'attack-kick-left'
		| 'interact-right'
		| 'interact-left'
		| 'wheelchair-sit'
		| 'wheelchair-look-left'
		| 'wheelchair-look-right'
		| 'wheelchair-move-forward'
		| 'wheelchair-move-back'
		| 'wheelchair-move-left'
		| 'wheelchair-move-right';
</script>

<script lang="ts">
	import type * as THREE from 'three';
	import { Group } from 'three';
	import type { Snippet } from 'svelte';
	import { T, type Props } from '@threlte/core';
	import { useGltf, useGltfAnimations, useSuspense } from '@threlte/extras';
	import { base } from '$app/paths';

	let {
		fallback,
		error,
		children,
		ref = $bindable(),
		currentAction = 'idle',
		transitionDuration = 0.3,
		gender = 'female',
		version = 'b',
		...props
	}: Props<THREE.Group> & {
		ref?: THREE.Group;
		children?: Snippet<[{ ref: THREE.Group }]>;
		fallback?: Snippet;
		currentAction: ActionName | undefined;
		transitionDuration?: number;
		error?: Snippet<[{ error: Error }]>;
		gender: 'male' | 'female';
		version: 'a' | 'b' | 'c' | 'd' | 'e' | 'f';
	} = $props();

	const suspend = useSuspense();

	ref = new Group();

	type GLTFResult = {
		nodes: {
			['body-mesh']: THREE.SkinnedMesh;
			['head-mesh']: THREE.SkinnedMesh;
			root: THREE.Bone;
		};
		materials: {
			colormap: THREE.MeshStandardMaterial;
		};
	};

	const gltf = suspend(
		useGltf<GLTFResult>(
			base + `/characters/character-${gender}-${version}.glb${'?r=' + Math.random().toFixed(4)}`
		)
	);

	export const { actions, mixer } = useGltfAnimations<ActionName>(gltf, ref);

	let lastAction: ActionName | undefined = $state(undefined);

	$effect(() => {
		if (currentAction && $actions[currentAction] && lastAction !== currentAction) {
			if (!lastAction) {
				let action = $actions[currentAction];
				action?.play();
			} else {
				transitionTo(currentAction);
			}
			lastAction = currentAction;
		}
	});

	const transitionTo = (nextActionKey: ActionName, duration = transitionDuration) => {
		if (!lastAction) return;
		const action = $actions[lastAction];
		const nextAction = $actions[nextActionKey];

		if (!nextAction || action === nextAction) return;

		nextAction.enabled = true;
		if (action) {
			action.crossFadeTo(nextAction, duration, true);
		}
		nextAction.play();
	};
</script>

<T is={ref} dispose={false} {...props}>
	{#await gltf}
		{@render fallback?.()}
	{:then gltf}
		<T.Group name={'character'}>
			<T.Group name={'character_1'}>
				<T is={gltf.nodes.root} />
				<T.SkinnedMesh
					name="body-mesh"
					receiveShadow
					geometry={gltf.nodes['body-mesh'].geometry}
					material={gltf.materials.colormap}
					skeleton={gltf.nodes['body-mesh'].skeleton}
					material.flatShading={true}
				/>
				<T.SkinnedMesh
					name="head-mesh"
					receiveShadow
					geometry={gltf.nodes['head-mesh'].geometry}
					material={gltf.materials.colormap}
					skeleton={gltf.nodes['head-mesh'].skeleton}
					material.flatShading={true}
				/>
			</T.Group>
		</T.Group>
	{:catch err}
		{@render error?.({ error: err })}
	{/await}

	{@render children?.({ ref })}
</T>
