package horse import ( "bytes" "fmt" "strconv" ) type SkillID int32 type TenThousandths int32 func (x TenThousandths) String() string { b := make([]byte, 0, 12) if x < 0 { x = -x b = append(b, '-') } b = strconv.AppendInt(b, int64(x/1e4), 10) if x%1e4 != 0 { b = append(b, '.') b = fmt.Appendf(b, "%04d", x%1e4) b = bytes.TrimRight(b, "0") } return string(b) } // Skill is the internal data about a skill. type Skill struct { ID SkillID `json:"skill_id"` Name string `json:"name"` Description string `json:"description"` Group SkillGroupID `json:"group"` Rarity int8 `json:"rarity"` GroupRate int8 `json:"group_rate"` GradeValue int32 `json:"grade_value,omitzero"` WitCheck bool `json:"wit_check"` Activations []Activation `json:"activations"` UniqueOwner string `json:"unique_owner,omitzero"` SPCost int `json:"sp_cost,omitzero"` IconID int `json:"icon_id"` } // Activation is the parameters controlling when a skill activates. type Activation struct { Precondition string `json:"precondition,omitzero"` Condition string `json:"condition"` Duration TenThousandths `json:"duration,omitzero"` DurScale DurScale `json:"dur_scale"` Cooldown TenThousandths `json:"cooldown,omitzero"` Abilities []Ability `json:"abilities"` } // Ability is an individual effect applied by a skill. type Ability struct { Type AbilityType `json:"type"` ValueUsage AbilityValueUsage `json:"value_usage"` Value TenThousandths `json:"value"` Target AbilityTarget `json:"target"` TargetValue int32 `json:"target_value,omitzero"` } func (a Ability) String() string { r := make([]byte, 0, 64) r = append(r, a.Type.String()...) if a.Value != 0 { r = append(r, ' ') if a.Value > 0 { r = append(r, '+') } switch a.Type { case AbilityPassiveSpeed, AbilityPassiveStamina, AbilityPassivePower, AbilityPassiveGuts, AbilityPassiveWit: r = append(r, a.Value.String()...) case AbilityVision: r = append(r, a.Value.String()...) r = append(r, 'm') case AbilityHP: r = append(r, (a.Value * 100).String()...) r = append(r, '%') case AbilityGateDelay: r = append(r, a.Value.String()...) r = append(r, "×"...) case AbilityFrenzy: r = append(r, a.Value.String()...) r = append(r, 's') case AbilityCurrentSpeed, AbilityTargetSpeed, AbilityLaneSpeed: r = append(r, a.Value.String()...) r = append(r, " m/s"...) case AbilityAccel: r = append(r, a.Value.String()...) r = append(r, " m/s²"...) case AbilityLaneChange: r = append(r, a.Value.String()...) r = append(r, " track widths"...) } } switch a.Target { case TargetSelf: // do nothing case TargetStyle, TargetRushingStyle: // TargetValue is the style to target, not the number of targets. r = append(r, " to "...) r = append(r, a.Target.String()...) switch a.TargetValue { case 1: r = append(r, " Front Runner"...) case 2: r = append(r, " Pace Chaser"...) case 3: r = append(r, " Late Surger"...) case 4: r = append(r, " End Closer"...) } default: // For other targeting types, TargetValue is either irrelevant or limit. r = append(r, " to "...) if a.TargetValue > 1 && a.TargetValue < 18 { r = strconv.AppendInt(r, int64(a.TargetValue), 10) r = append(r, ' ') } r = append(r, a.Target.String()...) } if a.ValueUsage != ValueUsageDirect { r = append(r, ' ') r = append(r, a.ValueUsage.String()...) } return string(r) } type DurScale int8 //go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type DurScale -trimprefix Duration -linecomment const ( DurationDirect DurScale = 1 // directly DurationFrontDistance DurScale = 2 // scaling with distance from the front DurationRemainingHP DurScale = 3 // scaling with remaining HP DurationIncrementPass DurScale = 4 // increasing with each pass while active DurationMidSideBlock DurScale = 5 // scaling with mid-race phase blocked side time DurationRemainingHP2 DurScale = 7 // scaling with remaining HP ) type AbilityType int8 //go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type AbilityType -trimprefix Ability -linecomment const ( AbilityPassiveSpeed AbilityType = 1 // Speed AbilityPassiveStamina AbilityType = 2 // Stamina AbilityPassivePower AbilityType = 3 // Power AbilityPassiveGuts AbilityType = 4 // Guts AbilityPassiveWit AbilityType = 5 // Wit AbilityGreatEscape AbilityType = 6 // Enable Great Escape AbilityVision AbilityType = 8 // Vision AbilityHP AbilityType = 9 // HP AbilityGateDelay AbilityType = 10 // Gate delay multiplier AbilityFrenzy AbilityType = 13 // Frenzy AbilityAddGateDelay AbilityType = 14 // Added gate delay AbilityCurrentSpeed AbilityType = 21 // Current speed AbilityTargetSpeed AbilityType = 27 // Target speed AbilityLaneSpeed AbilityType = 28 // Lane change speed AbilityAccel AbilityType = 31 // Acceleration AbilityLaneChange AbilityType = 35 // Forced lane change ) type AbilityValueUsage int8 //go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type AbilityValueUsage -trimprefix ValueUsage -linecomment const ( ValueUsageDirect AbilityValueUsage = 1 // directly ValueUsageSkillCount AbilityValueUsage = 2 // scaling with the number of skills ValueUsageTeamSpeed AbilityValueUsage = 3 // scaling with team Speed ValueUsageTeamStamina AbilityValueUsage = 4 // scaling with team Stamina ValueUsageTeamPower AbilityValueUsage = 5 // scaling with team Power ValueUsageTeamGuts AbilityValueUsage = 6 // scaling with team Guts ValueUsageTeamWit AbilityValueUsage = 7 // scaling with team Wit ValueUsageRandom AbilityValueUsage = 8 // with a random 0× to 0.04× multiplier ValueUsageRandom2 AbilityValueUsage = 9 // with a random 0× to 0.04× multiplier ValueUsageClimax AbilityValueUsage = 10 // scaling with the number of races won in training ValueUsageMaxStat AbilityValueUsage = 13 // scaling with the highest raw stat ValueUsageGreenCount AbilityValueUsage = 14 // scaling with the number of Passive skills activated ValueUsageDistAdd AbilityValueUsage = 19 // plus extra when far from the lead ValueUsageMidSideBlock AbilityValueUsage = 20 // scaling with mid-race phase blocked side time ValueUsageSpeed AbilityValueUsage = 22 // scaling with overall speed ValueUsageSpeed2 AbilityValueUsage = 23 // scaling with overall speed ValueUsageArcPotential AbilityValueUsage = 24 // scaling with L'Arc global potential ValueUsageMaxLead AbilityValueUsage = 25 // scaling with the longest lead obtained in the first ⅔ ) type AbilityTarget int8 //go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type AbilityTarget -trimprefix Target -linecomment const ( TargetSelf AbilityTarget = 1 // self TargetSympathizers AbilityTarget = 2 // others with Sympathy TargetInView AbilityTarget = 4 // others in view TargetFrontmost AbilityTarget = 7 // frontmost TargetAhead AbilityTarget = 9 // others ahead TargetBehind AbilityTarget = 10 // others behind TargetAllTeammates AbilityTarget = 11 // all teammates TargetStyle AbilityTarget = 18 // using style TargetRushingAhead AbilityTarget = 19 // rushing others ahead TargetRushingBehind AbilityTarget = 20 // rushing others behind TargetRushingStyle AbilityTarget = 21 // rushing using style TargetCharacter AbilityTarget = 22 // specific character TargetTriggering AbilityTarget = 23 // whosoever triggered this skill ) type SkillGroupID int32 // SkillGroup is a group of skills which are alternate versions of each other. // // Any of the skill IDs in a group may be zero, indicating that there is no // skill with the corresponding group rate. // Some skill groups contain only Skill2 or SkillBad, while others may have all // four skills. // // As a special case, horsegen lists both unique skills and their inherited // versions in the skill groups for both. type SkillGroup struct { ID SkillGroupID `json:"skill_group"` // Skill1 is the base version of the skill, either a common (white) skill // or an Uma's own unique. Skill1 SkillID `json:"skill1,omitzero"` // Skill2 is the first upgraded version of the skill: a rare (gold) // skill, a double circle skill, or an inherited unique skill. Skill2 SkillID `json:"skill2,omitzero"` // Skill3 is the highest upgraded version, a gold version of a skill with // a double circle version. Skill3 SkillID `json:"skill3,omitzero"` // SkillBad is a negative (purple) skill. SkillBad SkillID `json:"skill_bad,omitzero"` }