From 1f31ef223d5c5410810da1db31f83d437c223701 Mon Sep 17 00:00:00 2001 From: Branden J Brown Date: Wed, 26 Aug 2026 17:12:56 -0400 Subject: [PATCH] zenno/racelib: allow accel skills to be delayed --- zenno/src/lib/racelib/accel.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/zenno/src/lib/racelib/accel.ts b/zenno/src/lib/racelib/accel.ts index f2ca89c..58ca45c 100644 --- a/zenno/src/lib/racelib/accel.ts +++ b/zenno/src/lib/racelib/accel.ts @@ -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) {