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

345 lines
9.0 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.
import skillGlobal from '../../../../global/skill.json';
import groupGlobal from '../../../../global/skill-group.json';
/**
* Skill data.
*/
export interface Skill {
/**
* Skill ID.
*/
skill_id: number;
/**
* Regional skill name.
*/
name: string;
/**
* Regional skil description.
*/
description: string;
/**
* 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;
/**
* Skill icon ID.
*/
icon_id: number;
}
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,
GreatEscape = 6,
Vision = 8,
HP = 9,
GateDelay = 10,
Frenzy = 13,
AddGateDelay = 14,
CurrentSpeed = 21,
TargetSpeed = 27,
LaneSpeed = 28,
Accel = 31,
LaneChange = 35,
}
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.GreatEscape]: () => 'Enable Great Escape',
[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.TargetSpeed]: (v) => plusBonus(v) + ' m/s Target Speed',
[AbilityType.LaneSpeed]: (v) => plusBonus(v) + ' CW/s Lane Change Speed',
[AbilityType.Accel]: (v) => plusBonus(v) + ' m/s² Acceleration',
[AbilityType.LaneChange]: (v) => 'Target Lane = ' + tenThousandths(v) + ' CW',
} 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,
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 training',
[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'),
[Target.Behind]: (v) => targetn(v!, '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 const skills = {
global: skillGlobal as Skill[],
} as const;
export const skillGroups = {
global: groupGlobal as SkillGroup[],
} as const;
export const ZERO_SKILL: Readonly<Skill> = {
skill_id: 0,
name: 'invalid skill',
description: 'an invalid skill was specified',
group: 0,
rarity: 1,
group_rate: 1,
wit_check: false,
activations: [],
icon_id: 0,
} as const;