105 lines
3.6 KiB
JavaScript
105 lines
3.6 KiB
JavaScript
import {measureDistances} from "./compatibility.js";
|
|
import {recalculate, updateCombatantDragRulerFlags} from "./socket.js";
|
|
import {getTokenShape, isClose, zip} from "./util.js";
|
|
|
|
function initTrackingFlag(combatant) {
|
|
const initialFlag = {passedWaypoints: [], trackedRound: 0};
|
|
let dragRulerFlag = combatant.data.flags.dragRuler;
|
|
if (dragRulerFlag) {
|
|
if (isNaN(dragRulerFlag.trackedRound)) {
|
|
mergeObject(dragRulerFlag, initialFlag);
|
|
}
|
|
} else {
|
|
combatant.data.flags.dragRuler = initialFlag;
|
|
}
|
|
}
|
|
|
|
function getInitializedCombatant(token, combat) {
|
|
const combatant = combat.getCombatantByToken(token.id);
|
|
if (!combatant) return undefined;
|
|
initTrackingFlag(combatant);
|
|
return combatant;
|
|
}
|
|
|
|
export async function trackRays(tokens, tokenRays) {
|
|
const combat = game.combat;
|
|
if (!combat) return;
|
|
if (!combat.started) return;
|
|
if (!(tokens instanceof Array)) {
|
|
tokens = [tokens];
|
|
tokenRays = [tokenRays];
|
|
}
|
|
const updates = Array.from(zip(tokens, tokenRays))
|
|
.map(([token, rays]) => calculateUpdate(combat, token, rays))
|
|
.filter(Boolean);
|
|
await updateCombatantDragRulerFlags(combat, updates);
|
|
}
|
|
|
|
function calculateUpdate(combat, token, rays) {
|
|
const combatant = getInitializedCombatant(token, combat);
|
|
if (!combatant) return;
|
|
|
|
// Check if we have entered a new round. If so, remove the currently stored path
|
|
if (combat.data.round > combatant.data.flags.dragRuler.trackedRound) {
|
|
combatant.data.flags.dragRuler.passedWaypoints = [];
|
|
combatant.data.flags.dragRuler.trackedRound = combat.data.round;
|
|
}
|
|
|
|
// Add the passed waypoints to the combatant
|
|
const terrainRulerAvailable = game.modules.get("terrain-ruler")?.active;
|
|
const dragRulerFlags = combatant.data.flags.dragRuler;
|
|
const waypoints = dragRulerFlags.passedWaypoints;
|
|
for (const ray of rays) {
|
|
// Ignore rays that have the same start and end coordinates
|
|
if (ray.A.x !== ray.B.x || ray.A.y !== ray.B.y) {
|
|
if (terrainRulerAvailable) {
|
|
measureDistances([{ray}], token, getTokenShape(token), {
|
|
terrainRulerInitialState: waypoints[waypoints.length - 1]?.dragRulerFinalState,
|
|
enableTerrainRuler: terrainRulerAvailable,
|
|
});
|
|
ray.A.dragRulerVisitedSpaces = ray.terrainRulerVisitedSpaces;
|
|
ray.A.dragRulerFinalState = ray.terrainRulerFinalState;
|
|
}
|
|
waypoints.push(ray.A);
|
|
}
|
|
}
|
|
return {_id: combatant.id, dragRulerFlags};
|
|
}
|
|
|
|
export function getMovementHistory(token) {
|
|
const combat = game.combat;
|
|
if (!combat) return [];
|
|
const combatant = combat.getCombatantByToken(token.id);
|
|
if (!combatant) return [];
|
|
const dragRulerFlags = combatant.flags.dragRuler;
|
|
if (!dragRulerFlags) return [];
|
|
if (combat.round > dragRulerFlags.trackedRound) return [];
|
|
return dragRulerFlags.passedWaypoints ?? [];
|
|
}
|
|
|
|
export async function removeLastHistoryEntryIfAt(token, x, y) {
|
|
const history = getMovementHistory(token);
|
|
if (history.length === 0) return;
|
|
const entry = history[history.length - 1];
|
|
if (!isClose(x + token.w / 2, entry.x, 0.1) || !isClose(y + token.h / 2, entry.y, 0.1)) {
|
|
return;
|
|
}
|
|
history.pop();
|
|
const combat = game.combat;
|
|
const combatant = combat.getCombatantByToken(token.id);
|
|
await updateCombatantDragRulerFlags(combat, [
|
|
{_id: combatant.id, dragRulerFlags: combatant.data.flags.dragRuler},
|
|
]);
|
|
}
|
|
|
|
export async function resetMovementHistory(combat, combatantId) {
|
|
const combatant = combat.combatants.get(combatantId);
|
|
const dragRulerFlags = combatant.data.flags.dragRuler;
|
|
if (!dragRulerFlags) return;
|
|
dragRulerFlags.passedWaypoints = null;
|
|
dragRulerFlags.trackedRound = null;
|
|
dragRulerFlags.rulerState = null;
|
|
await updateCombatantDragRulerFlags(combat, [{_id: combatantId, dragRulerFlags}]);
|
|
recalculate();
|
|
}
|