zenno/doc/race: conserve power

This commit is contained in:
2026-07-01 23:39:31 -04:00
parent a6d264c79d
commit 1f58dec491
2 changed files with 56 additions and 13 deletions
+43
View File
@@ -642,6 +642,49 @@ export function laneChangeSpeed(powerStat: number, maxLaneOrOrder?: number, curr
*/ */
export const LANE_CHANGE_ACCEL = 0.03; export const LANE_CHANGE_ACCEL = 0.03;
const conserveStratDistCoef = {
[RunningStyle.FrontRunner]: {
[Distance.Sprint]: 1,
[Distance.Mile]: 1,
[Distance.Medium]: 1,
[Distance.Long]: 1,
},
[RunningStyle.PaceChaser]: {
[Distance.Sprint]: 0.7,
[Distance.Mile]: 0.8,
[Distance.Medium]: 0.9,
[Distance.Long]: 0.9,
},
[RunningStyle.LateSurger]: {
[Distance.Sprint]: 0.75,
[Distance.Mile]: 0.7,
[Distance.Medium]: 0.875,
[Distance.Long]: 1,
},
[RunningStyle.EndCloser]: {
[Distance.Sprint]: 0.7,
[Distance.Mile]: 0.75,
[Distance.Medium]: 0.86,
[Distance.Long]: 0.9,
},
[RunningStyle.GreatEscape]: {
[Distance.Sprint]: 1,
[Distance.Mile]: 1,
[Distance.Medium]: 1,
[Distance.Long]: 1,
},
} as const;
export function conservePowerAccel(distanceType: Distance, style: RunningStyle, rawPower: number, powerSkillBonus?: number, spotStruggled?: boolean, rushed?: boolean): number {
const power = rawPower + (powerSkillBonus ?? 0);
if (power <= 1200) {
return 0;
}
const sdc = conserveStratDistCoef[style][distanceType];
const activity = rushed ? 0.8 : (spotStruggled ? 0.98 : 1);
return Math.sqrt((power - 1200) * 130) * 0.001 * sdc * activity;
}
/** /**
* Calculate a horse's minimum speed for a race. * Calculate a horse's minimum speed for a race.
* @param raceLen Length of the race in meters * @param raceLen Length of the race in meters
+13 -13
View File
@@ -14,18 +14,9 @@
let surfaceApt = $state(race.AptitudeLevel.A); let surfaceApt = $state(race.AptitudeLevel.A);
let distanceApt = $state(race.AptitudeLevel.A); let distanceApt = $state(race.AptitudeLevel.A);
let raceLen = $state(2000); let raceLen = $state(2000);
const raceLenType = $derived.by(() => { const styleIsFront = $derived(style === race.RunningStyle.FrontRunner || style === race.RunningStyle.GreatEscape);
if (raceLen < 1500) { const distanceType = $derived(race.distance(raceLen));
return 'Sprint'; const raceLenType = $derived(race.Distance[distanceType]);
}
if (raceLen < 1900) {
return 'Mile';
}
if (raceLen < 2500) {
return 'Medium';
}
return 'Long';
});
const spurtSpeed: Array<ComputedSeries | null> = $derived([ const spurtSpeed: Array<ComputedSeries | null> = $derived([
{ label: `Guts 1200`, y: (x) => race.spurtSpeed(x, 1200, style, distanceApt, raceLen) }, { label: `Guts 1200`, y: (x) => race.spurtSpeed(x, 1200, style, distanceApt, raceLen) },
@@ -66,6 +57,11 @@
}, },
]); ]);
const laneChange: ComputedSeries[] = [{ label: 'Lane Change Target Speed', y: (x) => race.laneChangeSpeed(x) }]; const laneChange: ComputedSeries[] = [{ label: 'Lane Change Target Speed', y: (x) => race.laneChangeSpeed(x) }];
const conservePower: Array<ComputedSeries | null> = $derived([
{ label: 'Conserved Power', y: (_raw, x) => race.conservePowerAccel(distanceType, style, x, 0, false, false)},
{ label: 'Rushed', y: (_raw, x) => race.conservePowerAccel(distanceType, style, x, 0, false, true)},
styleIsFront ? { label: 'Spot Struggled', y: (_raw, x) => race.conservePowerAccel(distanceType, style, x, 0, true, false)} : null,
]);
const gutsSpurt: Array<ComputedSeries | null> = $derived([ const gutsSpurt: Array<ComputedSeries | null> = $derived([
{ label: `Speed 600`, y: (x) => race.spurtSpeed(600, x, style, distanceApt, raceLen) }, { label: `Speed 600`, y: (x) => race.spurtSpeed(600, x, style, distanceApt, raceLen) },
{ label: `Speed 1200`, y: (x) => race.spurtSpeed(1200, x, style, distanceApt, raceLen) }, { label: `Speed 1200`, y: (x) => race.spurtSpeed(1200, x, style, distanceApt, raceLen) },
@@ -85,7 +81,6 @@
]; ];
const duelSpeed: ComputedSeries[] = [{ label: 'Dueling Speed Boost', y: (x) => race.duelSpeedMod(x) }]; const duelSpeed: ComputedSeries[] = [{ label: 'Dueling Speed Boost', y: (x) => race.duelSpeedMod(x) }];
const duelAccel: ComputedSeries[] = [{ label: 'Dueling Acceleration Boost', y: (x) => race.duelAccelMod(x) }]; const duelAccel: ComputedSeries[] = [{ label: 'Dueling Acceleration Boost', y: (x) => race.duelAccelMod(x) }];
const styleIsFront = $derived(style === race.RunningStyle.FrontRunner || style === race.RunningStyle.GreatEscape);
const secSpeed: Array<ComputedAreas | null> = $derived([ const secSpeed: Array<ComputedAreas | null> = $derived([
{ {
label: 'Early Race', label: 'Early Race',
@@ -286,6 +281,11 @@
/> />
</div> </div>
<Sec h={3} id="conserve-power">Conserve Power Acceleration</Sec>
<p>Bonus acceleration at spurt start from the Conserve Power mechanic.</p>
<p>This mechanic does not appear to be well understood, in particular how long the acceleration bonus lasts and how the rushed and spot struggle multipliers apply.</p>
{@render statChart(race.Stat.Power, conservePower, 'Acceleration (m/s²)', [0, 0.35], {len: true, style: true})}
<Sec h={2} id="guts">Guts</Sec> <Sec h={2} id="guts">Guts</Sec>
<Sec h={3} id="spurt-speed-guts">Spurt Speed</Sec> <Sec h={3} id="spurt-speed-guts">Spurt Speed</Sec>