generate character.kk from game data
This commit is contained in:
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
Models, data, algorithms, and tools for Umamusume: Pretty Derby.
|
Models, data, algorithms, and tools for Umamusume: Pretty Derby.
|
||||||
|
|
||||||
Data generally comes from GameTora.
|
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 either Erzzy and Kireina's reference document or Crazyfellow's parenting and gene guide.
|
||||||
|
|||||||
19
go.mod
Normal file
19
go.mod
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
module git.sunturtle.xyz/zephyr/horse
|
||||||
|
|
||||||
|
go 1.24.1
|
||||||
|
|
||||||
|
require zombiezen.com/go/sqlite v1.4.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||||
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
modernc.org/libc v1.65.7 // indirect
|
||||||
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
modernc.org/memory v1.11.0 // indirect
|
||||||
|
modernc.org/sqlite v1.37.1 // indirect
|
||||||
|
)
|
||||||
51
go.sum
Normal file
51
go.sum
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||||
|
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||||
|
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||||
|
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||||
|
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||||
|
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||||
|
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||||
|
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||||
|
modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
|
||||||
|
modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||||
|
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||||
|
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||||
|
modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00=
|
||||||
|
modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
|
||||||
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
|
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
|
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||||
|
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||||
|
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||||
|
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||||
|
modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs=
|
||||||
|
modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g=
|
||||||
|
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||||
|
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
zombiezen.com/go/sqlite v1.4.2 h1:KZXLrBuJ7tKNEm+VJcApLMeQbhmAUOKA5VWS93DfFRo=
|
||||||
|
zombiezen.com/go/sqlite v1.4.2/go.mod h1:5Kd4taTAD4MkBzT25mQ9uaAlLjyR0rFhsR6iINO70jc=
|
||||||
77341
horse/character.kk
77341
horse/character.kk
File diff suppressed because it is too large
Load Diff
6
horsegen/README.md
Normal file
6
horsegen/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# gen
|
||||||
|
|
||||||
|
Go tool to generate the Koka source code from the game's SQLite database.
|
||||||
|
|
||||||
|
Code is generated using Go templates.
|
||||||
|
Templates use a `kkenum` function which converts a name `Mr. C.B.` to a Koka enumerant name `Mr-CB`.
|
||||||
34
horsegen/character.affinity2.sql
Normal file
34
horsegen/character.affinity2.sql
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
WITH uma_names AS (
|
||||||
|
SELECT
|
||||||
|
"index" AS "id",
|
||||||
|
"text" AS "name"
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 6 AND "index" BETWEEN 1000 AND 1999
|
||||||
|
-- Exclude characters who have no succession relations defined.
|
||||||
|
AND "index" IN (SELECT chara_id FROM succession_relation_member)
|
||||||
|
), pairs AS (
|
||||||
|
SELECT
|
||||||
|
a.id AS id_a,
|
||||||
|
a.name AS name_a,
|
||||||
|
b.id AS id_b,
|
||||||
|
b.name AS name_b
|
||||||
|
FROM uma_names a
|
||||||
|
JOIN uma_names b ON a.id != b.id -- exclude reflexive cases
|
||||||
|
), relation_pairs AS (
|
||||||
|
SELECT
|
||||||
|
ra.relation_type,
|
||||||
|
ra.chara_id AS id_a,
|
||||||
|
rb.chara_id AS id_b
|
||||||
|
FROM succession_relation_member ra
|
||||||
|
JOIN succession_relation_member rb ON ra.relation_type = rb.relation_type
|
||||||
|
), affinity AS (
|
||||||
|
SELECT
|
||||||
|
pairs.*,
|
||||||
|
SUM(IFNULL(relation_point, 0)) AS base_affinity
|
||||||
|
FROM pairs
|
||||||
|
LEFT JOIN relation_pairs rp ON pairs.id_a = rp.id_a AND pairs.id_b = rp.id_b
|
||||||
|
LEFT JOIN succession_relation sr ON rp.relation_type = sr.relation_type
|
||||||
|
GROUP BY pairs.id_a, pairs.id_b
|
||||||
|
)
|
||||||
|
SELECT * FROM affinity
|
||||||
|
ORDER BY id_a, id_b
|
||||||
41
horsegen/character.affinity3.sql
Normal file
41
horsegen/character.affinity3.sql
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
WITH uma_names AS (
|
||||||
|
SELECT
|
||||||
|
"index" AS "id",
|
||||||
|
"text" AS "name"
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 6 AND "index" BETWEEN 1000 AND 1999
|
||||||
|
-- Exclude characters who have no succession relations defined.
|
||||||
|
AND "index" IN (SELECT chara_id FROM succession_relation_member)
|
||||||
|
), trios AS (
|
||||||
|
SELECT
|
||||||
|
a.id AS id_a,
|
||||||
|
a.name AS name_a,
|
||||||
|
b.id AS id_b,
|
||||||
|
b.name AS name_b,
|
||||||
|
c.id AS id_c,
|
||||||
|
c.name AS name_c,
|
||||||
|
ra.relation_type
|
||||||
|
FROM uma_names a
|
||||||
|
JOIN uma_names b ON a.id != b.id
|
||||||
|
JOIN uma_names c ON a.id != c.id AND b.id != c.id
|
||||||
|
JOIN succession_relation_member ra ON a.id = ra.chara_id
|
||||||
|
JOIN succession_relation_member rb ON b.id = rb.chara_id
|
||||||
|
JOIN succession_relation_member rc ON c.id = rc.chara_id
|
||||||
|
WHERE
|
||||||
|
ra.relation_type = rb.relation_type
|
||||||
|
AND ra.relation_type = rc.relation_type
|
||||||
|
), affinity AS (
|
||||||
|
SELECT
|
||||||
|
id_a,
|
||||||
|
name_a,
|
||||||
|
id_b,
|
||||||
|
name_b,
|
||||||
|
id_c,
|
||||||
|
name_c,
|
||||||
|
SUM(relation_point) AS base_affinity
|
||||||
|
FROM trios
|
||||||
|
JOIN succession_relation sr ON trios.relation_type = sr.relation_type
|
||||||
|
GROUP BY id_a, id_b, id_c
|
||||||
|
)
|
||||||
|
SELECT * FROM affinity
|
||||||
|
ORDER BY id_a, id_b, id_c
|
||||||
81
horsegen/character.kk.template
Normal file
81
horsegen/character.kk.template
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
{{ define "koka-character" -}}
|
||||||
|
module horse/character
|
||||||
|
|
||||||
|
// Automatically generated with the horsegen tool; DO NOT EDIT
|
||||||
|
|
||||||
|
// Character identity.
|
||||||
|
pub type character
|
||||||
|
{{- range $uma := $.Characters }}
|
||||||
|
{{ kkenum $uma.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
// The list of all characters.
|
||||||
|
val character/all = [
|
||||||
|
{{- range $uma := $.Characters }}
|
||||||
|
{{ kkenum $uma.Name }},
|
||||||
|
{{- end }}
|
||||||
|
]
|
||||||
|
|
||||||
|
// Get the character for a character ID.
|
||||||
|
// Generally, these are four digit numbers in the range 1000-1999.
|
||||||
|
pub fun character/from-id(id: int): maybe<character>
|
||||||
|
match id
|
||||||
|
{{- range $uma := $.Characters }}
|
||||||
|
{{ $uma.ID }} -> Just( {{- kkenum $uma.Name -}} )
|
||||||
|
{{- end }}
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
// Get the ID for a character.
|
||||||
|
pub fun character/id(c: character): int
|
||||||
|
match c
|
||||||
|
{{- range $uma := $.Characters }}
|
||||||
|
{{ kkenum $uma.Name }} -> {{ $uma.ID }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
// Get the name of a character.
|
||||||
|
pub fun character/show(c: character): string
|
||||||
|
match c
|
||||||
|
{{- range $uma := $.Characters }}
|
||||||
|
{{ kkenum $uma.Name }} -> {{ printf "%q" $uma.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
// Compare two characters.
|
||||||
|
pub fip fun character/order2(a: character, b: character): order2<character>
|
||||||
|
match (a, b)
|
||||||
|
{{- range $uma := $.Characters }}{{ $e := kkenum $uma.Name }}
|
||||||
|
( {{- $e }}, {{ $e -}} ) -> Eq2( {{- $e -}} )
|
||||||
|
{{- if ne $uma.ID $.MaxID }}
|
||||||
|
( {{- $e }}, b') -> Lt2( {{- $e }}, b')
|
||||||
|
(a', {{ $e -}} ) -> Gt2( {{- $e }}, a')
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
// Character equality.
|
||||||
|
pub fun character/(==)(a: character, b: character): bool
|
||||||
|
match (a, b)
|
||||||
|
{{- range $uma := $.Characters }}{{ $e := kkenum $uma.Name }}
|
||||||
|
( {{- $e }}, {{ $e -}} ) -> True
|
||||||
|
{{- end }}
|
||||||
|
_ -> False
|
||||||
|
|
||||||
|
// Base affinity between a pair using the global ruleset.
|
||||||
|
pub fun global/pair-affinity(a: character, b: character): int
|
||||||
|
match (a, b)
|
||||||
|
{{- range $pair := $.Pairs }}
|
||||||
|
( {{- kkenum $pair.NameA }}, {{ kkenum $pair.NameB -}} ) -> {{ $pair.Affinity }}
|
||||||
|
{{- end }}
|
||||||
|
// Reflexive cases are always 0.
|
||||||
|
{{- range $uma := $.Characters }}{{ $e := kkenum $uma.Name }}
|
||||||
|
( {{- $e }}, {{ $e -}} ) -> 0
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/* TODO: probably use a data structure instead of hoping the compilers fix this */ -}}
|
||||||
|
// Base affinity for a trio using the global ruleset.
|
||||||
|
pub fun global/trio-affinity(a: character, b: character, c: character): int
|
||||||
|
match (a, b, c)
|
||||||
|
{{- range $trio := $.Trios }}
|
||||||
|
( {{- kkenum $trio.NameA }}, {{ kkenum $trio.NameB }}, {{ kkenum $trio.NameC -}} ) -> {{ $trio.Affinity }}
|
||||||
|
{{- end }}
|
||||||
|
(_, _, _) -> 0
|
||||||
|
|
||||||
|
{{- end }}
|
||||||
7
horsegen/character.sql
Normal file
7
horsegen/character.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
SELECT
|
||||||
|
"index" AS "id",
|
||||||
|
"text" AS "name"
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 6 AND "index" BETWEEN 1000 AND 1999
|
||||||
|
-- Exclude characters who have no succession relations defined.
|
||||||
|
AND "index" IN (SELECT chara_id FROM succession_relation_member)
|
||||||
46
horsegen/gen.go
Normal file
46
horsegen/gen.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed character.kk.template
|
||||||
|
var characterKK string
|
||||||
|
|
||||||
|
// LoadTemplates sets up templates to render game data to source code.
|
||||||
|
func LoadTemplates() (*template.Template, error) {
|
||||||
|
t := template.New("root")
|
||||||
|
t.Funcs(template.FuncMap{
|
||||||
|
"kkenum": kkenum,
|
||||||
|
})
|
||||||
|
t, err := t.Parse(characterKK)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing characterKK: %w", err)
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecCharacterKK renders the Koka character module to w.
|
||||||
|
func ExecCharacterKK(t *template.Template, w io.Writer, c []Character, pairs, trios []AffinityRelation) error {
|
||||||
|
maxid := 0
|
||||||
|
for _, u := range c {
|
||||||
|
maxid = max(maxid, u.ID)
|
||||||
|
}
|
||||||
|
data := struct {
|
||||||
|
Characters []Character
|
||||||
|
Pairs []AffinityRelation
|
||||||
|
Trios []AffinityRelation
|
||||||
|
MaxID int
|
||||||
|
}{c, pairs, trios, maxid}
|
||||||
|
return t.ExecuteTemplate(w, "koka-character", &data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func kkenum(name string) string {
|
||||||
|
name = strings.ReplaceAll(name, ".", "")
|
||||||
|
name = strings.ReplaceAll(name, " ", "-")
|
||||||
|
return name
|
||||||
|
}
|
||||||
131
horsegen/load.go
Normal file
131
horsegen/load.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"zombiezen.com/go/sqlite/sqlitex"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed character.sql
|
||||||
|
var characterSQL string
|
||||||
|
|
||||||
|
//go:embed character.affinity2.sql
|
||||||
|
var characterAffinity2SQL string
|
||||||
|
|
||||||
|
//go:embed character.affinity3.sql
|
||||||
|
var characterAffinity3SQL string
|
||||||
|
|
||||||
|
type Character struct {
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Characters(ctx context.Context, db *sqlitex.Pool) ([]Character, error) {
|
||||||
|
conn, err := db.Take(ctx)
|
||||||
|
defer db.Put(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't get connection for characters: %w", err)
|
||||||
|
}
|
||||||
|
stmt, _, err := conn.PrepareTransient(characterSQL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't prepare statement for characters: %w", err)
|
||||||
|
}
|
||||||
|
defer stmt.Finalize()
|
||||||
|
|
||||||
|
var r []Character
|
||||||
|
for {
|
||||||
|
ok, err := stmt.Step()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error stepping characters: %w", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c := Character{
|
||||||
|
ID: stmt.ColumnInt(0),
|
||||||
|
Name: stmt.ColumnText(1),
|
||||||
|
}
|
||||||
|
r = append(r, c)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AffinityRelation struct {
|
||||||
|
IDA int
|
||||||
|
NameA string
|
||||||
|
IDB int
|
||||||
|
NameB string
|
||||||
|
IDC int
|
||||||
|
NameC string
|
||||||
|
Affinity int
|
||||||
|
}
|
||||||
|
|
||||||
|
func CharacterPairs(ctx context.Context, db *sqlitex.Pool) ([]AffinityRelation, error) {
|
||||||
|
conn, err := db.Take(ctx)
|
||||||
|
defer db.Put(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't get connection for character pairs: %w", err)
|
||||||
|
}
|
||||||
|
stmt, _, err := conn.PrepareTransient(characterAffinity2SQL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't prepare statement for character pairs: %w", err)
|
||||||
|
}
|
||||||
|
defer stmt.Finalize()
|
||||||
|
|
||||||
|
var r []AffinityRelation
|
||||||
|
for {
|
||||||
|
ok, err := stmt.Step()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error stepping character pairs: %w", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p := AffinityRelation{
|
||||||
|
IDA: stmt.ColumnInt(0),
|
||||||
|
NameA: stmt.ColumnText(1),
|
||||||
|
IDB: stmt.ColumnInt(2),
|
||||||
|
NameB: stmt.ColumnText(3),
|
||||||
|
Affinity: stmt.ColumnInt(4),
|
||||||
|
}
|
||||||
|
r = append(r, p)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CharacterTrios(ctx context.Context, db *sqlitex.Pool) ([]AffinityRelation, error) {
|
||||||
|
conn, err := db.Take(ctx)
|
||||||
|
defer db.Put(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't get connection for character trios: %w", err)
|
||||||
|
}
|
||||||
|
stmt, _, err := conn.PrepareTransient(characterAffinity3SQL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't prepare statement for character trios: %w", err)
|
||||||
|
}
|
||||||
|
defer stmt.Finalize()
|
||||||
|
|
||||||
|
var r []AffinityRelation
|
||||||
|
for {
|
||||||
|
ok, err := stmt.Step()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error stepping character trios: %w", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p := AffinityRelation{
|
||||||
|
IDA: stmt.ColumnInt(0),
|
||||||
|
NameA: stmt.ColumnText(1),
|
||||||
|
IDB: stmt.ColumnInt(2),
|
||||||
|
NameB: stmt.ColumnText(3),
|
||||||
|
IDC: stmt.ColumnInt(4),
|
||||||
|
NameC: stmt.ColumnText(5),
|
||||||
|
Affinity: stmt.ColumnInt(6),
|
||||||
|
}
|
||||||
|
r = append(r, p)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
66
horsegen/main.go
Normal file
66
horsegen/main.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"zombiezen.com/go/sqlite"
|
||||||
|
"zombiezen.com/go/sqlite/sqlitex"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
mdb string
|
||||||
|
out string
|
||||||
|
)
|
||||||
|
flag.StringVar(&mdb, "mdb", os.ExpandEnv(`$USERPROFILE\AppData\LocalLow\Cygames\Umamusume\master\master.mdb`), "`path` to Umamusume master.mdb")
|
||||||
|
flag.StringVar(&out, "kk", `.\horse`, "existing `dir`ectory for output Koka files")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
stop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
t, err := LoadTemplates()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "loading templates: %s\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sqlitex.NewPool(mdb, sqlitex.PoolOptions{Flags: sqlite.OpenReadOnly})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
charas, err := Characters(ctx, db)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
pairs, err := CharacterPairs(ctx, db)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
trios, err := CharacterTrios(ctx, db)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cf, err := os.Create(filepath.Join(out, "character.kk"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := ExecCharacterKK(t, cf, charas, pairs, trios); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
// continue on
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user