horsegen: generate skills

This commit is contained in:
2026-01-10 02:30:38 -05:00
parent 5bcdd45b10
commit 05688a08e2
9 changed files with 3382 additions and 529 deletions

View File

@@ -3,4 +3,4 @@
Models, data, algorithms, and tools for Umamusume: Pretty Derby. Models, data, algorithms, and tools for Umamusume: Pretty Derby.
Data is generated from the game's local database. Data is generated from the game's local database.
Algorithms come from either Erzzy and Kireina's reference document or Crazyfellow's parenting and gene guide. Algorithms come from Erzzy and Kireina's reference, KuromiAK's race mechanics reference, and Crazyfellow's parenting and gene guide.

View File

@@ -130,6 +130,53 @@ race sparks with skills always give +1, skill sparks always give +1-5, unique sp
- support card skill hints are defined in single_mode_hint_gain - support card skill hints are defined in single_mode_hint_gain
- skill_set is NOT trainee skills, seems to be npcs - skill_set is NOT trainee skills, seems to be npcs
skill categories:
- 0 passive
- 1 early race
- 2 mid-race
- 3 late race or last spurt
- 4 anytime
- 5 unique
unique_skill_id_1 is for inherited uniques, points to non-inherited version.
unique_skill_id_2 is same but points to 1\*/2\* version.
exp_type appears to be whether the skill gains levels, i.e. own unique.
ability time and cooldown time are given in tenths of milliseconds, i.e. divide by 10000 to get seconds.
ability types:
- 1 speed bonus => ability_value / 10000 is flat gain
- 2 stamina bonus
- 3 power bonus
- 4 guts bonus
- 5 wit bonus
- 6 runaway => ability_value = 0
- 8 vision => ability_value / 10000 is amount (what are the units? what does vision do??)
- 9 heal or stam debuff => ability_value / 10000 is hp modify
- 10 starting gate delay (focus, concentration, gatekept) => ability_value / 10000 is multiplier
- 13 frenzy => ability_value / 10000 is rush time modifier (add?)
- 21 current speed => ability_value / 10000 is multiplier modifier
- 27 target speed => ''
- 28 lane change speed => ''
- 31 accel => ''
- 35 force lane change? it's only on dodging danger/sixth sense with a value of 5000 on both, gametora says "change lane (50% of the track)"
target types:
- 0 none (second and third abilities on skills that only have one)
- 1 self
- 4 others in view
- 9 others ahead, target_value is number of targets (18 for all)
- 10 others behind, target_value is number of targets
- 18 others in style, target_value is style (1=front, 2=pace, 3=late, 4=end)
- 19 rushing others ahead
- 20 rushing others behind
- 21 rushing others in style, target_value is style
ability_value_usage can be 1 for plain or 2-6 for aoharu stat skill stat scaling
seems to be activate_lot = 1 means wit check, 0 means guaranteed
# races # races
- group 1, grade: g1 100, g2 200, g3 300, op 400, pre-op 700 - group 1, grade: g1 100, g2 200, g3 300, op 400, pre-op 700

