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 start Motion at the beginning of the segment
* @param targetSpeed Target speed to accelerate to * @param targetSpeed Target speed to accelerate to
* @param totalAccel Total (positive) acceleration including skill bonuses * @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 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 * @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 * @returns Travel segment after reaching the first of the given limits
@@ -40,13 +40,13 @@ export function travelSegment(
): Travel { ): Travel {
const a = start.v < targetSpeed ? totalAccel : (decel ?? -1); const a = start.v < targetSpeed ? totalAccel : (decel ?? -1);
const dvt = (targetSpeed - start.v) / a; 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); 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, // To address numerical imprecision, if accel time comes from velocity or distance,
// set the reached values directly. // set the reached values directly.
const v = time === dvt ? targetSpeed : start.v + time * a; const v = time === dvt ? targetSpeed : start.v + time * a;
const dist = maxDist != null && time === dxt ? maxDist : (start.v + 0.5 * a * time) * time; const dist = maxDist != null && time === dxt ? maxDist : start.x + (start.v + 0.5 * a * time) * time;
const end = { t: start.t + time, x: start.x + dist, v }; const end = { t: start.t + time, x: dist, v };
return { start, end, a }; return { start, end, a };
} }
@@ -54,6 +54,10 @@ export function travelSegment(
* Acceleration bonus from skills. * Acceleration bonus from skills.
*/ */
export interface Skill { export interface Skill {
/**
* Forward position along the course at which the skill activates.
*/
x?: number;
/** /**
* Acceleration modifier in m/s². * Acceleration modifier in m/s².
*/ */
@@ -74,16 +78,18 @@ export interface Skill {
*/ */
export function up(start: Instant, targetSpeed: number, baseAccel: number, skills: Skill[]) { export function up(start: Instant, targetSpeed: number, baseAccel: number, skills: Skill[]) {
const segments: Travel[] = []; 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) { while (start.v < targetSpeed) {
const active = sk.filter((s) => s.dur > 0); const active = sk.filter((s) => s.dur > 0 && s.x <= start.x);
if (active.length === 0) { const dx = xs.find((x) => x > start.x);
if (active.length === 0 && dx == null) {
segments.push(travelSegment(start, targetSpeed, baseAccel)); segments.push(travelSegment(start, targetSpeed, baseAccel));
break; break;
} }
const boost = active.reduce((a, s) => a + s.accel, 0); const boost = active.reduce((a, s) => a + s.accel, 0);
const time = Math.min(...active.map((s) => s.dur)); 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); segments.push(seg);
start = seg.end; start = seg.end;
for (const s of active) { for (const s of active) {