Files
OpenFrontIO/src/client/graphics/layers/SegmentMotionSample.ts
T
2026-02-27 23:50:57 +01:00

104 lines
2.5 KiB
TypeScript

import { TileRef } from "../../../core/game/GameMap";
import type { GameView } from "../../../core/game/GameView";
export type GridSegmentMotionPlanView = {
planId: number;
startTick: number;
ticksPerStep: number;
points: Uint32Array;
segmentSteps: Uint32Array;
segCumSteps: Uint32Array;
};
export type SampledMotionPosition = {
x: number;
y: number;
isComplete: boolean;
tile0: TileRef;
tile1: TileRef;
};
function clamp01(v: number): number {
if (v <= 0) return 0;
if (v >= 1) return 1;
return v;
}
export function sampleGridSegmentPlan(
game: GameView,
plan: GridSegmentMotionPlanView,
tickFloat: number,
): SampledMotionPosition | null {
const points = plan.points;
if (points.length === 0) {
return null;
}
if (points.length === 1 || plan.segmentSteps.length === 0) {
const t = points[0] as TileRef;
return { x: game.x(t), y: game.y(t), isComplete: true, tile0: t, tile1: t };
}
const ticksPerStep = Math.max(1, plan.ticksPerStep);
const stepFloat = (tickFloat - plan.startTick) / ticksPerStep;
const segCum = plan.segCumSteps;
const totalSteps = segCum.length === 0 ? 0 : segCum[segCum.length - 1] >>> 0;
if (totalSteps <= 0) {
const t = points[points.length - 1] as TileRef;
return { x: game.x(t), y: game.y(t), isComplete: true, tile0: t, tile1: t };
}
if (stepFloat <= 0) {
const t = points[0] as TileRef;
const t1 = points[1] as TileRef;
return {
x: game.x(t),
y: game.y(t),
isComplete: false,
tile0: t,
tile1: t1,
};
}
if (stepFloat >= totalSteps) {
const t = points[points.length - 1] as TileRef;
return { x: game.x(t), y: game.y(t), isComplete: true, tile0: t, tile1: t };
}
// Find the segment containing stepFloat.
let seg = 0;
let lo = 0;
let hi = plan.segmentSteps.length - 1;
while (lo <= hi) {
const mid = (lo + hi) >>> 1;
const start = segCum[mid] >>> 0;
const end = segCum[mid + 1] >>> 0;
if (stepFloat < start) {
hi = mid - 1;
} else if (stepFloat >= end) {
lo = mid + 1;
} else {
seg = mid;
break;
}
}
const segStart = segCum[seg] >>> 0;
const steps = Math.max(1, plan.segmentSteps[seg] >>> 0);
const u = clamp01((stepFloat - segStart) / steps);
const tile0 = points[seg] as TileRef;
const tile1 = points[seg + 1] as TileRef;
const x0 = game.x(tile0);
const y0 = game.y(tile0);
const x1 = game.x(tile1);
const y1 = game.y(tile1);
return {
x: x0 + (x1 - x0) * u,
y: y0 + (y1 - y0) * u,
isComplete: false,
tile0,
tile1,
};
}