1025
horse/skill-group.kk Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -61,25 +61,37 @@ func ExecCharacterKK(t *template.Template, w io.Writer, c []NamedID[Character],
return t.ExecuteTemplate(w, "koka-character", &data) return t.ExecuteTemplate(w, "koka-character", &data)
} }
func ExecSkillKK(t *template.Template, w io.Writer, g []NamedID[SkillGroup]) error { func ExecSkillKK(t *template.Template, w io.Writer, g []NamedID[SkillGroup], s []Skill) error {
data := struct { data := struct {
Groups []NamedID[SkillGroup] Groups []NamedID[SkillGroup]
}{g} Skills []Skill
}{g, s}
return t.ExecuteTemplate(w, "koka-skill", &data) return t.ExecuteTemplate(w, "koka-skill", &data)
} }
const replaceDash = " ,!?/+();#○◎☆♡'&=♪∀゚∴" func ExecSkillGroupKK(t *template.Template, w io.Writer, g []NamedID[SkillGroup], s []Skill) error {
data := struct {
Groups []NamedID[SkillGroup]
Skills []Skill
}{g, s}
return t.ExecuteTemplate(w, "koka-skill-group", &data)
}
const replaceDash = " ,!?/+();#○☆♡'&=♪∀゚∴"
var ( var (
kkReplace = func() *strings.Replacer { kkReplace = func() *strings.Replacer {
r := []string{ r := []string{
"Triple 7s", "Triple-Sevens", // hard to replace with the right thing automatically "Triple 7s", "Triple-Sevens", // hard to replace with the right thing automatically
"1,500,000 CC", "Million-CC", "1,500,000 CC", "One-Million-CC",
"15,000,000 CC", "Fifteen-Million-CC",
"1st", "First", "1st", "First",
".", "", ".", "",
"'s", "s", "'s", "s",
"ó", "o", "ó", "o",
"∞", "Infinity", "∞", "Infinity",
"×", "x",
"◎", "Lv2",
} }
for _, c := range replaceDash { for _, c := range replaceDash {
r = append(r, string(c), "-") r = append(r, string(c), "-")

View File

@@ -20,6 +20,9 @@ var characterAffinity3SQL string
//go:embed skill-group.sql //go:embed skill-group.sql
var skillGroupSQL string var skillGroupSQL string
//go:embed skill.sql
var skillSQL string
type ( type (
Character struct{} Character struct{}
SkillGroup struct{} SkillGroup struct{}
@@ -174,3 +177,133 @@ func SkillGroups(ctx context.Context, db *sqlitex.Pool) ([]NamedID[SkillGroup],
} }
return r, nil return r, nil
} }
type Skill struct {
ID int
Name string
Description string
GroupID int
GroupName string
Rarity int
GroupRate int
GradeValue int
WitCheck bool
Activations [2]SkillActivation
IconID int
Index int
}
type SkillActivation struct {
Precondition string
Condition string
Duration float64
Cooldown float64
Abilities [3]SkillAbility
}
type SkillAbility struct {
Type int
ValueUsage int
Value float64
Target int
TargetValue int
}
func Skills(ctx context.Context, db *sqlitex.Pool) ([]Skill, error) {
conn, err := db.Take(ctx)
defer db.Put(conn)
if err != nil {
return nil, fmt.Errorf("couldn't get connection for skills: %w", err)
}
stmt, _, err := conn.PrepareTransient(skillSQL)
if err != nil {
return nil, fmt.Errorf("couldn't prepare statement for skills: %w", err)
}
defer stmt.Finalize()
var r []Skill
for {
ok, err := stmt.Step()
if err != nil {
return nil, fmt.Errorf("error stepping skills: %w", err)
}
if !ok {
break
}
s := Skill{
ID: stmt.ColumnInt(0),
Name: stmt.ColumnText(1),
Description: stmt.ColumnText(2),
GroupID: stmt.ColumnInt(3),
GroupName: stmt.ColumnText(4),
Rarity: stmt.ColumnInt(5),
GroupRate: stmt.ColumnInt(6),
GradeValue: stmt.ColumnInt(7),
WitCheck: stmt.ColumnInt(8) != 0,
Activations: [2]SkillActivation{
{
Precondition: stmt.ColumnText(9),
Condition: stmt.ColumnText(10),
Duration: stmt.ColumnFloat(11),
Cooldown: stmt.ColumnFloat(12),
Abilities: [3]SkillAbility{
{
Type: stmt.ColumnInt(13),
ValueUsage: stmt.ColumnInt(14),
Value: stmt.ColumnFloat(15),
Target: stmt.ColumnInt(16),
TargetValue: stmt.ColumnInt(17),
},
{
Type: stmt.ColumnInt(18),
ValueUsage: stmt.ColumnInt(19),
Value: stmt.ColumnFloat(20),
Target: stmt.ColumnInt(21),
TargetValue: stmt.ColumnInt(22),
},
{
Type: stmt.ColumnInt(23),
ValueUsage: stmt.ColumnInt(24),
Value: stmt.ColumnFloat(25),
Target: stmt.ColumnInt(26),
TargetValue: stmt.ColumnInt(27),
},
},
},
{
Precondition: stmt.ColumnText(28),
Condition: stmt.ColumnText(29),
Duration: stmt.ColumnFloat(30),
Cooldown: stmt.ColumnFloat(31),
Abilities: [3]SkillAbility{
{
Type: stmt.ColumnInt(32),
ValueUsage: stmt.ColumnInt(33),
Value: stmt.ColumnFloat(34),
Target: stmt.ColumnInt(35),
TargetValue: stmt.ColumnInt(36),
},
{
Type: stmt.ColumnInt(37),
ValueUsage: stmt.ColumnInt(38),
Value: stmt.ColumnFloat(39),
Target: stmt.ColumnInt(40),
TargetValue: stmt.ColumnInt(41),
},
{
Type: stmt.ColumnInt(42),
ValueUsage: stmt.ColumnInt(43),
Value: stmt.ColumnFloat(44),
Target: stmt.ColumnInt(45),
TargetValue: stmt.ColumnInt(46),
},
},
},
},
IconID: stmt.ColumnInt(47),
Index: stmt.ColumnInt(48),
}
r = append(r, s)
}
return r, nil
}

View File

@@ -47,6 +47,7 @@ func main() {
pairs []AffinityRelation pairs []AffinityRelation
trios []AffinityRelation trios []AffinityRelation
sg []NamedID[SkillGroup] sg []NamedID[SkillGroup]
skills []Skill
) )
eg.Go(func() error { eg.Go(func() error {
slog.Info("get characters") slog.Info("get characters")
@@ -72,6 +73,12 @@ func main() {
sg = r sg = r
return err return err
}) })
eg.Go(func() error {
slog.Info("get skills")
r, err := Skills(ctx, db)
skills = r
return err
})
if err := eg.Wait(); err != nil { if err := eg.Wait(); err != nil {
slog.Error("load", slog.Any("err", err)) slog.Error("load", slog.Any("err", err))
os.Exit(1) os.Exit(1)
@@ -92,7 +99,15 @@ func main() {
return err return err
} }
slog.Info("write skills") slog.Info("write skills")
return ExecSkillKK(t, sf, sg) return ExecSkillKK(t, sf, sg, skills)
})
eg.Go(func() error {
sf, err := os.Create(filepath.Join(out, "skill-group.kk"))
if err != nil {
return err
}
slog.Info("write skill groups")
return ExecSkillGroupKK(t, sf, sg, skills)
}) })
if err := eg.Wait(); err != nil { if err := eg.Wait(); err != nil {
slog.Error("generate", slog.Any("err", err)) slog.Error("generate", slog.Any("err", err))

View File

@@ -1,7 +1,7 @@
{{ define "koka-skill" -}} {{- define "koka-skill-group" -}}
module horse/skill module horse/skill-group
// Automatically generated with the horsegen tool; DO NOT EDIT // Automatically generated with horsegen; DO NOT EDIT
// Skill groups. // Skill groups.
// A skill group may contain white, circle, double-circle, gold, and purple skills // A skill group may contain white, circle, double-circle, gold, and purple skills
@@ -27,7 +27,7 @@ pub fip(1) fun skill-group/from-id(^id: int): maybe<skill-group>
{{- end }} {{- end }}
_ -> Nothing _ -> Nothing
// Get names for skill groups. // Get the name for a skill group.
// Skill group names are the name of the base skill in the group. // Skill group names are the name of the base skill in the group.
pub fun skill-group/show(sg: skill-group): string pub fun skill-group/show(sg: skill-group): string
match sg match sg
@@ -45,4 +45,291 @@ pub fip fun skill-group/order2(a: skill-group, b: skill-group): order2<skill-gro
pub fun skill-group/(==)(a: skill-group, b: skill-group): bool pub fun skill-group/(==)(a: skill-group, b: skill-group): bool
a.group-id == b.group-id a.group-id == b.group-id
{{- end }} {{- end -}}
{{- define "koka-skill" -}}
module horse/skill
import std/num/float64
pub import horse/skill-group
// Skills instances.
pub type skill
{{- range $s := $.Skills }}
{{ kkenum $s.Name }}
{{- end }}
// Map a skill to its ID.
pub fip fun skill/skill-id(^s: skill): int
match s
{{- range $s := $.Skills }}
{{ kkenum $s.Name }} -> {{ $s.ID }}
{{- end }}
// Get the skill for an ID.
pub fip(1) fun skill/from-id(^id: int): maybe<skill>
match id
{{- range $s := $.Skills }}
{{ $s.ID }} -> Just( {{- kkenum $s.Name -}} )
{{- end }}
_ -> Nothing
// Get the name of a skill.
pub fun skill/show(s: skill): string
match s
{{- range $s := $.Skills }}
{{ kkenum $s.Name }} -> {{ printf "%q" $s.Name }}
{{- end }}
// Compare two skills by ID order.
pub fip fun skill/order2(a: skill, b: skill): order2<skill>
match cmp(a.skill-id, b.skill-id)
Lt -> Lt2(a, b)
Eq -> Eq2(a)
Gt -> Gt2(a, b)
pub fun skill/(==)(a: skill, b: skill): bool
a.skill-id == b.skill-id
// Get complete skill info.
pub fun skill/detail(^s: skill): skill-detail
match s
{{- range $s := $.Skills }}
{{ kkenum $s.Name }} -> {{ template "kk-render-skill-detail" $s }}
{{- end }}
// Details about a skill.
pub struct skill-detail
skill-id: int
name: string
description: string
group: skill-group
rarity: rarity
group-rate: int
grade-value: int
wit-check: bool
activations: list<activation>
icon-id: int
// Automatically generated.
// Shows a string representation of the `skill-detail` type.
pub fun skill-detail/show(this : skill-detail) : e string
match this
Skill-detail(skill-id, name, description, group, rarity, group-rate, grade-value, wit-check, activations, icon-id) -> "Skill-detail(skill-id: " ++ skill-id.show ++ ", name: " ++ name.show ++ ", description: " ++ description.show ++ ", group: " ++ group.show ++ ", rarity: " ++ rarity.show ++ ", group-rate: " ++ group-rate.show ++ ", grade-value: " ++ grade-value.show ++ ", wit-check: " ++ wit-check.show ++ ", activations: " ++ activations.show ++ ", icon-id: " ++ icon-id.show ++ ")"
// Skill rarity.
pub type rarity
Common // white
Rare // gold
Unique-Low // 1*/2* unique
Unique-Upgraded // 3*+ unique on a trainee upgraded from 1*/2*
Unique // base 3* unique
pub fun rarity/show(r: rarity): string
match r
Common -> "Common"
Rare -> "Rare"
Unique-Low -> "Unique (1☆/2☆)"
Unique-Upgraded -> "Unique (3☆+ from 1☆/2☆ upgraded)"
Unique -> "Unique (3☆+)"
// Condition and precondition logic.
pub alias condition = string
// Activation conditions and effects.
// A skill has one or two activations.
pub struct activation
precondition: condition
condition: condition
duration: float64 // seconds
cooldown: float64 // seconds
abilities: list<ability> // one to three elements
pub fun activation/show(a: activation): string
match a
Activation("", condition, -1.0, _, abilities) -> condition ++ " -> " ++ abilities.show
Activation("", condition, duration, cooldown, abilities) | cooldown >= 500.0 -> condition ++ " -> " ++ abilities.show ++ " for " ++ duration.show ++ "s"
Activation("", condition, duration, cooldown, abilities) -> condition ++ " -> " ++ abilities.show ++ " for " ++ duration.show ++ "s on " ++ cooldown.show ++ "s cooldown"
Activation(precondition, condition, -1.0, _, abilities)-> precondition ++ " -> " ++ condition ++ " -> " ++ abilities.show
Activation(precondition, condition, duration, cooldown, abilities) | cooldown >= 500.0 -> precondition ++ " -> " ++ condition ++ " -> " ++ abilities.show ++ " for " ++ duration.show ++ "s"
Activation(precondition, condition, duration, cooldown, abilities) -> precondition ++ "-> " ++ condition ++ " -> " ++ abilities.show ++ " for " ++ duration.show ++ "s on " ++ cooldown.show ++ "s cooldown"
// Effects of activating a skill.
pub struct ability
ability-type: ability-type
value-usage: value-usage
target: target
pub fun ability/show(a: ability): string
match a
Ability(t, Direct, Self) -> t.show
Ability(t, Direct, target) -> t.show ++ " " ++ target.show
Ability(t, v, Self) -> t.show ++ " scaling by " ++ v.show
Ability(t, v, target) -> t.show ++ " " ++ target.show ++ " scaling by " ++ v.show
// Target of a skill activation effect.
pub type ability-type
Passive-Speed(bonus: float64)
Passive-Stamina(bonus: float64)
Passive-Power(bonus: float64)
Passive-Guts(bonus: float64)
Passive-Wit(bonus: float64)
Runaway
Vision(bonus: float64)
HP(rate: float64)
Gate-Delay(rate: float64)
Frenzy(add: float64)
Current-Speed(rate: float64)
Target-Speed(rate: float64)
Lane-Speed(rate: float64)
Accel(rate: float64)
Lane-Change(rate: float64)
pub fun ability-type/show(a: ability-type): string
match a
Passive-Speed(bonus) -> "passive " ++ bonus.show ++ " Speed"
Passive-Stamina(bonus) -> "passive " ++ bonus.show ++ " Stamina"
Passive-Power(bonus) -> "passive " ++ bonus.show ++ " Power"
Passive-Guts(bonus) -> "passive " ++ bonus.show ++ " Guts"
Passive-Wit(bonus) -> "passive " ++ bonus.show ++ " Wit"
Runaway -> "enable Great Escape style"
Vision(bonus) -> bonus.show ++ " vision"
HP(rate) | rate >= 0.0 -> show(rate * 100.0) ++ "% HP recovery"
HP(rate) -> show(rate * 100.0) ++ "% HP loss"
Gate-Delay(rate) -> rate.show ++ "× gate delay"
Frenzy(add) -> add.show ++ "s longer Rushed"
Current-Speed(rate) -> show(rate * 100.0) ++ "% current speed"
Target-Speed(rate) -> show(rate * 100.0) ++ "% target speed"
Lane-Speed(rate) -> show(rate * 100.0) ++ "% lane speed"
Accel(rate) -> show(rate * 100.0) ++ "% acceleration"
Lane-Change(rate) -> rate.show ++ " course width movement"
// Special scaling for skill activation effects.
pub type value-usage
Direct
Team-Speed
Team-Stamina
Team-Power
Team-Guts
Team-Wit
Multiply-Random
pub fun value-usage/show(v: value-usage): string
match v
Direct -> "no scaling"
Team-Speed -> "team's Speed"
Team-Stamina -> "team's Stamina"
Team-Power -> "team's Power"
Team-Guts -> "team's Guts"
Team-Wit -> "team's Wit"
Multiply-Random -> "random multiplier (0× to 0.04×)"
// Who a skill activation targets.
pub type target
Self
In-View
Ahead(limit: int)
Behind(limit: int)
Style(style: style)
Rushing-Ahead(limit: int)
Rushing-Behind(limit: int)
Rushing-Style(style: style)
pub fun target/show(t: target): string
match t
Self -> "self"
In-View -> "others in field of view"
Ahead(limit) | limit >= 18 -> "others ahead"
Ahead(limit) -> "next " ++ limit.show ++ " others ahead"
Behind(limit) | limit >= 18 -> "others behind"
Behind(limit) -> "next " ++ limit.show ++ " others behind"
Style(Front-Runner) -> "other Front Runners"
Style(Pace-Chaser) -> "other Pace Chasers"
Style(Late-Surger) -> "other Late Surgers"
Style(End-Closer) -> "other End Closers"
Rushing-Ahead(limit) | limit >= 18 -> "others rushing ahead"
Rushing-Ahead(limit) -> "next " ++ limit.show ++ " others rushing ahead"
Rushing-Behind(limit) | limit >= 18 -> "others rushing behind"
Rushing-Behind(limit) -> "next " ++ limit.show ++ " others rushing behind"
Rushing-Style(Front-Runner) -> "rushing Front Runners"
Rushing-Style(Pace-Chaser) -> "rushing Pace Chasers"
Rushing-Style(Late-Surger) -> "rushing Late Surgers"
Rushing-Style(End-Closer) -> "rushing End Closers"
// Running style for skill targets.
// TODO(zeph): there is definitely a better place for this to live
pub type style
Front-Runner
Pace-Chaser
Late-Surger
End-Closer
{{- end -}}
{{ define "kk-render-skill-detail" }}
{{- /* Call with Skill structure as argument. */ -}}
Skill-detail(skill-id = {{ $.ID -}}
, name = {{ printf "%q" $.Name -}}
, description = {{ printf "%q" $.Description -}}
, group = {{ kkenum $.GroupName -}}
, rarity = {{ if eq $.Rarity 1 }}Common{{ else if eq $.Rarity 2 }}Rare{{ else if eq $.Rarity 3 }}Unique-Low{{ else if eq $.Rarity 4 }}Unique-Upgraded{{ else if eq $.Rarity 5 }}Unique{{ else }}??? $.Rarity={{ $.Rarity }}{{ end -}}
, group-rate = {{ $.GroupRate -}}
, grade-value = {{ $.GradeValue -}}
, wit-check = {{ if $.WitCheck }}True{{ else }}False{{ end -}}
, activations = [
{{- range $a := $.Activations -}}
{{- if ne $a.Condition "" -}}
Activation(precondition = {{ printf "%q" $a.Precondition -}}
, condition = {{ printf "%q" $a.Condition -}}
, duration = {{ printf "%f" $a.Duration -}}
, cooldown = {{ printf "%f" $a.Cooldown -}}
, abilities = [
{{- range $abil := $a.Abilities -}}
{{- if ne $abil.Type 0 -}}
Ability(ability-type =
{{- if eq $abil.Type 1 -}}Passive-Speed({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 2 -}}Passive-Stamina({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 3 -}}Passive-Power({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 4 -}}Passive-Guts({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 5 -}}Passive-Wit({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 6 -}}Runaway
{{- else if eq $abil.Type 8 -}}Vision({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 9 -}}HP({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 10 -}}Gate-Delay({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 13 -}}Frenzy({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 21 -}}Current-Speed({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 27 -}}Target-Speed({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 28 -}}Lane-Speed({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 31 -}}Accel({{ printf "%f" $abil.Value }})
{{- else if eq $abil.Type 35 -}}Lane-Change({{ printf "%f" $abil.Value }})
{{- else -}}??? $abil.Type={{$abil.Type}}
{{- end -}}
, value-usage =
{{- if eq $abil.ValueUsage 1 -}}Direct
{{- else if eq $abil.ValueUsage 3 -}}Team-Speed
{{- else if eq $abil.ValueUsage 4 -}}Team-Stamina
{{- else if eq $abil.ValueUsage 5 -}}Team-Power
{{- else if eq $abil.ValueUsage 6 -}}Team-Guts
{{- else if eq $abil.ValueUsage 7 -}}Team-Wit
{{- else if eq $abil.ValueUsage 8 -}}Multiply-Random
{{- else -}}??? $abil.ValueUsage={{ $abil.ValueUsage }}
{{- end -}}
, target =
{{- if eq $abil.Target 1 -}}Self
{{- else if eq $abil.Target 4 -}}In-View
{{- else if eq $abil.Target 9 -}}Ahead({{ $abil.TargetValue }})
{{- else if eq $abil.Target 10 -}}Behind({{ $abil.TargetValue }})
{{- else if eq $abil.Target 18 -}}Style({{ if eq $abil.TargetValue 1 }}Front-Runner{{ else if eq $abil.TargetValue 2 }}Pace-Chaser{{ else if eq $abil.TargetValue 3 }}Late-Surger{{ else if eq $abil.TargetValue 4 }}End-Closer{{ else }}??? $abil.TargetValue={{ $abil.TargetValue }}{{ end }})
{{- else if eq $abil.Target 19 -}}Rushing-Ahead({{ $abil.TargetValue }})
{{- else if eq $abil.Target 20 -}}Rushing-Behind({{ $abil.TargetValue }})
{{- else if eq $abil.Target 21 -}}Rushing-Style({{ if eq $abil.TargetValue 1 }}Front-Runner{{ else if eq $abil.TargetValue 2 }}Pace-Chaser{{ else if eq $abil.TargetValue 3 }}Late-Surger{{ else if eq $abil.TargetValue 4 }}End-Closer{{ else }}??? $abil.TargetValue={{ $abil.TargetValue }}{{ end }})
{{- end -}}
),
{{- end -}}
{{- end -}}
]),
{{- end -}}
{{- end -}}
], icon-id = {{ $.IconID -}}
)
{{- end -}}

69
horsegen/skill.sql Normal file
View File

@@ -0,0 +1,69 @@
WITH skill_names AS (
SELECT
n."index" AS "id",
n."text" AS "name",
d."text" AS "description"
FROM text_data n
JOIN text_data d ON n."index" = d."index" AND n."category" = 47 AND d."category" = 48
), skill_groups AS (
SELECT
group_id,
name
FROM skill_data d
JOIN skill_names n ON d.id = n.id
WHERE group_rate = 1
)
SELECT
d.id,
n.name,
n.description,
d.group_id,
g.name,
d.rarity,
d.group_rate,
d.grade_value,
d.activate_lot,
d.precondition_1,
d.condition_1,
IIF(d.float_ability_time_1 <= 0, CAST(d.float_ability_time_1 AS REAL), d.float_ability_time_1 / 1e4) AS float_ability_time_1,
IIF(d.float_cooldown_time_1 <= 0, CAST(d.float_cooldown_time_1 AS REAL), d.float_cooldown_time_1 / 1e4) AS float_cooldown_time_1,
d.ability_type_1_1,
d.ability_value_usage_1_1,
d.float_ability_value_1_1 / 1e4 AS float_ability_value_1_1,
d.target_type_1_1,
d.target_value_1_1,
d.ability_type_1_2,
d.ability_value_usage_1_2,
d.float_ability_value_1_2 / 1e4 AS float_ability_value_1_2,
d.target_type_1_2,
d.target_value_1_2,
d.ability_type_1_3,
d.ability_value_usage_1_3,
d.float_ability_value_1_3 / 1e4 AS float_ability_value_1_3,
d.target_type_1_3,
d.target_value_1_3,
d.precondition_2,
d.condition_2,
IIF(d.float_ability_time_2 <= 0, CAST(d.float_ability_time_2 AS REAL), d.float_ability_time_2 / 1e4) AS float_ability_time_2,
IIF(d.float_cooldown_time_2 <= 0, CAST(d.float_cooldown_time_2 AS REAL), d.float_cooldown_time_2 / 1e4) AS float_cooldown_time_2,
d.ability_type_2_1,
d.ability_value_usage_2_1,
d.float_ability_value_2_1 / 1e4 AS float_ability_value_2_1,
d.target_type_2_1,
d.target_value_2_1,
d.ability_type_2_2,
d.ability_value_usage_2_2,
d.float_ability_value_2_2 / 1e4 AS float_ability_value_2_2,
d.target_type_2_2,
d.target_value_2_2,
d.ability_type_2_3,
d.ability_value_usage_2_3,
d.float_ability_value_2_3 / 1e4 AS float_ability_value_2_3,
d.target_type_2_3,
d.target_value_2_3,
d.icon_id,
ROW_NUMBER() OVER (ORDER BY d.id) - 1 AS "index"
FROM skill_data d
JOIN skill_names n ON d.id = n.id
JOIN skill_groups g ON d.group_id = g.group_id
ORDER BY d.id