flyyt / src /useLiveFlyState.ts
gpue's picture
Deploy 771c7d3cfb0b08d632f7fe3d02e59b7bcdff47c4
5a3b695 verified
Raw History Blame Contribute Delete
3.17 kB
import { useRef } from "react";
import { createBrain, type BrainState } from "./flyBrain";
import { createGaitState, type GaitState } from "./tripodGait";
import { FLY_GROUND_OFFSET_MM, type FlyInstanceData } from "./useFlyLayout";
import { createWingControllerState, type WingControllerState } from "./wingController";
export interface VisionState {
/** Which other fly this one is currently tracking as "nearest visible", if any. */
trackedFlyId: string | null;
/** Last frame's distance to trackedFlyId — closing speed is the frame-to-frame delta of this. */
lastDistance: number;
}
export type OperatingMode = "AUTOMATIC" | "MANUAL";
export type ConnectionState = "ONLINE" | "OFFLINE" | "CONNECTION_BROKEN" | "UNKNOWN";
export interface LiveFlyState {
x: number;
y: number;
z: number;
heading: number;
gait: GaitState;
brain: BrainState;
wingController: WingControllerState;
vision: VisionState;
/** VDA5050 status, from flyyt-backend over NATS (useVda5050Nats.ts) — AUTOMATIC
* (backend-driven, order-following) makes FlyInstances.tsx consume x/z/heading
* from the backend instead of local joystick simulation; MANUAL is the opposite
* (this fly's own local joystick/gait is authoritative). Backend-less "practice
* mode" (no connection ever established) behaves exactly like today: MANUAL always. */
operatingMode: OperatingMode;
connectionState: ConnectionState;
batteryPercent: number;
orderId: string;
driving: boolean;
/** Flight mode -- true while this fly is commanded airborne (App.tsx's
* flying/altitude UI state syncs onto whichever fly is selected). */
flying: boolean;
/** What live.y eases toward each frame (FlyInstances.tsx) -- ground
* offset while grounded/landing, one of altitudeLevels.ts's levels while flying. */
targetAltitudeMm: number;
/** Downward speed while grounded and mid-fall (FlyInstances.tsx) -- real
* accelerating physics, not an eased glide; reset to 0 once landed or
* while flying, so the next fall always starts from rest. */
fallSpeedMmS: number;
/** Counts down from BITE_ANIM_DURATION_S (FlyInstances.tsx) -- set by
* App.tsx's onFeed on a successful bite, drives a transient dip/nod
* render-only animation, never affects live.x/y/z/targetAltitudeMm. */
biteAnimRemainingS: number;
}
export type LiveFlyPositions = Map<string, LiveFlyState>;
export function useLiveFlyState(flies: FlyInstanceData[]) {
return useRef<LiveFlyPositions>(
new Map(
flies.map((fly) => [
fly.id,
{
x: fly.position[0],
y: fly.position[1],
z: fly.position[2],
heading: fly.heading,
gait: createGaitState(),
brain: createBrain(),
wingController: createWingControllerState(),
vision: { trackedFlyId: null, lastDistance: 0 },
operatingMode: "MANUAL",
connectionState: "UNKNOWN",
batteryPercent: 100,
orderId: "",
driving: false,
flying: false,
targetAltitudeMm: FLY_GROUND_OFFSET_MM,
fallSpeedMmS: 0,
biteAnimRemainingS: 0,
},
]),
),
);
}