Files
horse/zenno/src/lib/data/skill.ts
T

414 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Skill data.
*/
export interface Skill {
/**
* Skill ID.
*/
skill_id: number;
/**
* Regional skill name.
*/
name: string;
/**
* Regional skil description.
*/
description: string;
/**
* Other skills in the skill group.
*/
group_skills: GroupSkill[];
/**
* Skill group ID.
*/
group: number;
/**
* Skill rarity. 3-5 are uniques for various star levels.
*/
rarity: 1 | 2 | 3 | 4 | 5;
/**
* Upgrade position within the skill's group.
* -1 is for negative (purple) skills.
*/
group_rate: 1 | 2 | 3 | -1;
/**
* Grade value, or the amount of rating gained for having the skill with
* appropriate aptitude.
*/
grade_value?: number;
/**
* Whether the skill requires a wit check.
*/
wit_check: boolean;
/**
* Conditions and results of skill activation.
*/
activations: Activation[];
/**
* Name of the Uma which owns this skill as a unique, if applicable.
*/
unique_owner?: string;
/**
* SP cost to purchase the skill, if applicable.
*/
sp_cost?: number;
/**
* Numeric tags applied to the skill, for effects which select skill types.
*/
tags: number[];
/**
* Skill icon ID.
*/
icon_id: number;
}
/**
* Skill reference in a skill group.
*/
export interface GroupSkill {
/** Skill ID. */
skill_id: number;
/** Group rate, as in `Skill`. */
group_rate: Skill['group_rate'];
/** Rarity, as in `Skill`. */
rarity: Skill['rarity'];
}
export enum DurationScale {
Direct = 1,
FrontDistance = 2,
RemainingHP = 3,
IncrementPass = 4,
MidSideBlock = 5,
RemainingHP2 = 7,
}
export const DURATION_SCALE_NAME: Readonly<Record<DurationScale, string>> = {
[DurationScale.Direct]: '',
[DurationScale.FrontDistance]: 'scaling with distance from the front',
[DurationScale.RemainingHP]: 'scaling with remaining HP',
[DurationScale.IncrementPass]: 'increasing with each pass while active',
[DurationScale.MidSideBlock]: 'scaling with mid-race phase blocked side time',
[DurationScale.RemainingHP2]: 'scaling with remaining HP',
} as const;
/**
* Conditions and results of skill activation.
*/
export interface Activation {
/**
* Precondition which must be satisfied before the condition is checked.
*/
precondition?: string;
/**
* Activation conditions.
*/
condition: string;
/**
* Skill duration in ten thousandths of a second.
* Generally undefined for activations which only affect HP.
*/
duration?: number;
/**
* Special skill duration scaling mode.
*/
dur_scale: DurationScale;
/**
* Skill cooldown in ten thousandths of a second.
* A value of 5000000 indicates that the cooldown is forever.
* Generally undefined for passive skills.
*/
cooldown?: number;
/**
* Results applied when the skill's conditions are met.
*/
abilities: Ability[];
}
export function tenThousandths(v: number): string {
const q = v / 10000;
if (v % 10000 === 0) {
return q.toString();
}
const s = q.toFixed(4);
const j = /\.?0+$/.exec(s);
return s.substring(0, j?.index ?? undefined);
}
function plusBonus(v: number): string {
const s = tenThousandths(v);
return v < 0 ? s : '+' + s;
}
export enum AbilityType {
Speed = 1,
Stamina = 2,
Power = 3,
Guts = 4,
Wit = 5,
Runaway = 6,
Vision = 8,
HP = 9,
GateDelay = 10,
Frenzy = 13,
AddGateDelay = 14,
CurrentSpeed = 21,
CurrentSpeedWithDecel = 22,
TargetSpeed = 27,
LaneSpeed = 28,
RushedChance = 29,
Accel = 31,
AllStatus = 32,
LaneChange = 35,
ActivateRare = 37,
}
export const ABILITY_TYPE_FORMAT: Readonly<Record<AbilityType, (v: number) => string>> = {
[AbilityType.Speed]: (v) => 'Speed ' + plusBonus(v),
[AbilityType.Stamina]: (v) => 'Stamina ' + plusBonus(v),
[AbilityType.Power]: (v) => 'Power ' + plusBonus(v),
[AbilityType.Guts]: (v) => 'Guts ' + plusBonus(v),
[AbilityType.Wit]: (v) => 'Wit ' + plusBonus(v),
[AbilityType.Runaway]: () => 'Enable Runaway',
[AbilityType.Vision]: (v) => plusBonus(v) + 'm Vision',
[AbilityType.HP]: (v) => (v > 0 ? tenThousandths(v * 100) + '% Recovery' : tenThousandths(v * 100) + '% HP Drain'),
[AbilityType.GateDelay]: (v) => tenThousandths(v) + '× Gate Delay',
[AbilityType.Frenzy]: (v) => tenThousandths(v) + 's Frenzy',
[AbilityType.AddGateDelay]: (v) => plusBonus(v) + 's Gate Delay',
[AbilityType.CurrentSpeed]: (v) => plusBonus(v) + ' m/s Current Speed',
[AbilityType.CurrentSpeedWithDecel]: (v) => plusBonus(v) + ' m/s Current Speed',
[AbilityType.TargetSpeed]: (v) => plusBonus(v) + ' m/s Target Speed',
[AbilityType.LaneSpeed]: (v) => plusBonus(v) + ' CW/s Lane Change Speed',
[AbilityType.RushedChance]: (v) => plusBonus(v) + '% Rushed Chance',
[AbilityType.Accel]: (v) => plusBonus(v) + ' m/s² Acceleration',
[AbilityType.AllStatus]: (v) => 'All stats ' + plusBonus(v),
[AbilityType.LaneChange]: (v) => 'Target Lane = ' + tenThousandths(v) + ' CW',
[AbilityType.ActivateRare]: (v) => 'Activate Rare Skills ×' + tenThousandths(v),
} as const;
export enum AbilityValueUsage {
Direct = 1,
SkillCount = 2,
TeamSpeed = 3,
TeamStamina = 4,
TeamPower = 5,
TeamGuts = 6,
TeamWit = 7,
Random1 = 8,
Random2 = 9,
Climax = 10,
Fans = 12,
MaxStat = 13,
GreenCount = 14,
DistAdd = 19,
MidRaceSideBlock = 20,
Speed1 = 22,
Speed2 = 23,
ArcPotential = 24,
MaxLead = 25,
}
export const ABILITY_SCALE_NAME: Readonly<Record<AbilityValueUsage, string>> = {
[AbilityValueUsage.Direct]: '',
[AbilityValueUsage.SkillCount]: 'scaling with the number of skills',
[AbilityValueUsage.TeamSpeed]: 'scaling with team Speed',
[AbilityValueUsage.TeamStamina]: 'scaling with team Stamina',
[AbilityValueUsage.TeamPower]: 'scaling with team Power',
[AbilityValueUsage.TeamGuts]: 'scaling with team Guts',
[AbilityValueUsage.TeamWit]: 'scaling with team Wit',
[AbilityValueUsage.Random1]: 'with a random 0× to 0.04× multiplier',
[AbilityValueUsage.Random2]: 'with a random 0× to 0.04× mulitplier',
[AbilityValueUsage.Climax]: 'scaling with the number of races won in the career',
[AbilityValueUsage.Fans]: 'scaling with fans earned in the career',
[AbilityValueUsage.MaxStat]: 'scaling with the highest raw stat',
[AbilityValueUsage.GreenCount]: 'scaling with the number of Passive skills activated',
[AbilityValueUsage.DistAdd]: 'plus extra when far from the lead',
[AbilityValueUsage.MidRaceSideBlock]: 'scaling with mid-race phase blocked side time',
[AbilityValueUsage.Speed1]: 'scaling with overall speed',
[AbilityValueUsage.Speed2]: 'scaling with overall speed',
[AbilityValueUsage.ArcPotential]: "scaling with L'Arc global potential",
[AbilityValueUsage.MaxLead]: 'scaling with the longest lead obtained in the first ⅔',
};
export enum Target {
Self = 1,
Sympathizers = 2,
InView = 4,
Frontmost = 7,
Ahead = 9,
Behind = 10,
AllTeammates = 11,
Style = 18,
RushingAhead = 19,
RushingBehind = 20,
RushingStyle = 21,
Character = 22,
Triggering = 23,
}
function targetn(v: number, n: string, one?: string): string {
switch (v) {
case 1:
return one != null ? one : `to ${n}`;
case 18:
return `to all ${n}`;
default:
return `to ${v} others ${n}`;
}
}
function stylename(v: number, infix: string): string {
switch (v) {
case 1:
return `to ${infix} Front Runners`;
case 2:
return `to ${infix} Pace Chasers`;
case 3:
return `to ${infix} Late Surgers`;
case 4:
return `to ${infix} End Closers`;
default:
return `to all running unknown style ${v}`;
}
}
export const TARGET_FORMAT: Readonly<Record<Target, (v?: number) => string>> = {
[Target.Self]: () => '',
[Target.Sympathizers]: () => 'to others with Sympathy',
[Target.InView]: (v) => targetn(v!, 'in view'),
[Target.Frontmost]: (v) => targetn(v!, 'at the front', 'the frontmost'),
[Target.Ahead]: (v) => targetn(v!, 'ahead', 'to the runner ahead'),
[Target.Behind]: (v) => targetn(v!, 'behind', 'to the runner behind'),
[Target.AllTeammates]: () => 'to all teammates',
[Target.Style]: (v) => stylename(v!, 'all'),
[Target.RushingAhead]: (v) => targetn(v!, 'rushing ahead'),
[Target.RushingBehind]: (v) => targetn(v!, 'rushing behind'),
[Target.RushingStyle]: (v) => stylename(v!, 'all rushing'),
[Target.Character]: (v) => `to character ${v}`,
[Target.Triggering]: () => 'to whosoever triggered this skill',
} as const;
/**
* Effects applied when a skill activates.
*/
export interface Ability {
/**
* Race mechanic affected by the ability.
*/
type: AbilityType;
/**
* Special scaling type of the skill value.
*/
value_usage: AbilityValueUsage;
/**
* Amount that the skill modifies the race mechanic in ten thousandths of
* whatever is the appropriate unit.
*/
value: number;
/**
* Selector for horses targeted by the ability.
*/
target: Target;
/**
* Argument value for the ability target, when appropriate.
*/
target_value?: number;
}
/**
* Skill groups.
* Skills in a skill group replace each other when purchased.
*
* As a special case, horsegen lists both unique skills and their inherited
* versions in the skill groups for both.
*/
export interface SkillGroup {
/**
* Skill group ID.
*/
skill_group: number;
/**
* Base skill in the skill group, if any.
* Either a common (white) skill or an Uma's own unique.
*
* Some skill groups, e.g. for G1 Averseness, have no base skill.
*/
skill1?: number;
/**
* First upgraded version of a skill, if any.
* A rare (gold) skill, double circle skill, or an inherited unique skill.
*/
skill2?: number;
/**
* Highest upgraded version of a skill, if any.
* Gold version of a skill with a double circle version.
*/
skill3?: number;
/**
* Negative (purple) version of a skill, if any.
*/
skill_bad?: number;
}
export async function skills(): Promise<Skill[]> {
const resp = await fetch('/api/global/skill');
return resp.json();
}
export async function skillGroups(): Promise<SkillGroup[]> {
const resp = await fetch('/api/global/skill-group');
return resp.json();
}
export const TAGS = [
{ name: 'Front Runner', tag: 101 },
{ name: 'Pace Chaser', tag: 102 },
{ name: 'Late Surger', tag: 103 },
{ name: 'End Closer', tag: 104 },
{ name: 'Sprint', tag: 201 },
{ name: 'Mile', tag: 202 },
{ name: 'Medium', tag: 203 },
{ name: 'Long', tag: 204 },
{ name: 'Early Race', tag: 301 },
{ name: 'Mid Race', tag: 302 },
{ name: 'Late Race', tag: 303 },
{ name: 'Speed Stat or Velocity', tag: 401 },
{ name: 'Stamina Stat or HP', tag: 402 },
{ name: 'Power Stat, Acceleration, or Lane Movement', tag: 403 },
{ name: 'Guts Stat', tag: 404 },
{ name: 'Wit Stat or Vision', tag: 405 },
{ name: 'Debuff', tag: 406 },
{ name: 'Gate Delay', tag: 407 },
{ name: 'Turf', tag: 501 },
{ name: 'Dirt', tag: 502 },
{ name: 'Ground Condition Passive', tag: 601 },
{ name: 'Weather Passive', tag: 602 },
{ name: 'Season Passive', tag: 603 },
{ name: 'Racecourse Passive', tag: 604 },
{ name: 'Time of Day Passive', tag: 605 },
{ name: 'Exchange Race Passive', tag: 606 },
{ name: 'Distance Passive', tag: 607 },
{ name: 'Corner-Based Passive', tag: 608 },
{ name: 'Gate Position Passive', tag: 609 },
{ name: 'Ground Type Passive', tag: 610 },
{ name: 'Popularity Passive', tag: 611 },
{ name: 'Running Style Passive', tag: 612 },
{ name: "Passive Depending on Other Horses' Styles or Skills", tag: 613 },
{ name: 'Base Stat Threshold Passive', tag: 614 },
{ name: 'Mood Passive', tag: 615 },
{ name: 'URA', tag: 801 },
{ name: 'Unity Cup', tag: 802 },
{ name: 'Trackblazer', tag: 804 },
{ name: 'Our Grand Concert', tag: 803 },
// { name: 'Grand Masters', tag: ? },
// { name: "Project L'Arc", tag: ? },
// { name: 'U.A.F.', tag: ? },
// { name: 'Great Food Festival', tag: ? },
// { name: 'Run, Mecha Umamusume!', tag: ? },
// { name: 'The Twinkle Legends', tag: ? },
// { name: 'Design Your Island', tag: ? },
// { name: 'Yukoma Hot Springs', tag: ? },
// { name: 'Beyond Dreams', tag: ? },
// { name: 'Tracen-Ken', tag: ? },
] as const;