zenno/racelib: allow accel skills to be delayed

This commit is contained in:
2026-08-26 17:12:56 -04:00
parent 444fea85f0
commit 1f31ef223d
+14 -8
View File
@@ -25,7 +25,7 @@ export interface Travel {
* @param start Motion at the beginning of the segment
* @param targetSpeed Target speed to accelerate to
* @param totalAccel Total (positive) acceleration including skill bonuses
* @param maxDur Max duration of the acceleration, or infinite if not given
* @param maxDur Max position of the acceleration, or infinite if not given
* @param maxDist Max distance of the acceleration, or infinite if not given
* @param decel Negative acceleration value for cases where target speed is below start speed
* @returns Travel segment after reaching the first of the given limits
@@ -40,13 +40,13 @@ export function travelSegment(
): Travel {
const a = start.v < targetSpeed ? totalAccel : (decel ?? -1);
const dvt = (targetSpeed - start.v) / a;
const dxt = maxDist != null ? (-start.v + Math.sqrt(start.v * start.v + 4 * a * maxDist)) / (2 * a) : dvt;
const dxt = maxDist != null ? (-start.v + Math.sqrt(start.v * start.v + 4 * a * (maxDist - start.x))) / (2 * a) : dvt;
const time = maxDur != null ? Math.min(dvt, dxt, maxDur) : Math.min(dvt, dxt);
// To address numerical imprecision, if accel time comes from velocity or distance,
// set the reached values directly.
const v = time === dvt ? targetSpeed : start.v + time * a;
const dist = maxDist != null && time === dxt ? maxDist : (start.v + 0.5 * a * time) * time;
const end = { t: start.t + time, x: start.x + dist, v };
const dist = maxDist != null && time === dxt ? maxDist : start.x + (start.v + 0.5 * a * time) * time;
const end = { t: start.t + time, x: dist, v };
return { start, end, a };
}
@@ -54,6 +54,10 @@ export function travelSegment(
* Acceleration bonus from skills.
*/
export interface Skill {
/**
* Forward position along the course at which the skill activates.
*/
x?: number;
/**
* Acceleration modifier in m/s².
*/
@@ -74,16 +78,18 @@ export interface Skill {
*/
export function up(start: Instant, targetSpeed: number, baseAccel: number, skills: Skill[]) {
const segments: Travel[] = [];
const sk = skills.map((s) => ({ ...s })).filter((s) => s.dur > 0);
const sk = skills.map((s) => ({ x: 0, ...s })).filter((s) => s.dur > 0);
const xs = sk.map((s) => s.x).filter((x) => x > 0).sort((a, b) => a - b);
while (start.v < targetSpeed) {
const active = sk.filter((s) => s.dur > 0);
if (active.length === 0) {
const active = sk.filter((s) => s.dur > 0 && s.x <= start.x);
const dx = xs.find((x) => x > start.x);
if (active.length === 0 && dx == null) {
segments.push(travelSegment(start, targetSpeed, baseAccel));
break;
}
const boost = active.reduce((a, s) => a + s.accel, 0);
const time = Math.min(...active.map((s) => s.dur));
const seg = travelSegment(start, targetSpeed, baseAccel + boost, time);
const seg = travelSegment(start, targetSpeed, baseAccel + boost, time, dx);
segments.push(seg);
start = seg.end;
for (const s of active) {