zenno: update stat charts for new limits

This commit is contained in:
2026-07-01 22:12:10 -04:00
parent 9ce665865e
commit d62e729052
6 changed files with 38 additions and 36 deletions
+9 -11
View File
@@ -55,39 +55,37 @@
let width = $state(0);
let height = $state(0);
let expand = $state(false);
const xLabel = $derived(Stat[stat]);
const xLabel = $derived(`Raw ${Stat[stat]}`);
const xLines = $derived([xRule].flat(1));
const xMin = $derived(xRange?.[0] ?? 200);
const xMax = $derived.by(() => {
if (xRange?.[1] != null) {
return xRange[1];
}
if (expand) {
return 2000;
}
return 100 * Math.ceil(Math.max(1200, ...xLines) / 100);
return 100 * Math.ceil(Math.max(2000, ...xLines) / 100);
});
const xVal = $derived(d3.range(xMin, xMax + 1, 5)); // +1 because d3.range does not include the max
const thrX = 1200;
function adj(x: number): number {
return Math.min(x, 1200) + 0.5 * (Math.max(x, 1200) - 1200);
}
const series = $derived([y].flat(1).filter((s) => s != null));
const areas = $derived([yArea].flat(1).filter((s) => s != null));
const vals = $derived(series.flatMap(({ y, label }) => xVal.map((x) => ({ x, y: y(x), label }))));
const areaVals = $derived(areas.flatMap(({ y1, y2, label }) => xVal.map((x) => ({ x, y1: y1(x), y2: y2(x), label }))));
const vals = $derived(series.flatMap(({ y, label }) => xVal.map((x) => ({ x, y: y(adj(x), x), label }))));
const areaVals = $derived(areas.flatMap(({ y1, y2, label }) => xVal.map((x) => ({ x, y1: y1(adj(x), x), y2: y2(adj(x), x), label }))));
const yRange: [number, number] = $derived.by(() => {
if (range != null) {
if (range.length === 2) {
return range;
}
return [range[0], expand ? range[2] : range[1]];
return [range[0], range[2]];
}
const l = d3.min(vals, ({ y }) => y) ?? 0;
const r = d3.max(vals, ({ y }) => y) ?? 1;
return [l, r];
});
const yLines = $derived(xLines.flatMap((x) => series.map(({ y, label }) => ({ y: y(x), label }))));
const yLines = $derived(xLines.flatMap((x) => series.map(({ y, label }) => ({ y: y(adj(x), x), label }))));
const makeChart: Attachment = (el) => {
$effect(() => {
+3 -3
View File
@@ -1,11 +1,11 @@
export interface ComputedSeries {
y: (x: number) => number;
y: (base: number, raw: number) => number;
label: string;
}
export interface ComputedAreas {
y1: (x: number) => number;
y2: (x: number) => number;
y1: (base: number, raw: number) => number;
y2: (base: number, raw: number) => number;
label: string;
}
+2 -1
View File
@@ -519,7 +519,8 @@ export function speedGain(speedBonus: number, dur: number, accel: number | null,
* @returns Probability per candidate to accept
*/
export function reducedSpurtChance(witStat: number): number {
return 0.15 + 0.0005 * witStat;
// 1900 wit style S exceeds 100% chance if we don't explicitly cap.
return Math.min(0.15 + 0.0005 * witStat, 1);
}
/**
+5 -5
View File
@@ -414,8 +414,8 @@
never happen, its duration also scales with the guts stat. Unlike skills, its duration <i>does not</i> scale with race distance.
</p>
<div class="mb-4 grid h-60 w-full grid-cols-2 md:h-96">
<StatChart stat={Stat.Guts} y={ssBoostSeries} yLabel="Speed Bonus (m/s)" range={[0, 0.3]} />
<StatChart stat={Stat.Guts} y={ssDurSeries} yLabel="Duration (s)" range={[0, 12]} />
<StatChart stat={Stat.Guts} y={ssBoostSeries} yLabel="Speed Bonus (m/s)" range={[0, 0.4]} />
<StatChart stat={Stat.Guts} y={ssDurSeries} yLabel="Duration (s)" range={[0, 14]} />
</div>
<p>
Spot struggle also greatly increases HP consumption. For normal front runners, the rate is slightly less than Rushed. For
@@ -443,7 +443,7 @@
y={laneComboSeries}
yLabel="Speed Boost"
yRule={lcYRule}
range={[0.2, 0.5]}
range={[0.2, 0.6]}
/>
</div>
<p>
@@ -530,7 +530,7 @@
stat={Stat.Wit}
y={downhillSeries}
yLabel="Entry Chance (% each second)"
range={[0, 60]}
range={[0, 75]}
/>
</div>
<p>
@@ -550,7 +550,7 @@
stat={Stat.Wit}
yArea={secSpeedSeries}
yLabel="Section Speed (m/s)"
range={[17.5, 22.5]}
range={[17, 23]}
plotOptions={{ color: { legend: true } }}
/>
<div class="mx-auto flex w-full md:max-w-2xl">
+17 -14
View File
@@ -67,10 +67,13 @@
]);
const laneChange: ComputedSeries[] = [{ label: 'Lane Change Target Speed', y: (x) => race.laneChangeSpeed(x) }];
const gutsSpurt: Array<ComputedSeries | null> = $derived([
{ label: `Speed 1200`, y: (x) => race.spurtSpeed(1200, 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) },
// NOTE(zeph): The label is 1600 despite the input being 1400
// for raw vs final difference.
{ label: `Speed 1600`, y: (x) => race.spurtSpeed(1400, x, style, distanceApt, raceLen) },
distanceApt !== race.AptitudeLevel.A
? { label: `${raceLenType} A, Speed 1200`, y: (x) => race.spurtSpeed(1200, x, style, race.AptitudeLevel.A, raceLen) }
? { label: `${raceLenType} A, Speed 1600`, y: (x) => race.spurtSpeed(1400, x, style, race.AptitudeLevel.A, raceLen) }
: null,
]);
const minSpeed: ComputedSeries[] = $derived([{ label: 'Minimum Speed', y: (x) => race.minSpeed(raceLen, x) }]);
@@ -241,7 +244,7 @@
<Sec h={3} id="spurt-speed">Spurt Speed</Sec>
<p>Target speed during the Uma's last spurt. See also <a href="#spurt-speed-guts">the effect of Guts</a>.</p>
{@render statChart(race.Stat.Speed, spurtSpeed, 'Spurt Speed (m/s)', [20, 26], { len: true, style: true, dist: true })}
{@render statChart(race.Stat.Speed, spurtSpeed, 'Spurt Speed (m/s)', [20, 26.5], { len: true, style: true, dist: true })}
<Sec h={3} id="late-race-speed">Late Race Speed</Sec>
<p>Base target speed during late race when the Uma is not spurting (but not dead yet).</p>
@@ -261,7 +264,7 @@
href={resolve('/doc/frbm#lane-combo')}>lane combo</a
>.
</p>
{@render statChart(race.Stat.Power, moveLane, 'Target Speed Modifier (m/s)', [0.2, 0.5])}
{@render statChart(race.Stat.Power, moveLane, 'Target Speed Modifier (m/s)', [0.2, 0.6])}
<Sec h={3} id="uphill">Uphill Target Speed Loss</Sec>
<p>Target speed modifier while running uphill.</p>
@@ -269,7 +272,7 @@
<Sec h={3} id="acceleration">Acceleration</Sec>
<p>Acceleration.</p>
{@render statChart(race.Stat.Power, accel, 'Acceleration (m/s²)', [0.1, 0.5], { style: true })}
{@render statChart(race.Stat.Power, accel, 'Acceleration (m/s²)', [0.1, 0.55], { style: true })}
<Sec h={3} id="lane-change-target-speed">Lane Change Target Speed</Sec>
<p>Horizontal (rather than forward) target speed of changing lanes.</p>
@@ -279,7 +282,7 @@
stat={race.Stat.Power}
y={laneChange}
yLabel="Lane Change Target Speed (CW/s)"
range={[0.01, 0.03]}
range={[0.01, 0.04]}
/>
</div>
@@ -287,11 +290,11 @@
<Sec h={3} id="spurt-speed-guts">Spurt Speed</Sec>
<p>Target speed during the Uma's last spurt. See also <a href="#spurt-speed">the effect of Speed</a>.</p>
{@render statChart(race.Stat.Guts, gutsSpurt, 'Spurt Speed (m/s)', [20, 26], { len: true, style: true, dist: true })}
{@render statChart(race.Stat.Guts, gutsSpurt, 'Spurt Speed (m/s)', [20, 26.5], { len: true, style: true, dist: true })}
<Sec h={3} id="min-speed">Minimum Speed</Sec>
<p>Forced minimum speed, as well as target speed when out of HP.</p>
{@render statChart(race.Stat.Guts, minSpeed, 'Target Speed (m/s)', [15.8, 18.4], { len: true })}
{@render statChart(race.Stat.Guts, minSpeed, 'Target Speed (m/s)', [15.8, 18.5], { len: true })}
<Sec h={3} id="hp-rate-mod">Late Race HP Consumption Rate Modifier</Sec>
<p>Multiplier on HP consumption rate during late race and last spurt phase.</p>
@@ -300,15 +303,15 @@
<Sec h={3} id="spot-struggle">Spot Struggle</Sec>
<p>Speed boost and duration of spot struggle.</p>
<div class="mb-4 grid h-96 w-full md:grid-cols-2">
<StatChart stat={race.Stat.Guts} y={ssBoost} yLabel="Speed Bonus (m/s)" range={[0, 0.3]} />
<StatChart stat={race.Stat.Guts} y={ssDur} yLabel="Duration (s)" range={[0, 12]} />
<StatChart stat={race.Stat.Guts} y={ssBoost} yLabel="Speed Bonus (m/s)" range={[0, 0.4]} />
<StatChart stat={race.Stat.Guts} y={ssDur} yLabel="Duration (s)" range={[0, 14]} />
</div>
<Sec h={3} id="dueling">Dueling</Sec>
<p>Speed boost and acceleration boost of dueling.</p>
<div class="mb-4 grid h-96 w-full md:grid-cols-2">
<StatChart stat={race.Stat.Guts} y={duelSpeed} yLabel="Speed Bonus (m/s)" range={[0, 0.65]} />
<StatChart stat={race.Stat.Guts} y={duelAccel} yLabel="Acceleration Bonus (m/s²)" range={[0, 0.14]} />
<StatChart stat={race.Stat.Guts} y={duelSpeed} yLabel="Speed Bonus (m/s)" range={[0, 0.8]} />
<StatChart stat={race.Stat.Guts} y={duelAccel} yLabel="Acceleration Bonus (m/s²)" range={[0, 0.16]} />
</div>
<Sec h={2} id="wit">Wit</Sec>
@@ -321,7 +324,7 @@
stat={race.Stat.Wit}
yArea={secSpeed}
yLabel="Section Speed (m/s)"
range={[17.5, 22.5]}
range={[17, 23]}
plotOptions={{ color: { legend: true } }}
/>
<div class="mx-auto flex w-full md:max-w-2xl">
@@ -347,7 +350,7 @@
<Sec h={3} id="downhill">Downhill Accel Mode Chance</Sec>
<p>Chance each second to enter downhill accel mode when eligible.</p>
{@render statChart(race.Stat.Wit, downhill, 'Entry Chance (% each second)', [0, 60])}
{@render statChart(race.Stat.Wit, downhill, 'Entry Chance (% each second)', [0, 75])}
<Sec h={3} id="spurt-accept">Reduced Spurt Accept Chance</Sec>
<p>Chance to accept each checked spurt delay and speed when not full spurting.</p>
+2 -2
View File
@@ -169,7 +169,7 @@
stat={Stat.Guts}
y={ssY}
yLabel="Lengths Gained"
range={[0, 1.5, 2.5]}
range={[0, 2]}
xRule={gutsStat}
yRule={ssYRule}
/>
@@ -178,7 +178,7 @@
stat={Stat.Power}
y={lcY}
yLabel="Lengths Gained"
range={[0, 1.5, 2.5]}
range={[0, 1.1]}
xRule={powerStat}
yRule={lcYRule}
/>