Compare commits

...

10 Commits

54 changed files with 3115 additions and 258290 deletions
-2
View File
@@ -27,8 +27,6 @@ type Conversation struct {
Number int `json:"number"`
// Location is the ID of the location the conversation occurs in the lobby.
Location LobbyConversationLocationID `json:"location"`
// LocationName is the name of the lobby location, for convenience.
LocationName string `json:"location_name"`
// Chara1 is the first conversation participant ID.
// It does not necessarily match CharacterID.
Chara1 CharacterID `json:"chara_1"`
+104 -79
View File
@@ -13,7 +13,7 @@ import (
"net/http"
"os"
"os/signal"
"path/filepath"
"strconv"
"time"
"github.com/disgoorg/disgo"
@@ -24,16 +24,18 @@ import (
"github.com/disgoorg/disgo/httpserver"
"github.com/disgoorg/disgo/rest"
httpmiddle "github.com/go-chi/chi/v5/middleware"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
"git.sunturtle.xyz/zephyr/horse"
"git.sunturtle.xyz/zephyr/horse/mdb"
)
func main() {
var (
// public site
addr string
dataDir string
public string
addr string
mdbf string
public string
// discord
tokenFile string
apiRoute string
@@ -42,8 +44,8 @@ func main() {
level slog.Level
textfmt string
)
flag.StringVar(&addr, "http", ":80", "`address` to bind HTTP server")
flag.StringVar(&dataDir, "data", "", "`dir`ectory containing exported json data")
flag.StringVar(&addr, "http", ":13669", "`address` to bind HTTP server")
flag.StringVar(&mdbf, "mdb", "", "`path` to master.mdb")
flag.StringVar(&public, "public", "", "`dir`ectory containing the website to serve")
flag.StringVar(&tokenFile, "token", "", "`file` containing the Discord bot token")
flag.StringVar(&apiRoute, "route", "/interactions/callback", "`path` to serve Discord HTTP API calls")
@@ -64,56 +66,78 @@ func main() {
}
slog.SetDefault(slog.New(lh))
stat, err := os.Stat(public)
if err != nil {
slog.Error("public", slog.Any("err", err))
os.Exit(1)
if public != "" {
stat, err := os.Stat(public)
if err != nil {
slog.Error("public", slog.Any("err", err))
os.Exit(1)
}
if !stat.IsDir() {
slog.Error("public", slog.String("err", "not a directory"))
os.Exit(1)
}
}
if !stat.IsDir() {
slog.Error("public", slog.String("err", "not a directory"))
os.Exit(1)
}
skills, err := loadSkills(filepath.Join(dataDir, "skill.json"))
slog.Info("loaded skills", slog.Int("count", len(skills)))
groups, err2 := loadSkillGroups(filepath.Join(dataDir, "skill-group.json"))
slog.Info("loaded skill groups", slog.Int("count", len(groups)))
if err = errors.Join(err, err2); err != nil {
slog.Error("loading data", slog.Any("err", err))
os.Exit(1)
}
skillSrv := newSkillServer(skills, groups)
slog.Info("skill server ready")
token, err := os.ReadFile(tokenFile)
if err != nil {
slog.Error("reading token", slog.Any("err", err))
os.Exit(1)
}
token = bytes.TrimSuffix(token, []byte{'\n'})
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
r := handler.New()
r.DefaultContext(func() context.Context { return ctx })
r.Use(middleware.Go)
r.Use(logMiddleware)
r.Route("/skill", func(r handler.Router) {
r.SlashCommand("/", skillSrv.slash)
r.Autocomplete("/", skillSrv.autocomplete)
r.SelectMenuComponent("/swap", skillSrv.menu)
r.ButtonComponent("/swap/{id}", skillSrv.button)
})
slog.Info("connect", slog.String("disgo", disgo.Version))
client, err := disgo.New(string(token), bot.WithDefaultGateway(), bot.WithEventListeners(r))
db, err := sqlitex.NewPool(mdbf, sqlitex.PoolOptions{Flags: sqlite.OpenReadOnly})
if err != nil {
slog.Error("building bot", slog.Any("err", err))
slog.Error("mdb", slog.Any("err", err))
os.Exit(1)
}
var client *bot.Client
if tokenFile != "" {
skills, err := mdb.Skills(ctx, db)
slog.Info("loaded skills", slog.Int("count", len(skills)))
groups, err2 := mdb.SkillGroups(ctx, db)
slog.Info("loaded skill groups", slog.Int("count", len(groups)))
if err = errors.Join(err, err2); err != nil {
slog.Error("loading data", slog.Any("err", err))
os.Exit(1)
}
skillSrv := newSkillServer(skills, groups)
slog.Info("skill server ready")
r := handler.New()
r.DefaultContext(func() context.Context { return ctx })
r.Use(middleware.Go)
r.Use(logMiddleware)
r.Route("/skill", func(r handler.Router) {
r.SlashCommand("/", skillSrv.slash)
r.Autocomplete("/", skillSrv.autocomplete)
r.SelectMenuComponent("/swap", skillSrv.menu)
r.ButtonComponent("/swap/{id}", skillSrv.button)
})
slog.Info("connect", slog.String("disgo", disgo.Version))
token, err := os.ReadFile(tokenFile)
if err != nil {
slog.Error("reading token", slog.Any("err", err))
os.Exit(1)
}
token = bytes.TrimSuffix(token, []byte{'\n'})
client, err = disgo.New(string(token), bot.WithDefaultGateway(), bot.WithEventListeners(r))
if err != nil {
slog.Error("building bot", slog.Any("err", err))
os.Exit(1)
}
}
mux := http.NewServeMux()
mux.Handle("GET /", httpmiddle.Compress(5)(http.FileServerFS(os.DirFS(public))))
mux.Handle("GET /api/data/", httpmiddle.Compress(5)(http.StripPrefix("/api/data", http.FileServerFS(os.DirFS(dataDir)))))
if public != "" {
mux.Handle("GET /", httpmiddle.Compress(5)(http.FileServerFS(os.DirFS(public))))
}
mux.Handle("GET /api/global/affinity", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.AffinitySummary)))
mux.Handle("GET /api/global/character", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Characters)))
mux.Handle("GET /api/global/conversation", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Conversations)))
mux.Handle("GET /api/global/race", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Races)))
mux.Handle("GET /api/global/saddle", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Saddles)))
mux.Handle("GET /api/global/scenario", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Scenarios)))
mux.Handle("GET /api/global/skill", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Skills)))
mux.Handle("GET /api/global/skill-group", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.SkillGroups)))
mux.Handle("GET /api/global/spark", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Sparks)))
mux.Handle("GET /api/global/uma", httpmiddle.Compress(5)(dataroute(ctx, db, mdb.Umas)))
if pubkey != "" {
pk, err := hex.DecodeString(pubkey)
if err != nil {
@@ -130,7 +154,7 @@ func main() {
}
srv := http.Server{
Addr: addr,
Handler: mux,
Handler: httpmiddle.Logger(mux),
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
BaseContext: func(l net.Listener) context.Context { return ctx },
@@ -144,23 +168,28 @@ func main() {
slog.Error("HTTP server closed", slog.Any("err", err))
}()
if err := handler.SyncCommands(client, commands, nil, rest.WithCtx(ctx)); err != nil {
slog.Error("syncing commands", slog.Any("err", err))
os.Exit(1)
if client != nil {
if err := handler.SyncCommands(client, commands, nil, rest.WithCtx(ctx)); err != nil {
slog.Error("syncing commands", slog.Any("err", err))
os.Exit(1)
}
slog.Info("start gateway")
if err := client.OpenGateway(ctx); err != nil {
slog.Error("starting gateway", slog.Any("err", err))
stop()
}
}
slog.Info("start gateway")
if err := client.OpenGateway(ctx); err != nil {
slog.Error("starting gateway", slog.Any("err", err))
stop()
}
slog.Info("ready")
<-ctx.Done()
stop()
ctx, stop = context.WithTimeout(context.Background(), 5*time.Second)
defer stop()
client.Close(ctx)
if client != nil {
client.Close(ctx)
}
if err := srv.Shutdown(ctx); err != nil {
slog.Error("HTTP API shutdown", slog.Any("err", err))
os.Exit(1)
@@ -186,26 +215,22 @@ var commands = []discord.ApplicationCommandCreate{
},
}
func loadSkills(file string) ([]horse.Skill, error) {
b, err := os.ReadFile(file)
if err != nil {
return nil, err
func dataroute[T any](ctx context.Context, db *sqlitex.Pool, f func(context.Context, *sqlitex.Pool) ([]T, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, err := f(ctx, db)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
b, err := json.Marshal(data)
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Length", strconv.Itoa(len(b)))
if _, err := w.Write(b); err != nil {
slog.Error("writing response", slog.Any("err", err))
return
}
}
var skills []horse.Skill
if err := json.Unmarshal(b, &skills); err != nil {
return nil, err
}
return skills, nil
}
func loadSkillGroups(file string) ([]horse.SkillGroup, error) {
b, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var groups []horse.SkillGroup
if err := json.Unmarshal(b, &groups); err != nil {
return nil, err
}
return groups, nil
}
-435
View File
@@ -1,435 +0,0 @@
package main
import (
"bufio"
"cmp"
"context"
_ "embed"
"encoding/json"
"errors"
"flag"
"fmt"
"log/slog"
"maps"
"os"
"os/signal"
"path/filepath"
"slices"
"strconv"
"strings"
"golang.org/x/sync/errgroup"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
"git.sunturtle.xyz/zephyr/horse"
)
func main() {
var (
mdb string
out string
region string
)
flag.StringVar(&mdb, "mdb", os.ExpandEnv(`$USERPROFILE\AppData\LocalLow\Cygames\Umamusume\master\master.mdb`), "`path` to Umamusume master.mdb")
flag.StringVar(&out, "o", `.`, "`dir`ectory for output files")
flag.StringVar(&region, "region", "global", "region the database is for (global, jp)")
flag.Parse()
slog.Info("open", slog.String("mdb", mdb))
db, err := sqlitex.NewPool(mdb, sqlitex.PoolOptions{Flags: sqlite.OpenReadOnly})
if err != nil {
slog.Error("opening mdb", slog.String("mdb", mdb), slog.Any("err", err))
os.Exit(1)
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
go func() {
<-ctx.Done()
stop()
}()
loadgroup, ctx1 := errgroup.WithContext(ctx)
charas := load(ctx1, loadgroup, db, "characters", characterSQL, func(s *sqlite.Stmt) horse.Character {
return horse.Character{
ID: horse.CharacterID(s.ColumnInt(0)),
Name: s.ColumnText(1),
}
})
aff := load(ctx1, loadgroup, db, "pair affinity", affinitySQL, func(s *sqlite.Stmt) horse.AffinityRelation {
return horse.AffinityRelation{
IDA: s.ColumnInt(0),
IDB: s.ColumnInt(1),
IDC: s.ColumnInt(2),
Affinity: s.ColumnInt(3),
}
})
umas := load(ctx1, loadgroup, db, "umas", umaSQL, func(s *sqlite.Stmt) horse.Uma {
return horse.Uma{
ID: horse.UmaID(s.ColumnInt(0)),
CharacterID: horse.CharacterID(s.ColumnInt(1)),
Name: s.ColumnText(2),
Variant: s.ColumnText(3),
Sprint: horse.AptitudeLevel(s.ColumnInt(4)),
Mile: horse.AptitudeLevel(s.ColumnInt(6)),
Medium: horse.AptitudeLevel(s.ColumnInt(7)),
Long: horse.AptitudeLevel(s.ColumnInt(8)),
Front: horse.AptitudeLevel(s.ColumnInt(9)),
Pace: horse.AptitudeLevel(s.ColumnInt(10)),
Late: horse.AptitudeLevel(s.ColumnInt(11)),
End: horse.AptitudeLevel(s.ColumnInt(12)),
Turf: horse.AptitudeLevel(s.ColumnInt(13)),
Dirt: horse.AptitudeLevel(s.ColumnInt(14)),
Unique: horse.SkillID(s.ColumnInt(15)),
Skill1: horse.SkillID(s.ColumnInt(16)),
Skill2: horse.SkillID(s.ColumnInt(17)),
Skill3: horse.SkillID(s.ColumnInt(18)),
SkillPL2: horse.SkillID(s.ColumnInt(19)),
SkillPL3: horse.SkillID(s.ColumnInt(20)),
SkillPL4: horse.SkillID(s.ColumnInt(21)),
SkillPL5: horse.SkillID(s.ColumnInt(22)),
}
})
sg := load(ctx1, loadgroup, db, "skill groups", skillGroupSQL, func(s *sqlite.Stmt) horse.SkillGroup {
return horse.SkillGroup{
ID: horse.SkillGroupID(s.ColumnInt(0)),
Skill1: horse.SkillID(s.ColumnInt(1)),
Skill2: horse.SkillID(s.ColumnInt(2)),
Skill3: horse.SkillID(s.ColumnInt(3)),
SkillBad: horse.SkillID(s.ColumnInt(4)),
}
})
skills := load(ctx1, loadgroup, db, "skills", skillSQL, func(s *sqlite.Stmt) horse.Skill {
return horse.Skill{
ID: horse.SkillID(s.ColumnInt(0)),
Name: s.ColumnText(1),
Description: s.ColumnText(2),
Group: horse.SkillGroupID(s.ColumnInt32(3)),
Rarity: int8(s.ColumnInt(5)),
GroupRate: int8(s.ColumnInt(6)),
GradeValue: s.ColumnInt32(7),
WitCheck: s.ColumnBool(8),
Activations: trimActivations([]horse.Activation{
{
Precondition: s.ColumnText(9),
Condition: s.ColumnText(10),
Duration: horse.TenThousandths(s.ColumnInt(11)),
DurScale: horse.DurScale(s.ColumnInt(12)),
Cooldown: horse.TenThousandths(s.ColumnInt(13)),
Abilities: trimAbilities([]horse.Ability{
{
Type: horse.AbilityType(s.ColumnInt(14)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(15)),
Value: horse.TenThousandths(s.ColumnInt(16)),
Target: horse.AbilityTarget(s.ColumnInt(17)),
TargetValue: s.ColumnInt32(18),
},
{
Type: horse.AbilityType(s.ColumnInt(19)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(20)),
Value: horse.TenThousandths(s.ColumnInt(21)),
Target: horse.AbilityTarget(s.ColumnInt(22)),
TargetValue: s.ColumnInt32(23),
},
{
Type: horse.AbilityType(s.ColumnInt(24)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(25)),
Value: horse.TenThousandths(s.ColumnInt(26)),
Target: horse.AbilityTarget(s.ColumnInt(27)),
TargetValue: s.ColumnInt32(28),
},
}),
},
{
Precondition: s.ColumnText(29),
Condition: s.ColumnText(30),
Duration: horse.TenThousandths(s.ColumnInt(31)),
DurScale: horse.DurScale(s.ColumnInt(32)),
Cooldown: horse.TenThousandths(s.ColumnInt(33)),
Abilities: trimAbilities([]horse.Ability{
{
Type: horse.AbilityType(s.ColumnInt(34)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(35)),
Value: horse.TenThousandths(s.ColumnInt(36)),
Target: horse.AbilityTarget(s.ColumnInt(37)),
TargetValue: s.ColumnInt32(38),
},
{
Type: horse.AbilityType(s.ColumnInt(39)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(40)),
Value: horse.TenThousandths(s.ColumnInt(41)),
Target: horse.AbilityTarget(s.ColumnInt(42)),
TargetValue: s.ColumnInt32(43),
},
{
Type: horse.AbilityType(s.ColumnInt(44)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(45)),
Value: horse.TenThousandths(s.ColumnInt(46)),
Target: horse.AbilityTarget(s.ColumnInt(47)),
TargetValue: s.ColumnInt32(48),
},
}),
},
}),
UniqueOwner: s.ColumnText(52), // TODO(zeph): should be id, not name
Tags: parseTags(s.ColumnText(54)),
SPCost: s.ColumnInt(49),
IconID: s.ColumnInt(53),
}
})
races := load(ctx1, loadgroup, db, "races", raceSQL, func(s *sqlite.Stmt) horse.Race {
return horse.Race{
ID: horse.RaceID(s.ColumnInt(0)),
Name: s.ColumnText(1),
// TODO(zeph): grade
Thumbnail: s.ColumnInt(3),
Primary: horse.RaceID(s.ColumnInt(4)),
}
})
saddles := load(ctx1, loadgroup, db, "saddles", saddleSQL, func(s *sqlite.Stmt) horse.Saddle {
return horse.Saddle{
ID: horse.SaddleID(s.ColumnInt(0)),
Name: s.ColumnText(1),
Races: trimZeros(
horse.RaceID(s.ColumnInt(2)),
horse.RaceID(s.ColumnInt(3)),
horse.RaceID(s.ColumnInt(4)),
),
Type: horse.SaddleType(s.ColumnInt(5)),
Primary: horse.SaddleID(s.ColumnInt(6)),
}
})
scenarios := load(ctx1, loadgroup, db, "scenarios", scenarioSQL, func(s *sqlite.Stmt) horse.Scenario {
return horse.Scenario{
ID: horse.ScenarioID(s.ColumnInt(0)),
Name: s.ColumnText(1),
Title: s.ColumnText(2),
}
})
sparks := load(ctx1, loadgroup, db, "sparks", sparkSQL, func(s *sqlite.Stmt) horse.Spark {
return horse.Spark{
ID: horse.SparkID(s.ColumnInt(0)),
Name: s.ColumnText(1),
Description: s.ColumnText(2),
Group: horse.SparkGroupID(s.ColumnInt(3)),
Rarity: horse.SparkRarity(s.ColumnInt(4)),
Type: horse.SparkType(s.ColumnInt(5)),
// Effects filled in later.
}
})
sparkeffs := load(ctx1, loadgroup, db, "spark effects", sparkEffectSQL, func(s *sqlite.Stmt) SparkEffImm {
return SparkEffImm{
Group: horse.SparkGroupID(s.ColumnInt(0)),
Effect: s.ColumnInt(1),
Target: horse.SparkTarget(s.ColumnInt(2)),
Value1: s.ColumnInt32(3),
Value2: s.ColumnInt32(4),
}
})
convos := load(ctx1, loadgroup, db, "lobby conversations", conversationSQL, func(s *sqlite.Stmt) horse.Conversation {
return horse.Conversation{
CharacterID: horse.CharacterID(s.ColumnInt(0)),
Number: s.ColumnInt(1),
Location: horse.LobbyConversationLocationID(s.ColumnInt(2)),
LocationName: horse.LobbyConversationLocationID(s.ColumnInt(2)).String(),
Chara1: horse.CharacterID(s.ColumnInt(3)),
Chara2: horse.CharacterID(s.ColumnInt(4)),
Chara3: horse.CharacterID(s.ColumnInt(5)),
ConditionType: s.ColumnInt(6),
}
})
if err := os.MkdirAll(filepath.Join(out, region), 0775); err != nil {
slog.Error("create output dir", slog.Any("err", err))
os.Exit(1)
}
writegroup, ctx2 := errgroup.WithContext(ctx)
writegroup.Go(func() error { return write(ctx2, out, region, "character.json", charas) })
writegroup.Go(func() error { return write(ctx2, out, region, "affinity.json", aff) })
writegroup.Go(func() error { return write(ctx2, out, region, "uma.json", umas) })
writegroup.Go(func() error { return write(ctx2, out, region, "skill-group.json", sg) })
writegroup.Go(func() error { return write(ctx2, out, region, "skill.json", skills) })
writegroup.Go(func() error { return write(ctx2, out, region, "race.json", races) })
writegroup.Go(func() error { return write(ctx2, out, region, "saddle.json", saddles) })
writegroup.Go(func() error { return write(ctx2, out, region, "scenario.json", scenarios) })
writegroup.Go(func() error { return write(ctx2, out, region, "spark.json", mergesparks(sparks, sparkeffs)) })
writegroup.Go(func() error { return write(ctx2, out, region, "conversation.json", convos) })
if err := writegroup.Wait(); err != nil {
slog.ErrorContext(ctx, "write", slog.Any("err", err))
os.Exit(1)
}
slog.InfoContext(ctx, "done")
}
var (
//go:embed sql/character.sql
characterSQL string
//go:embed sql/affinity.sql
affinitySQL string
//go:embed sql/uma.sql
umaSQL string
//go:embed sql/skill-group.sql
skillGroupSQL string
//go:embed sql/skill.sql
skillSQL string
//go:embed sql/race.sql
raceSQL string
//go:embed sql/saddle.sql
saddleSQL string
//go:embed sql/scenario.sql
scenarioSQL string
//go:embed sql/spark.sql
sparkSQL string
//go:embed sql/spark-effect.sql
sparkEffectSQL string
//go:embed sql/conversation.sql
conversationSQL string
)
func load[T any](ctx context.Context, group *errgroup.Group, db *sqlitex.Pool, kind, sql string, row func(*sqlite.Stmt) T) func() ([]T, error) {
slog.InfoContext(ctx, "load", slog.String("kind", kind))
var r []T
group.Go(func() error {
conn, err := db.Take(ctx)
defer db.Put(conn)
if err != nil {
return fmt.Errorf("couldn't get connection for %s: %w", kind, err)
}
stmt, _, err := conn.PrepareTransient(sql)
if err != nil {
return fmt.Errorf("couldn't prepare statement for %s: %w", kind, err)
}
for {
ok, err := stmt.Step()
if err != nil {
return fmt.Errorf("error stepping %s: %w", kind, err)
}
if !ok {
break
}
r = append(r, row(stmt))
}
return nil
})
return func() ([]T, error) {
err := group.Wait()
if err == context.Canceled {
// After the first wait, all future ones return context.Canceled.
// We want to be able to wait any number of times, so hide it.
err = nil
}
return r, err
}
}
func write[T any](ctx context.Context, out, region, name string, v func() (T, error)) error {
p := filepath.Join(out, region, name)
r, err := v()
if err != nil {
return err
}
slog.InfoContext(ctx, "write", slog.String("path", p))
f, err := os.Create(p)
if err != nil {
return err
}
defer f.Close()
w := bufio.NewWriter(f)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(r)
err = errors.Join(err, w.Flush())
slog.InfoContext(ctx, "marshaled", slog.String("path", p))
return err
}
func mergesparks(sparks func() ([]horse.Spark, error), effs func() ([]SparkEffImm, error)) func() ([]horse.Spark, error) {
return func() ([]horse.Spark, error) {
sp, err := sparks()
if err != nil {
return nil, err
}
ef, err := effs()
if err != nil {
return nil, err
}
// Spark effects are sorted by group ID, but groups apply to multiple
// sparks, and we don't rely on sparks and groups being in the same order.
// It is possible to merge in linear time, but not worth the effort:
// n log n is fine since this is an AOT step.
for i := range sp {
k, ok := slices.BinarySearchFunc(ef, sp[i].Group, func(e SparkEffImm, v horse.SparkGroupID) int { return cmp.Compare(e.Group, v) })
if !ok {
panic(fmt.Errorf("mergesparks: no spark group for %+v", &sp[i]))
}
// Back up to the first effect in the group.
for k > 0 && ef[k-1].Group == sp[i].Group {
k--
}
// Map effect IDs to the lists of their effects.
m := make(map[int][]horse.SparkEffect)
for _, e := range ef[k:] {
if e.Group != sp[i].Group {
// Done with this group.
break
}
m[e.Effect] = append(m[e.Effect], horse.SparkEffect{Target: e.Target, Value1: e.Value1, Value2: e.Value2})
}
// Now get effects in order.
keys := slices.Sorted(maps.Keys(m))
sp[i].Effects = make([][]horse.SparkEffect, 0, len(keys))
for _, key := range keys {
sp[i].Effects = append(sp[i].Effects, m[key])
}
}
return sp, nil
}
}
type SparkEffImm struct {
Group horse.SparkGroupID
Effect int
Target horse.SparkTarget
Value1 int32
Value2 int32
}
func parseTags(s string) []uint16 {
r := make([]uint16, 0, 8)
for s != "" {
t, u, _ := strings.Cut(s, "/")
s = u
v, err := strconv.ParseUint(t, 10, 16)
if err != nil {
panic(fmt.Errorf("parsing skill tags: %w", err))
}
r = append(r, uint16(v))
}
return trimZeros(r...)
}
func trimAbilities(s []horse.Ability) []horse.Ability {
for len(s) > 0 && s[len(s)-1].Type == 0 {
s = s[:len(s)-1]
}
return s
}
func trimActivations(s []horse.Activation) []horse.Activation {
for len(s) > 0 && s[len(s)-1].Condition == "" {
s = s[:len(s)-1]
}
return s
}
func trimZeros[T comparable](s ...T) []T {
var zero T
for len(s) > 0 && s[len(s)-1] == zero {
s = s[:len(s)-1]
}
return s
}
+57
View File
@@ -228,6 +228,63 @@ tag_id is a slash-separated list of numeric IDs that roughly describe all effect
- 614 base stat threshold passive
- 615 mood passive
unrelated, query to get all condition types:
```sql
WITH RECURSIVE condition AS (
SELECT DISTINCT precondition_1 AS c FROM skill_data
UNION
SELECT DISTINCT condition_1 AS c FROM skill_data
UNION
SELECT DISTINCT precondition_2 AS c FROM skill_data
UNION
SELECT DISTINCT condition_2 AS c FROM skill_data
), disj AS (
SELECT
IIF(INSTR(c, '@') != 0, SUBSTR(c, 1, INSTR(c, '@')-1), c) AS c,
IIF(INSTR(c, '@') != 0, SUBSTR(c, 1 + INSTR(c, '@')), '') AS rest
FROM condition
UNION
SELECT
IIF(INSTR(rest, '@') != 0, SUBSTR(rest, 1, INSTR(rest, '@')-1), rest) AS c,
IIF(INSTR(rest, '@') != 0, SUBSTR(rest, 1 + INSTR(rest, '@')), '') AS rest
FROM disj
WHERE rest != ''
), term AS (
SELECT
IIF(INSTR(c, '&') != 0, SUBSTR(c, 1, INSTR(c, '&')-1), c) AS term,
IIF(INSTR(c, '&') != 0, SUBSTR(c, 1 + INSTR(c, '&')), '') AS rest
FROM disj
UNION
SELECT
IIF(INSTR(rest, '&') != 0, SUBSTR(rest, 1, INSTR(rest, '&')-1), rest) AS term,
IIF(INSTR(rest, '&') != 0, SUBSTR(rest, 1 + INSTR(rest, '&')), '') AS rest
FROM term
WHERE rest != ''
), op AS (
SELECT
term,
CASE
WHEN INSTR(term, '==') != 0 THEN '=='
WHEN INSTR(term, '!=') != 0 THEN '!='
WHEN INSTR(term, '<=') != 0 THEN '<='
WHEN INSTR(term, '>=') != 0 THEN '>='
WHEN INSTR(term, '<') != 0 THEN '<'
WHEN INSTR(term, '>') != 0 THEN '>'
END AS op
FROM term WHERE term != ''
), type_op_arg AS (
SELECT DISTINCT
term,
SUBSTR(term, 1, INSTR(term, op) - 1) AS "type",
op,
CAST(SUBSTR(term, INSTR(term, op) + LENGTH(op)) AS INTEGER) AS arg
FROM op
)
SELECT DISTINCT CONCAT(type, op), arg FROM type_op_arg
```
final select can be adjusted to get different info, but type+op together seems most useful
# races
- group 1, grade: g1 100, g2 200, g3 300, op 400, pre-op 700
-189583
View File
File diff suppressed because it is too large Load Diff
-346
View File
@@ -1,346 +0,0 @@
[
{
"chara_id": 1001,
"name": "Special Week"
},
{
"chara_id": 1002,
"name": "Silence Suzuka"
},
{
"chara_id": 1003,
"name": "Tokai Teio"
},
{
"chara_id": 1004,
"name": "Maruzensky"
},
{
"chara_id": 1005,
"name": "Fuji Kiseki"
},
{
"chara_id": 1006,
"name": "Oguri Cap"
},
{
"chara_id": 1007,
"name": "Gold Ship"
},
{
"chara_id": 1008,
"name": "Vodka"
},
{
"chara_id": 1009,
"name": "Daiwa Scarlet"
},
{
"chara_id": 1010,
"name": "Taiki Shuttle"
},
{
"chara_id": 1011,
"name": "Grass Wonder"
},
{
"chara_id": 1012,
"name": "Hishi Amazon"
},
{
"chara_id": 1013,
"name": "Mejiro McQueen"
},
{
"chara_id": 1014,
"name": "El Condor Pasa"
},
{
"chara_id": 1015,
"name": "T.M. Opera O"
},
{
"chara_id": 1016,
"name": "Narita Brian"
},
{
"chara_id": 1017,
"name": "Symboli Rudolf"
},
{
"chara_id": 1018,
"name": "Air Groove"
},
{
"chara_id": 1019,
"name": "Agnes Digital"
},
{
"chara_id": 1020,
"name": "Seiun Sky"
},
{
"chara_id": 1021,
"name": "Tamamo Cross"
},
{
"chara_id": 1022,
"name": "Fine Motion"
},
{
"chara_id": 1023,
"name": "Biwa Hayahide"
},
{
"chara_id": 1024,
"name": "Mayano Top Gun"
},
{
"chara_id": 1025,
"name": "Manhattan Cafe"
},
{
"chara_id": 1026,
"name": "Mihono Bourbon"
},
{
"chara_id": 1027,
"name": "Mejiro Ryan"
},
{
"chara_id": 1028,
"name": "Hishi Akebono"
},
{
"chara_id": 1029,
"name": "Yukino Bijin"
},
{
"chara_id": 1030,
"name": "Rice Shower"
},
{
"chara_id": 1031,
"name": "Ines Fujin"
},
{
"chara_id": 1032,
"name": "Agnes Tachyon"
},
{
"chara_id": 1033,
"name": "Admire Vega"
},
{
"chara_id": 1034,
"name": "Inari One"
},
{
"chara_id": 1035,
"name": "Winning Ticket"
},
{
"chara_id": 1036,
"name": "Air Shakur"
},
{
"chara_id": 1037,
"name": "Eishin Flash"
},
{
"chara_id": 1038,
"name": "Curren Chan"
},
{
"chara_id": 1039,
"name": "Kawakami Princess"
},
{
"chara_id": 1040,
"name": "Gold City"
},
{
"chara_id": 1041,
"name": "Sakura Bakushin O"
},
{
"chara_id": 1042,
"name": "Seeking the Pearl"
},
{
"chara_id": 1043,
"name": "Shinko Windy"
},
{
"chara_id": 1044,
"name": "Sweep Tosho"
},
{
"chara_id": 1045,
"name": "Super Creek"
},
{
"chara_id": 1046,
"name": "Smart Falcon"
},
{
"chara_id": 1047,
"name": "Zenno Rob Roy"
},
{
"chara_id": 1048,
"name": "Tosen Jordan"
},
{
"chara_id": 1049,
"name": "Nakayama Festa"
},
{
"chara_id": 1050,
"name": "Narita Taishin"
},
{
"chara_id": 1051,
"name": "Nishino Flower"
},
{
"chara_id": 1052,
"name": "Haru Urara"
},
{
"chara_id": 1053,
"name": "Bamboo Memory"
},
{
"chara_id": 1054,
"name": "Biko Pegasus"
},
{
"chara_id": 1055,
"name": "Marvelous Sunday"
},
{
"chara_id": 1056,
"name": "Matikanefukukitaru"
},
{
"chara_id": 1057,
"name": "Mr. C.B."
},
{
"chara_id": 1058,
"name": "Meisho Doto"
},
{
"chara_id": 1059,
"name": "Mejiro Dober"
},
{
"chara_id": 1060,
"name": "Nice Nature"
},
{
"chara_id": 1061,
"name": "King Halo"
},
{
"chara_id": 1062,
"name": "Matikanetannhauser"
},
{
"chara_id": 1063,
"name": "Ikuno Dictus"
},
{
"chara_id": 1064,
"name": "Mejiro Palmer"
},
{
"chara_id": 1065,
"name": "Daitaku Helios"
},
{
"chara_id": 1066,
"name": "Twin Turbo"
},
{
"chara_id": 1067,
"name": "Satono Diamond"
},
{
"chara_id": 1068,
"name": "Kitasan Black"
},
{
"chara_id": 1069,
"name": "Sakura Chiyono O"
},
{
"chara_id": 1070,
"name": "Sirius Symboli"
},
{
"chara_id": 1071,
"name": "Mejiro Ardan"
},
{
"chara_id": 1072,
"name": "Yaeno Muteki"
},
{
"chara_id": 1073,
"name": "Tsurumaru Tsuyoshi"
},
{
"chara_id": 1074,
"name": "Mejiro Bright"
},
{
"chara_id": 1077,
"name": "Narita Top Road"
},
{
"chara_id": 1078,
"name": "Yamanin Zephyr"
},
{
"chara_id": 2001,
"name": "Happy Meek"
},
{
"chara_id": 2002,
"name": "Bitter Glasse"
},
{
"chara_id": 2003,
"name": "Little Cocon"
},
{
"chara_id": 2004,
"name": "Montjeu"
},
{
"chara_id": 9001,
"name": "Tazuna Hayakawa"
},
{
"chara_id": 9002,
"name": "Director Akikawa"
},
{
"chara_id": 9003,
"name": "Etsuko Otonashi"
},
{
"chara_id": 9004,
"name": "Trainer Kiryuin"
},
{
"chara_id": 9005,
"name": "Sasami Anshinzawa"
},
{
"chara_id": 9006,
"name": "Riko Kashimoto"
}
]
File diff suppressed because it is too large Load Diff
-1712
View File
File diff suppressed because it is too large Load Diff
-1420
View File
File diff suppressed because it is too large Load Diff
-17
View File
@@ -1,17 +0,0 @@
[
{
"scenario_id": 1,
"name": "URA Finale",
"title": "The Beginning: URA Finale"
},
{
"scenario_id": 2,
"name": "Unity Cup",
"title": "Unity Cup: Shine On, Team Spirit!"
},
{
"scenario_id": 4,
"name": "TS Climax",
"title": "Trackblazer: Start of the Climax"
}
]
File diff suppressed because it is too large Load Diff
-19511
View File
File diff suppressed because it is too large Load Diff
-38294
View File
File diff suppressed because it is too large Load Diff
-1994
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -5,8 +5,8 @@ go 1.25.5
require (
github.com/disgoorg/disgo v0.19.0-rc.15
github.com/go-chi/chi/v5 v5.2.5
github.com/google/go-cmp v0.6.0
github.com/junegunn/fzf v0.67.0
golang.org/x/sync v0.20.0
zombiezen.com/go/sqlite v1.4.2
)
@@ -25,6 +25,7 @@ require (
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.39.0 // indirect
modernc.org/libc v1.65.7 // indirect
modernc.org/mathutil v1.7.1 // indirect
+88
View File
@@ -0,0 +1,88 @@
package mdb
import (
"context"
_ "embed"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
"git.sunturtle.xyz/zephyr/horse"
)
var (
//go:embed sql/character.sql
characterSQL string
//go:embed sql/affinity.sql
affinitySQL string
//go:embed sql/uma.sql
umaSQL string
//go:embed sql/conversation.sql
conversationSQL string
)
// Characters retrieves all named characters.
func Characters(ctx context.Context, db *sqlitex.Pool) ([]horse.Character, error) {
return load(ctx, db, make([]horse.Character, 0, 128), characterSQL, func(s *sqlite.Stmt) horse.Character {
return horse.Character{
ID: horse.CharacterID(s.ColumnInt(0)),
Name: s.ColumnText(1),
}
})
}
// AffinitySummary gets precomputed base affinity for all pairs and trios of characters.
func AffinitySummary(ctx context.Context, db *sqlitex.Pool) ([]horse.AffinityRelation, error) {
return load(ctx, db, nil, affinitySQL, func(s *sqlite.Stmt) horse.AffinityRelation {
return horse.AffinityRelation{
IDA: s.ColumnInt(0),
IDB: s.ColumnInt(1),
IDC: s.ColumnInt(2),
Affinity: s.ColumnInt(3),
}
})
}
// Umas retrieves all trainable Uma variants.
func Umas(ctx context.Context, db *sqlitex.Pool) ([]horse.Uma, error) {
return load(ctx, db, make([]horse.Uma, 0, 128), umaSQL, func(s *sqlite.Stmt) horse.Uma {
return horse.Uma{
ID: horse.UmaID(s.ColumnInt(0)),
CharacterID: horse.CharacterID(s.ColumnInt(1)),
Name: s.ColumnText(2),
Variant: s.ColumnText(3),
Sprint: horse.AptitudeLevel(s.ColumnInt(5)),
Mile: horse.AptitudeLevel(s.ColumnInt(6)),
Medium: horse.AptitudeLevel(s.ColumnInt(7)),
Long: horse.AptitudeLevel(s.ColumnInt(8)),
Front: horse.AptitudeLevel(s.ColumnInt(9)),
Pace: horse.AptitudeLevel(s.ColumnInt(10)),
Late: horse.AptitudeLevel(s.ColumnInt(11)),
End: horse.AptitudeLevel(s.ColumnInt(12)),
Turf: horse.AptitudeLevel(s.ColumnInt(13)),
Dirt: horse.AptitudeLevel(s.ColumnInt(14)),
Unique: horse.SkillID(s.ColumnInt(15)),
Skill1: horse.SkillID(s.ColumnInt(16)),
Skill2: horse.SkillID(s.ColumnInt(17)),
Skill3: horse.SkillID(s.ColumnInt(18)),
SkillPL2: horse.SkillID(s.ColumnInt(19)),
SkillPL3: horse.SkillID(s.ColumnInt(20)),
SkillPL4: horse.SkillID(s.ColumnInt(21)),
SkillPL5: horse.SkillID(s.ColumnInt(22)),
}
})
}
func Conversations(ctx context.Context, db *sqlitex.Pool) ([]horse.Conversation, error) {
return load(ctx, db, make([]horse.Conversation, 0, 1024), conversationSQL, func(s *sqlite.Stmt) horse.Conversation {
return horse.Conversation{
CharacterID: horse.CharacterID(s.ColumnInt(0)),
Number: s.ColumnInt(1),
Location: horse.LobbyConversationLocationID(s.ColumnInt(2)),
Chara1: horse.CharacterID(s.ColumnInt(3)),
Chara2: horse.CharacterID(s.ColumnInt(4)),
Chara3: horse.CharacterID(s.ColumnInt(5)),
ConditionType: s.ColumnInt(6),
}
})
}
+224
View File
@@ -0,0 +1,224 @@
package mdb_test
import (
_ "embed"
"testing"
"git.sunturtle.xyz/zephyr/horse"
"git.sunturtle.xyz/zephyr/horse/mdb"
"github.com/google/go-cmp/cmp"
)
//go:embed testdata/character.sql
var characterSQL string
func TestCharacters(t *testing.T) {
db := testdb(t.Context(), "file:TestCharacters?mode=memory&cache=shared", characterSQL)
got, err := mdb.Characters(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.Character{
{ID: 1001, Name: "Special Week"},
{ID: 1002, Name: "Silence Suzuka"},
{ID: 1078, Name: "Yamanin Zephyr"},
{ID: 2001, Name: "Happy Meek"},
{ID: 9001, Name: "Tazuna Hayakawa"},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong characters (+got/-want):\n%s", diff)
}
}
func TestAffinitySummary(t *testing.T) {
db := testdb(t.Context(), "file:TestAffinitySummary?mode=memory&cache=shared", characterSQL)
got, err := mdb.AffinitySummary(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.AffinityRelation{
{IDA: 1001, IDB: 1002, Affinity: 23},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong affinity (+got/-want):\n%s", diff)
}
}
func TestUmas(t *testing.T) {
db := testdb(t.Context(), "file:TestUmas?mode=memory&cache=shared", characterSQL)
got, err := mdb.Umas(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.Uma{
{
ID: 100101,
CharacterID: 1001,
Name: `[Special Dreamer] Special Week`,
Variant: `[Special Dreamer]`,
Sprint: 2,
Mile: 5,
Medium: 7,
Long: 7,
Front: 1,
Pace: 7,
Late: 7,
End: 5,
Turf: 7,
Dirt: 1,
Unique: 100011,
Skill1: 200512,
Skill2: 201352,
Skill3: 200732,
SkillPL2: 200162,
SkillPL3: 201351,
SkillPL4: 200612,
SkillPL5: 200511,
},
{
ID: 100102,
CharacterID: 1001,
Name: `[Hopp'n♪Happy Heart] Special Week`,
Variant: `[Hopp'n♪Happy Heart]`,
Sprint: 2,
Mile: 5,
Medium: 7,
Long: 7,
Front: 1,
Pace: 7,
Late: 7,
End: 5,
Turf: 7,
Dirt: 1,
Unique: 110011,
Skill1: 200462,
Skill2: 200592,
Skill3: 201132,
SkillPL2: 200212,
SkillPL3: 200591,
SkillPL4: 201611,
SkillPL5: 200461,
},
{
ID: 100201,
CharacterID: 1002,
Name: `[Innocent Silence] Silence Suzuka`,
Variant: `[Innocent Silence]`,
Sprint: 4,
Mile: 7,
Medium: 7,
Long: 3,
Front: 7,
Pace: 5,
Late: 3,
End: 1,
Turf: 7,
Dirt: 1,
Unique: 100021,
Skill1: 200432,
Skill2: 200552,
Skill3: 200712,
SkillPL2: 200022,
SkillPL3: 200431,
SkillPL4: 200542,
SkillPL5: 200551,
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong umas (+got/-want):\n%s", diff)
}
}
func TestConversations(t *testing.T) {
db := testdb(t.Context(), "file:TestConversations?mode=memory&cache=shared", characterSQL)
got, err := mdb.Conversations(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.Conversation{
{
CharacterID: 1001,
Number: 1,
Location: 410,
Chara1: 1001,
},
{
CharacterID: 1001,
Number: 2,
Location: 510,
Chara1: 1001,
ConditionType: 1,
},
{
CharacterID: 1001,
Number: 3,
Location: 310,
Chara1: 1001,
ConditionType: 1,
},
{
CharacterID: 1001,
Number: 4,
Location: 120,
Chara1: 1001,
Chara2: 1002,
ConditionType: 2,
},
{
CharacterID: 1001,
Number: 5,
Location: 520,
Chara1: 1003,
Chara2: 1001,
ConditionType: 3,
},
{
CharacterID: 1001,
Number: 6,
Location: 430,
Chara1: 1001,
Chara2: 1014,
Chara3: 1011,
ConditionType: 1,
},
{
CharacterID: 1002,
Number: 1,
Location: 310,
Chara1: 1002,
},
{
CharacterID: 1002,
Number: 2,
Location: 210,
Chara1: 1002,
ConditionType: 1,
},
{
CharacterID: 1002,
Number: 3,
Location: 110,
Chara1: 1002,
ConditionType: 1,
},
{
CharacterID: 1002,
Number: 4,
Location: 520,
Chara1: 1010,
Chara2: 1002,
ConditionType: 3,
},
{
CharacterID: 1002,
Number: 5,
Location: 220,
Chara1: 1002,
Chara2: 1018,
ConditionType: 2,
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong conversations (+got/-want):\n%s", diff)
}
}
+33
View File
@@ -0,0 +1,33 @@
package mdb
import (
"context"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
)
// load scans all results of sql and appends them to r.
func load[T any](ctx context.Context, db *sqlitex.Pool, r []T, sql string, row func(*sqlite.Stmt) T) ([]T, error) {
conn, err := db.Take(ctx)
defer db.Put(conn)
if err != nil {
return nil, err
}
stmt, err := conn.Prepare(sql)
if err != nil {
return nil, err
}
for {
ok, err := stmt.Step()
if err != nil {
return r, err
}
if !ok {
break
}
r = append(r, row(stmt))
}
return r, err
}
+23
View File
@@ -0,0 +1,23 @@
package mdb_test
import (
"context"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
)
func testdb(ctx context.Context, uri, schema string) *sqlitex.Pool {
db, err := sqlitex.NewPool(uri, sqlitex.PoolOptions{Flags: sqlite.OpenCreate | sqlite.OpenReadWrite | sqlite.OpenMemory | sqlite.OpenSharedCache | sqlite.OpenURI})
if err != nil {
panic(err)
}
conn, err := db.Take(ctx)
if err != nil {
panic(err)
}
if err := sqlitex.ExecScript(conn, schema); err != nil {
panic(err)
}
return db
}
+61
View File
@@ -0,0 +1,61 @@
package mdb
import (
"context"
_ "embed"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
"git.sunturtle.xyz/zephyr/horse"
)
var (
//go:embed sql/race.sql
raceSQL string
//go:embed sql/saddle.sql
saddleSQL string
//go:embed sql/scenario.sql
scenarioSQL string
)
// Races retrieves all races.
func Races(ctx context.Context, db *sqlitex.Pool) ([]horse.Race, error) {
return load(ctx, db, nil, raceSQL, func(s *sqlite.Stmt) horse.Race {
return horse.Race{
ID: horse.RaceID(s.ColumnInt(0)),
Name: s.ColumnText(1),
// TODO(zeph): grade
Thumbnail: s.ColumnInt(3),
Primary: horse.RaceID(s.ColumnInt(4)),
}
})
}
// Saddles retrieves all saddles.
func Saddles(ctx context.Context, db *sqlitex.Pool) ([]horse.Saddle, error) {
return load(ctx, db, nil, saddleSQL, func(s *sqlite.Stmt) horse.Saddle {
return horse.Saddle{
ID: horse.SaddleID(s.ColumnInt(0)),
Name: s.ColumnText(1),
Races: trimZeros(
horse.RaceID(s.ColumnInt(2)),
horse.RaceID(s.ColumnInt(3)),
horse.RaceID(s.ColumnInt(4)),
),
Type: horse.SaddleType(s.ColumnInt(5)),
Primary: horse.SaddleID(s.ColumnInt(6)),
}
})
}
// Scenarios retrieves all scenarios.
func Scenarios(ctx context.Context, db *sqlitex.Pool) ([]horse.Scenario, error) {
return load(ctx, db, nil, scenarioSQL, func(s *sqlite.Stmt) horse.Scenario {
return horse.Scenario{
ID: horse.ScenarioID(s.ColumnInt(0)),
Name: s.ColumnText(1),
Title: s.ColumnText(2),
}
})
}
+230
View File
@@ -0,0 +1,230 @@
package mdb_test
import (
_ "embed"
"testing"
"git.sunturtle.xyz/zephyr/horse"
"git.sunturtle.xyz/zephyr/horse/mdb"
"github.com/google/go-cmp/cmp"
)
//go:embed testdata/race.sql
var raceSQL string
func TestRaces(t *testing.T) {
db := testdb(t.Context(), "file:TestRaces?mode=memory&cache=shared", raceSQL)
got, err := mdb.Races(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.Race{
{
ID: 1005,
Name: "Satsuki Sho",
Thumbnail: 1005,
Primary: 1005,
},
{
ID: 1010,
Name: "Tokyo Yushun (Japanese Derby)",
Thumbnail: 1010,
Primary: 1010,
},
{
ID: 1015,
Name: "Kikuka Sho",
Thumbnail: 1015,
Primary: 1015,
},
{
ID: 1026,
Name: "Kikuka Sho",
Thumbnail: 1015,
Primary: 1015,
},
{
ID: 1028,
Name: "Satsuki Sho",
Thumbnail: 1028,
Primary: 1005,
},
{
ID: 1101,
Name: "Teio Sho",
Thumbnail: 1101,
Primary: 1101,
},
{
ID: 2001,
Name: "Nikkei Shinshun Hai",
Thumbnail: 2001,
Primary: 2001,
},
{
ID: 2010,
Name: "Spring Stakes",
Thumbnail: 2010,
Primary: 2010,
},
{
ID: 2035,
Name: "Spring Stakes",
Thumbnail: 2010,
Primary: 2010,
},
{
ID: 3001,
Name: "Kyoto Kimpai",
Thumbnail: 3001,
Primary: 3001,
},
{
ID: 4001,
Name: "Manyo Stakes",
Thumbnail: 4001,
Primary: 4001,
},
{
ID: 4501,
Name: "Aster Sho",
Thumbnail: 4501,
Primary: 4501,
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong races (+got/-want):\n%s", diff)
}
}
func TestSaddles(t *testing.T) {
db := testdb(t.Context(), "file:TestSaddles?mode=memory&cache=shared", raceSQL)
got, err := mdb.Saddles(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.Saddle{
{
ID: 1,
Name: "Classic Triple Crown",
Races: []horse.RaceID{100501, 101001, 101501},
Type: 0,
Primary: 1,
},
{
ID: 12,
Name: "Japanese Derby",
Races: []horse.RaceID{101001},
Type: 3,
Primary: 12,
},
{
ID: 16,
Name: "Kikuka Sho",
Races: []horse.RaceID{101501},
Type: 3,
Primary: 16,
},
{
ID: 18,
Name: "Satsuki Sho",
Races: []horse.RaceID{100501},
Type: 3,
Primary: 18,
},
{
ID: 36,
Name: "Teio Sho",
Races: []horse.RaceID{110101},
Type: 3,
Primary: 36,
},
{
ID: 40,
Name: "Nikkei Shinshun Hai",
Races: []horse.RaceID{200101},
Type: 2,
Primary: 40,
},
{
ID: 49,
Name: "Spring S.",
Races: []horse.RaceID{201001},
Type: 2,
Primary: 49,
},
{
ID: 74,
Name: "Kyoto Kimpai",
Races: []horse.RaceID{300101},
Type: 1,
Primary: 74,
},
{
ID: 144,
Name: "Classic Triple Crown",
Races: []horse.RaceID{100501, 101001, 102601},
Type: 0,
Primary: 1,
},
{
ID: 148,
Name: "Kikuka Sho",
Races: []horse.RaceID{102601},
Type: 3,
Primary: 16,
},
{
ID: 149,
Name: "Spring S.",
Races: []horse.RaceID{203501},
Type: 2,
Primary: 49,
},
{
ID: 154,
Name: "Classic Triple Crown",
Races: []horse.RaceID{102801, 101001, 101501},
Type: 0,
Primary: 1,
},
{
ID: 155,
Name: "Satsuki Sho",
Races: []horse.RaceID{102801},
Type: 3,
Primary: 18,
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong saddles (+got/-want):\n%s", diff)
}
}
func TestScenarios(t *testing.T) {
db := testdb(t.Context(), "file:TestScenarios?mode=memory&cache=shared", raceSQL)
got, err := mdb.Scenarios(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.Scenario{
{
ID: 1,
Name: "URA Finale",
Title: "The Beginning: URA Finale",
},
{
ID: 2,
Name: "Unity Cup",
Title: "Unity Cup: Shine On, Team Spirit!",
},
{
ID: 4,
Name: "TS Climax",
Title: "Trackblazer: Start of the Climax",
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong scenarios (+got/-want):\n%s", diff)
}
}
+152
View File
@@ -0,0 +1,152 @@
package mdb
import (
"context"
_ "embed"
"fmt"
"strconv"
"strings"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
"git.sunturtle.xyz/zephyr/horse"
)
var (
//go:embed sql/skill-group.sql
skillGroupSQL string
//go:embed sql/skill.sql
skillSQL string
)
// SkillGroups retrieves all skill groups.
func SkillGroups(ctx context.Context, db *sqlitex.Pool) ([]horse.SkillGroup, error) {
return load(ctx, db, nil, skillGroupSQL, func(s *sqlite.Stmt) horse.SkillGroup {
return horse.SkillGroup{
ID: horse.SkillGroupID(s.ColumnInt(0)),
Skill1: horse.SkillID(s.ColumnInt(1)),
Skill2: horse.SkillID(s.ColumnInt(2)),
Skill3: horse.SkillID(s.ColumnInt(3)),
SkillBad: horse.SkillID(s.ColumnInt(4)),
}
})
}
// Skills retrieves all skills.
func Skills(ctx context.Context, db *sqlitex.Pool) ([]horse.Skill, error) {
return load(ctx, db, nil, skillSQL, func(s *sqlite.Stmt) horse.Skill {
return horse.Skill{
ID: horse.SkillID(s.ColumnInt(0)),
Name: s.ColumnText(1),
Description: s.ColumnText(2),
Group: horse.SkillGroupID(s.ColumnInt32(3)),
Rarity: int8(s.ColumnInt(5)),
GroupRate: int8(s.ColumnInt(6)),
GradeValue: s.ColumnInt32(7),
WitCheck: s.ColumnBool(8),
Activations: trimActivations([]horse.Activation{
{
Precondition: s.ColumnText(9),
Condition: s.ColumnText(10),
Duration: horse.TenThousandths(s.ColumnInt(11)),
DurScale: horse.DurScale(s.ColumnInt(12)),
Cooldown: horse.TenThousandths(s.ColumnInt(13)),
Abilities: trimAbilities([]horse.Ability{
{
Type: horse.AbilityType(s.ColumnInt(14)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(15)),
Value: horse.TenThousandths(s.ColumnInt(16)),
Target: horse.AbilityTarget(s.ColumnInt(17)),
TargetValue: s.ColumnInt32(18),
},
{
Type: horse.AbilityType(s.ColumnInt(19)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(20)),
Value: horse.TenThousandths(s.ColumnInt(21)),
Target: horse.AbilityTarget(s.ColumnInt(22)),
TargetValue: s.ColumnInt32(23),
},
{
Type: horse.AbilityType(s.ColumnInt(24)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(25)),
Value: horse.TenThousandths(s.ColumnInt(26)),
Target: horse.AbilityTarget(s.ColumnInt(27)),
TargetValue: s.ColumnInt32(28),
},
}),
},
{
Precondition: s.ColumnText(29),
Condition: s.ColumnText(30),
Duration: horse.TenThousandths(s.ColumnInt(31)),
DurScale: horse.DurScale(s.ColumnInt(32)),
Cooldown: horse.TenThousandths(s.ColumnInt(33)),
Abilities: trimAbilities([]horse.Ability{
{
Type: horse.AbilityType(s.ColumnInt(34)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(35)),
Value: horse.TenThousandths(s.ColumnInt(36)),
Target: horse.AbilityTarget(s.ColumnInt(37)),
TargetValue: s.ColumnInt32(38),
},
{
Type: horse.AbilityType(s.ColumnInt(39)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(40)),
Value: horse.TenThousandths(s.ColumnInt(41)),
Target: horse.AbilityTarget(s.ColumnInt(42)),
TargetValue: s.ColumnInt32(43),
},
{
Type: horse.AbilityType(s.ColumnInt(44)),
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(45)),
Value: horse.TenThousandths(s.ColumnInt(46)),
Target: horse.AbilityTarget(s.ColumnInt(47)),
TargetValue: s.ColumnInt32(48),
},
}),
},
}),
UniqueOwner: s.ColumnText(52), // TODO(zeph): should be id, not name
Tags: parseTags(s.ColumnText(54)),
SPCost: s.ColumnInt(49),
IconID: s.ColumnInt(53),
}
})
}
func parseTags(s string) []uint16 {
r := make([]uint16, 0, 8)
for s != "" {
t, u, _ := strings.Cut(s, "/")
s = u
v, err := strconv.ParseUint(t, 10, 16)
if err != nil {
panic(fmt.Errorf("parsing skill tags: %w", err))
}
r = append(r, uint16(v))
}
return trimZeros(r...)
}
func trimAbilities(s []horse.Ability) []horse.Ability {
for len(s) > 0 && s[len(s)-1].Type == 0 {
s = s[:len(s)-1]
}
return s
}
func trimActivations(s []horse.Activation) []horse.Activation {
for len(s) > 0 && s[len(s)-1].Condition == "" {
s = s[:len(s)-1]
}
return s
}
func trimZeros[T comparable](s ...T) []T {
var zero T
for len(s) > 0 && s[len(s)-1] == zero {
s = s[:len(s)-1]
}
return s
}
+701
View File
@@ -0,0 +1,701 @@
package mdb_test
import (
_ "embed"
"testing"
"git.sunturtle.xyz/zephyr/horse"
"git.sunturtle.xyz/zephyr/horse/mdb"
"github.com/google/go-cmp/cmp"
)
//go:embed testdata/skill.sql
var skillSQL string
func TestSkillGroups(t *testing.T) {
db := testdb(t.Context(), "file:TestSkillGroups?mode=memory&cache=shared", skillSQL)
got, err := mdb.SkillGroups(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.SkillGroup{
{ID: 1035, Skill1: 10351},
{ID: 10001, Skill1: 100011, Skill2: 900011},
{ID: 10035, Skill1: 100351, Skill2: 900351},
{ID: 11024, Skill1: 110241, Skill2: 910241},
{ID: 20001, Skill1: 200012, Skill2: 200011, Skill3: 200014, SkillBad: 200013},
{ID: 20002, Skill1: 200022, Skill2: 200021, SkillBad: 200023},
{ID: 20036, Skill1: 200362, Skill2: 200361},
{ID: 20083, Skill1: 200831},
{ID: 20180, SkillBad: 201801},
{ID: 30001, Skill1: 300011},
{ID: 90001, Skill1: 100011, Skill2: 900011},
{ID: 90035, Skill1: 100351, Skill2: 900351},
{ID: 91024, Skill1: 110241, Skill2: 910241},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong skill groups (+got/-want):\n%s", diff)
}
}
func TestSkills(t *testing.T) {
db := testdb(t.Context(), "file:TestSkills?mode=memory&cache=shared", skillSQL)
got, err := mdb.Skills(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.Skill{
{
ID: 10351,
Name: "V Is for Victory!",
Description: "Moderately increase velocity with winning ambition when positioned toward the front on the final straight after engaging in a challenge on the final corner or later.",
Group: 1035,
Rarity: 3,
GroupRate: 1,
GradeValue: 240,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "is_finalcorner==1&blocked_side_continuetime>=2",
Condition: "is_finalcorner==1&corner==0&order<=5",
Duration: 50000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 2500,
Target: 1,
TargetValue: 0,
},
},
},
},
UniqueOwner: "[Get to Winning!] Winning Ticket",
Tags: []uint16{401},
SPCost: 0,
IconID: 20013,
},
{
ID: 100011,
Name: "Shooting Star",
Description: "Ride the momentum to increase velocity and very slightly increase acceleration after passing another runner toward the front late-race.",
Group: 10001,
Rarity: 5,
GroupRate: 1,
GradeValue: 340,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "phase>=2&order>=1&order_rate<=50&change_order_onetime<0",
Duration: 50000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 3500,
Target: 1,
TargetValue: 0,
},
{
Type: 31,
ValueUsage: 1,
Value: 1000,
Target: 1,
TargetValue: 0,
},
},
},
},
UniqueOwner: "[Special Dreamer] Special Week",
Tags: []uint16{401, 403},
SPCost: 0,
IconID: 20013,
},
{
ID: 100351,
Name: "Our Ticket to Win!",
Description: "Increase velocity with winning ambition when positioned toward the front on the final straight after engaging in a challenge on the final corner or later.",
Group: 10035,
Rarity: 4,
GroupRate: 1,
GradeValue: 340,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "is_finalcorner==1&blocked_side_continuetime>=2",
Condition: "is_finalcorner==1&corner==0&order<=5",
Duration: 50000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 3500,
Target: 1,
TargetValue: 0,
},
},
},
},
UniqueOwner: "[Get to Winning!] Winning Ticket",
Tags: []uint16{401},
SPCost: 0,
IconID: 20013,
},
{
ID: 110241,
Name: "Flowery☆Maneuver",
Description: "Increase velocity when passing another runner toward the front on the final corner. If passing toward the back, increase acceleration instead.",
Group: 11024,
Rarity: 5,
GroupRate: 1,
GradeValue: 340,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "is_finalcorner==1&corner!=0&order_rate<=40&change_order_onetime<0",
Duration: 50000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 3500,
Target: 1,
TargetValue: 0,
},
},
},
{
Precondition: "",
Condition: "is_finalcorner==1&corner!=0&order_rate>=50&order_rate<=80&change_order_onetime<0",
Duration: 40000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 31,
ValueUsage: 1,
Value: 4000,
Target: 1,
TargetValue: 0,
},
},
},
},
UniqueOwner: "[Sunlight Bouquet] Mayano Top Gun",
Tags: []uint16{401, 403},
SPCost: 0,
IconID: 20013,
},
{
ID: 200011,
Name: "Right-Handed ◎",
Description: "Increase performance on right-handed tracks.",
Group: 20001,
Rarity: 1,
GroupRate: 2,
GradeValue: 174,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "rotation==1",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 1,
ValueUsage: 1,
Value: 600000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 110,
IconID: 10011,
},
{
ID: 200012,
Name: "Right-Handed ○",
Description: "Moderately increase performance on right-handed tracks.",
Group: 20001,
Rarity: 1,
GroupRate: 1,
GradeValue: 129,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "rotation==1",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 1,
ValueUsage: 1,
Value: 400000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 90,
IconID: 10011,
},
{
ID: 200013,
Name: "Right-Handed ×",
Description: "Moderately decrease performance on right-handed tracks.",
Group: 20001,
Rarity: 1,
GroupRate: -1,
GradeValue: -129,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "rotation==1",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 1,
ValueUsage: 1,
Value: -400000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 50,
IconID: 10014,
},
{
ID: 200014,
Name: "Right-Handed Demon",
Description: "Increase proficiency in right-handed tracks, increasing Speed and Power.",
Group: 20001,
Rarity: 2,
GroupRate: 3,
GradeValue: 461,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "rotation==1",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 1,
ValueUsage: 1,
Value: 600000,
Target: 1,
TargetValue: 0,
},
{
Type: 3,
ValueUsage: 1,
Value: 600000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401, 403},
SPCost: 130,
IconID: 10012,
},
{
ID: 200021,
Name: "Left-Handed ◎",
Description: "Increase performance on left-handed tracks.",
Group: 20002,
Rarity: 1,
GroupRate: 2,
GradeValue: 174,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "rotation==2",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 1,
ValueUsage: 1,
Value: 600000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 110,
IconID: 10011,
},
{
ID: 200022,
Name: "Left-Handed ○",
Description: "Moderately increase performance on left-handed tracks.",
Group: 20002,
Rarity: 1,
GroupRate: 1,
GradeValue: 129,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "rotation==2",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 1,
ValueUsage: 1,
Value: 400000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 90,
IconID: 10011,
},
{
ID: 200023,
Name: "Left-Handed ×",
Description: "Moderately decrease performance on left-handed tracks.",
Group: 20002,
Rarity: 1,
GroupRate: -1,
GradeValue: -129,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "rotation==2",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 1,
ValueUsage: 1,
Value: -400000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 50,
IconID: 10014,
},
{
ID: 200361,
Name: "Beeline Burst",
Description: "Increase velocity on a straight.",
Group: 20036,
Rarity: 2,
GroupRate: 2,
GradeValue: 508,
WitCheck: true,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "straight_random==1",
Duration: 24000,
DurScale: 1,
Cooldown: 300000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 3500,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 170,
IconID: 20012,
},
{
ID: 200362,
Name: "Straightaway Adept",
Description: "Slightly increase velocity on a straight.",
Group: 20036,
Rarity: 1,
GroupRate: 1,
GradeValue: 217,
WitCheck: true,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "straight_random==1",
Duration: 24000,
DurScale: 1,
Cooldown: 300000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 1500,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 170,
IconID: 20011,
},
{
ID: 200831,
Name: "Subdued Front Runners",
Description: "Slightly increase fatigue for front runners early-race.",
Group: 20083,
Rarity: 1,
GroupRate: 1,
GradeValue: 217,
WitCheck: true,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "running_style_count_nige_otherself>=1&phase_random==0&accumulatetime>=5",
Duration: 0,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 9,
ValueUsage: 1,
Value: -100,
Target: 18,
TargetValue: 1,
},
},
},
},
Tags: []uint16{301, 406},
SPCost: 130,
IconID: 30051,
},
{
ID: 201801,
Name: "♡ 3D Nail Art",
Description: "Moderately decrease performance on firm ground.",
Group: 20180,
Rarity: 1,
GroupRate: -1,
GradeValue: -129,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "ground_condition==1",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 1,
ValueUsage: 1,
Value: -400000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{401},
SPCost: 50,
IconID: 10014,
},
{
ID: 300011,
Name: "Unquenched Thirst",
Description: "Moderately increase performance with the desire to race.",
Group: 30001,
Rarity: 1,
GroupRate: 1,
GradeValue: 0,
WitCheck: false,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "track_id==10008",
Duration: -1,
DurScale: 1,
Cooldown: 0,
Abilities: []horse.Ability{
{
Type: 2,
ValueUsage: 1,
Value: 400000,
Target: 1,
TargetValue: 0,
},
},
},
},
Tags: []uint16{402},
SPCost: 0,
IconID: 10021,
},
{
ID: 900011,
Name: "Shooting Star",
Description: "Slightly increase velocity and very minimally increase acceleration after passing another runner toward the front late-race.",
Group: 10001,
Rarity: 1,
GroupRate: 2,
GradeValue: 180,
WitCheck: true,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "phase>=2&order>=1&order_rate<=50&change_order_onetime<0",
Duration: 30000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 1500,
Target: 1,
TargetValue: 0,
},
{
Type: 31,
ValueUsage: 1,
Value: 500,
Target: 1,
TargetValue: 0,
},
},
},
},
UniqueOwner: "[Special Dreamer] Special Week",
Tags: []uint16{401, 403},
SPCost: 200,
IconID: 20011,
},
{
ID: 900351,
Name: "Our Ticket to Win!",
Description: "Slightly increase velocity when positioned toward the front on the final straight after engaging in a challenge on the final corner or later.",
Group: 10035,
Rarity: 1,
GroupRate: 2,
GradeValue: 180,
WitCheck: true,
Activations: []horse.Activation{
{
Precondition: "is_finalcorner==1&blocked_side_continuetime>=2",
Condition: "is_finalcorner==1&corner==0&order<=5",
Duration: 30000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 1500,
Target: 1,
TargetValue: 0,
},
},
},
},
UniqueOwner: "[Get to Winning!] Winning Ticket",
Tags: []uint16{401},
SPCost: 200,
IconID: 20011,
},
{
ID: 910241,
Name: "Flowery☆Maneuver",
Description: "Slightly increase velocity when passing another runner toward the front on the final corner. If passing toward the back, slightly increase acceleration instead.",
Group: 11024,
Rarity: 1,
GroupRate: 2,
GradeValue: 180,
WitCheck: true,
Activations: []horse.Activation{
{
Precondition: "",
Condition: "is_finalcorner==1&corner!=0&order_rate<=40&change_order_onetime<0",
Duration: 30000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 27,
ValueUsage: 1,
Value: 1500,
Target: 1,
TargetValue: 0,
},
},
},
{
Precondition: "",
Condition: "is_finalcorner==1&corner!=0&order_rate>=50&order_rate<=80&change_order_onetime<0",
Duration: 24000,
DurScale: 1,
Cooldown: 5000000,
Abilities: []horse.Ability{
{
Type: 31,
ValueUsage: 1,
Value: 2000,
Target: 1,
TargetValue: 0,
},
},
},
},
UniqueOwner: "[Sunlight Bouquet] Mayano Top Gun",
Tags: []uint16{401, 403},
SPCost: 200,
IconID: 20011,
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong skills (+got/-want):\n%s", diff)
}
}
+95
View File
@@ -0,0 +1,95 @@
package mdb
import (
"context"
_ "embed"
"zombiezen.com/go/sqlite/sqlitex"
"git.sunturtle.xyz/zephyr/horse"
)
var (
//go:embed sql/spark.sql
sparkSQL string
//go:embed sql/spark-effect.sql
sparkEffectSQL string
)
func Sparks(ctx context.Context, db *sqlitex.Pool) ([]horse.Spark, error) {
// We don't use func load here because we have multiple queries to run.
conn, err := db.Take(ctx)
defer db.Put(conn)
if err != nil {
return nil, err
}
var sp []horse.Spark
{
stmt := conn.Prep(sparkSQL)
defer stmt.Reset()
for {
ok, err := stmt.Step()
if err != nil {
return nil, err
}
if !ok {
break
}
sp = append(sp, horse.Spark{
ID: horse.SparkID(stmt.ColumnInt(0)),
Name: stmt.ColumnText(1),
Description: stmt.ColumnText(2),
Group: horse.SparkGroupID(stmt.ColumnInt(3)),
Rarity: horse.SparkRarity(stmt.ColumnInt(4)),
Type: horse.SparkType(stmt.ColumnInt(5)),
// Effects filled in later, but we can start with space.
// The vast majority of sparks are skill sparks,
// which have five rolls.
Effects: make([][]horse.SparkEffect, 0, 5),
})
}
}
stmt := conn.Prep(sparkEffectSQL)
defer stmt.Reset()
cur := sp
last := 0
for {
ok, err := stmt.Step()
if err != nil {
return nil, err
}
if !ok {
break
}
// We sort sparks by group ID first, so we can add effects in a single pass.
group := stmt.ColumnInt(0)
for len(cur) > 0 && cur[0].Group != horse.SparkGroupID(group) {
cur = cur[1:]
last = 0 // reset whenever we change group IDs
}
effect := stmt.ColumnInt(1)
if effect != last {
// This effect is a separate roll from the previous one.
// Create a new slot for the effects to go into.
for i := range cur {
if cur[i].Group != horse.SparkGroupID(group) {
break
}
cur[i].Effects = append(cur[i].Effects, make([]horse.SparkEffect, 0, 1))
}
last = effect
}
for i := range cur {
if cur[i].Group != horse.SparkGroupID(group) {
break
}
e := horse.SparkEffect{
Target: horse.SparkTarget(stmt.ColumnInt(2)),
Value1: stmt.ColumnInt32(3),
Value2: stmt.ColumnInt32(4),
}
cur[i].Effects[len(cur[i].Effects)-1] = append(cur[i].Effects[len(cur[i].Effects)-1], e)
}
}
return sp, nil
}
+345
View File
@@ -0,0 +1,345 @@
package mdb_test
import (
_ "embed"
"testing"
"github.com/google/go-cmp/cmp"
"git.sunturtle.xyz/zephyr/horse"
"git.sunturtle.xyz/zephyr/horse/mdb"
)
//go:embed testdata/spark.sql
var sparkSQL string
func TestSparks(t *testing.T) {
db := testdb(t.Context(), "file:TestSparks?mode=memory&cache=shared", sparkSQL)
got, err := mdb.Sparks(t.Context(), db)
if err != nil {
t.Error(err)
}
want := []horse.Spark{
{
ID: 101,
Name: "Speed",
Description: "A Spark that increases Speed.",
Group: 1,
Rarity: 1,
Type: 1,
Effects: [][]horse.SparkEffect{
{{Target: 1, Value1: 1}},
{{Target: 1, Value1: 4}},
{{Target: 1, Value1: 7}},
{{Target: 1, Value1: 10}},
{{Target: 1, Value1: 13}},
{{Target: 1, Value1: 16}},
{{Target: 1, Value1: 19}},
{{Target: 1, Value1: 22}},
{{Target: 1, Value1: 25}},
{{Target: 1, Value1: 28}},
},
},
{
ID: 102,
Name: "Speed",
Description: "A Spark that increases Speed.",
Group: 1,
Rarity: 2,
Type: 1,
Effects: [][]horse.SparkEffect{
{{Target: 1, Value1: 1}},
{{Target: 1, Value1: 4}},
{{Target: 1, Value1: 7}},
{{Target: 1, Value1: 10}},
{{Target: 1, Value1: 13}},
{{Target: 1, Value1: 16}},
{{Target: 1, Value1: 19}},
{{Target: 1, Value1: 22}},
{{Target: 1, Value1: 25}},
{{Target: 1, Value1: 28}},
},
},
{
ID: 103,
Name: "Speed",
Description: "A Spark that increases Speed.",
Group: 1,
Rarity: 3,
Type: 1,
Effects: [][]horse.SparkEffect{
{{Target: 1, Value1: 1}},
{{Target: 1, Value1: 4}},
{{Target: 1, Value1: 7}},
{{Target: 1, Value1: 10}},
{{Target: 1, Value1: 13}},
{{Target: 1, Value1: 16}},
{{Target: 1, Value1: 19}},
{{Target: 1, Value1: 22}},
{{Target: 1, Value1: 25}},
{{Target: 1, Value1: 28}},
},
},
{
ID: 201,
Name: "Stamina",
Description: "A Spark that increases Stamina.",
Group: 2,
Rarity: 1,
Type: 1,
Effects: [][]horse.SparkEffect{
{{Target: 2, Value1: 1}},
{{Target: 2, Value1: 4}},
{{Target: 2, Value1: 7}},
{{Target: 2, Value1: 10}},
{{Target: 2, Value1: 13}},
{{Target: 2, Value1: 16}},
{{Target: 2, Value1: 19}},
{{Target: 2, Value1: 22}},
{{Target: 2, Value1: 25}},
{{Target: 2, Value1: 28}},
},
},
{
ID: 202,
Name: "Stamina",
Description: "A Spark that increases Stamina.",
Group: 2,
Rarity: 2,
Type: 1,
Effects: [][]horse.SparkEffect{
{{Target: 2, Value1: 1}},
{{Target: 2, Value1: 4}},
{{Target: 2, Value1: 7}},
{{Target: 2, Value1: 10}},
{{Target: 2, Value1: 13}},
{{Target: 2, Value1: 16}},
{{Target: 2, Value1: 19}},
{{Target: 2, Value1: 22}},
{{Target: 2, Value1: 25}},
{{Target: 2, Value1: 28}},
},
},
{
ID: 203,
Name: "Stamina",
Description: "A Spark that increases Stamina.",
Group: 2,
Rarity: 3,
Type: 1,
Effects: [][]horse.SparkEffect{
{{Target: 2, Value1: 1}},
{{Target: 2, Value1: 4}},
{{Target: 2, Value1: 7}},
{{Target: 2, Value1: 10}},
{{Target: 2, Value1: 13}},
{{Target: 2, Value1: 16}},
{{Target: 2, Value1: 19}},
{{Target: 2, Value1: 22}},
{{Target: 2, Value1: 25}},
{{Target: 2, Value1: 28}},
},
},
{
ID: 1101,
Name: "Turf",
Description: "A Spark that increases Turf Aptitude.",
Group: 11,
Rarity: 1,
Type: 2,
Effects: [][]horse.SparkEffect{
{{Target: 11, Value1: 1}},
{{Target: 11, Value1: 2}},
},
},
{
ID: 1102,
Name: "Turf",
Description: "A Spark that increases Turf Aptitude.",
Group: 11,
Rarity: 2,
Type: 2,
Effects: [][]horse.SparkEffect{
{{Target: 11, Value1: 1}},
{{Target: 11, Value1: 2}},
},
},
{
ID: 1103,
Name: "Turf",
Description: "A Spark that increases Turf Aptitude.",
Group: 11,
Rarity: 3,
Type: 2,
Effects: [][]horse.SparkEffect{
{{Target: 11, Value1: 1}},
{{Target: 11, Value1: 2}},
},
},
{
ID: 1000101,
Name: "February S.",
Description: `A Spark that increases Power and gives a skill hint for "Winter Runner ○".`,
Group: 10001,
Rarity: 1,
Type: 5,
Effects: [][]horse.SparkEffect{
{{Target: 3, Value1: 3}, {Target: 41, Value1: 200202, Value2: 1}},
{{Target: 3, Value1: 6}, {Target: 41, Value1: 200202, Value2: 1}},
{{Target: 3, Value1: 9}, {Target: 41, Value1: 200202, Value2: 1}},
},
},
{
ID: 1000102,
Name: "February S.",
Description: `A Spark that increases Power and gives a skill hint for "Winter Runner ○".`,
Group: 10001,
Rarity: 2,
Type: 5,
Effects: [][]horse.SparkEffect{
{{Target: 3, Value1: 3}, {Target: 41, Value1: 200202, Value2: 1}},
{{Target: 3, Value1: 6}, {Target: 41, Value1: 200202, Value2: 1}},
{{Target: 3, Value1: 9}, {Target: 41, Value1: 200202, Value2: 1}},
},
},
{
ID: 1000103,
Name: "February S.",
Description: `A Spark that increases Power and gives a skill hint for "Winter Runner ○".`,
Group: 10001,
Rarity: 3,
Type: 5,
Effects: [][]horse.SparkEffect{
{{Target: 3, Value1: 3}, {Target: 41, Value1: 200202, Value2: 1}},
{{Target: 3, Value1: 6}, {Target: 41, Value1: 200202, Value2: 1}},
{{Target: 3, Value1: 9}, {Target: 41, Value1: 200202, Value2: 1}},
},
},
{
ID: 2000101,
Name: "Right-Handed ○",
Description: `A Spark that gives a skill hint for "Right-Handed ○".`,
Group: 20001,
Rarity: 1,
Type: 4,
Effects: [][]horse.SparkEffect{
{{Target: 41, Value1: 200012, Value2: 1}},
{{Target: 41, Value1: 200012, Value2: 2}},
{{Target: 41, Value1: 200012, Value2: 3}},
{{Target: 41, Value1: 200012, Value2: 4}},
{{Target: 41, Value1: 200012, Value2: 5}},
},
},
{
ID: 2000102,
Name: "Right-Handed ○",
Description: `A Spark that gives a skill hint for "Right-Handed ○".`,
Group: 20001,
Rarity: 2,
Type: 4,
Effects: [][]horse.SparkEffect{
{{Target: 41, Value1: 200012, Value2: 1}},
{{Target: 41, Value1: 200012, Value2: 2}},
{{Target: 41, Value1: 200012, Value2: 3}},
{{Target: 41, Value1: 200012, Value2: 4}},
{{Target: 41, Value1: 200012, Value2: 5}},
},
},
{
ID: 2000103,
Name: "Right-Handed ○",
Description: `A Spark that gives a skill hint for "Right-Handed ○".`,
Group: 20001,
Rarity: 3,
Type: 4,
Effects: [][]horse.SparkEffect{
{{Target: 41, Value1: 200012, Value2: 1}},
{{Target: 41, Value1: 200012, Value2: 2}},
{{Target: 41, Value1: 200012, Value2: 3}},
{{Target: 41, Value1: 200012, Value2: 4}},
{{Target: 41, Value1: 200012, Value2: 5}},
},
},
{
ID: 3000101,
Name: "URA Finale",
Description: `A Spark that increases Speed and Stamina.`,
Group: 30001,
Rarity: 1,
Type: 6,
Effects: [][]horse.SparkEffect{
{{Target: 1, Value1: 10}, {Target: 2, Value1: 10}},
{{Target: 1, Value1: 20}, {Target: 2, Value1: 20}},
{{Target: 1, Value1: 30}, {Target: 2, Value1: 30}},
},
},
{
ID: 3000102,
Name: "URA Finale",
Description: `A Spark that increases Speed and Stamina.`,
Group: 30001,
Rarity: 2,
Type: 6,
Effects: [][]horse.SparkEffect{
{{Target: 1, Value1: 10}, {Target: 2, Value1: 10}},
{{Target: 1, Value1: 20}, {Target: 2, Value1: 20}},
{{Target: 1, Value1: 30}, {Target: 2, Value1: 30}},
},
},
{
ID: 3000103,
Name: "URA Finale",
Description: `A Spark that increases Speed and Stamina.`,
Group: 30001,
Rarity: 3,
Type: 6,
Effects: [][]horse.SparkEffect{
{{Target: 1, Value1: 10}, {Target: 2, Value1: 10}},
{{Target: 1, Value1: 20}, {Target: 2, Value1: 20}},
{{Target: 1, Value1: 30}, {Target: 2, Value1: 30}},
},
},
{
ID: 10010101,
Name: "Shooting Star",
Description: `A Spark that gives a skill hint for "Shooting Star".`,
Group: 100101,
Rarity: 1,
Type: 3,
Effects: [][]horse.SparkEffect{
{{Target: 41, Value1: 900011, Value2: 1}},
{{Target: 41, Value1: 900011, Value2: 2}},
{{Target: 41, Value1: 900011, Value2: 3}},
},
},
{
ID: 10010102,
Name: "Shooting Star",
Description: `A Spark that gives a skill hint for "Shooting Star".`,
Group: 100101,
Rarity: 2,
Type: 3,
Effects: [][]horse.SparkEffect{
{{Target: 41, Value1: 900011, Value2: 1}},
{{Target: 41, Value1: 900011, Value2: 2}},
{{Target: 41, Value1: 900011, Value2: 3}},
},
},
{
ID: 10010103,
Name: "Shooting Star",
Description: `A Spark that gives a skill hint for "Shooting Star".`,
Group: 100101,
Rarity: 3,
Type: 3,
Effects: [][]horse.SparkEffect{
{{Target: 41, Value1: 900011, Value2: 1}},
{{Target: 41, Value1: 900011, Value2: 2}},
{{Target: 41, Value1: 900011, Value2: 3}},
},
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong sparks (+got/-want):\n%s", diff)
}
}
@@ -5,5 +5,10 @@ SELECT
value_1,
value_2
FROM succession_factor_effect
WHERE factor_group_id NOT IN (40001) -- exclude Carnival Bonus
-- exclude Carnival Bonus
WHERE factor_group_id NOT IN (
SELECT factor_group_id
FROM succession_factor
WHERE factor_type = 7
)
ORDER BY factor_group_id, effect_id, id
@@ -17,4 +17,4 @@ SELECT
FROM spark
JOIN succession_factor sf ON spark.id = sf.factor_id
WHERE sf.factor_type != 7 -- exclude Carnival Bonus
ORDER BY sf.factor_id
ORDER BY sf.factor_group_id, sf.factor_id
+1 -1
View File
@@ -15,7 +15,7 @@ WITH uma_name AS (
uma.id,
s.skill_id,
s.need_rank,
ROW_NUMBER() OVER (PARTITION BY s.available_skill_set_id, s.need_rank) AS idx
ROW_NUMBER() OVER (PARTITION BY s.available_skill_set_id, s.need_rank ORDER BY s.id) AS idx
FROM card_data uma
LEFT JOIN available_skill_set s ON uma.available_skill_set_id = s.available_skill_set_id
)
+262
View File
@@ -0,0 +1,262 @@
CREATE TABLE IF NOT EXISTS 'text_data' ('id' INTEGER NOT NULL, 'category' INTEGER NOT NULL, 'index' INTEGER NOT NULL, 'text' TEXT NOT NULL, PRIMARY KEY('category','index'));
-- SELECT * FROM text_data WHERE category = 6 AND "index" IN (1001, 1002, 1078, 2001, 9001) OR category IN (4, 5) AND "index" IN (SELECT id FROM card_data WHERE chara_id IN (1001, 1002, 1078, 2001, 9001));
INSERT INTO text_data VALUES(6,6,1001,'Special Week');
INSERT INTO text_data VALUES(6,6,1002,'Silence Suzuka');
INSERT INTO text_data VALUES(6,6,1078,'Yamanin Zephyr');
INSERT INTO text_data VALUES(6,6,2001,'Happy Meek');
INSERT INTO text_data VALUES(6,6,9001,'Tazuna Hayakawa');
INSERT INTO text_data VALUES(4,4,100101,'[Special Dreamer] Special Week');
INSERT INTO text_data VALUES(4,4,100102,'[Hopp''n♪Happy Heart] Special Week');
INSERT INTO text_data VALUES(4,4,100201,'[Innocent Silence] Silence Suzuka');
INSERT INTO text_data VALUES(5,5,100101,'[Special Dreamer]');
INSERT INTO text_data VALUES(5,5,100102,'[Hopp''n♪Happy Heart]');
INSERT INTO text_data VALUES(5,5,100201,'[Innocent Silence]');
CREATE TABLE IF NOT EXISTS 'chara_data' ('id' INTEGER NOT NULL, 'birth_year' INTEGER NOT NULL, 'birth_month' INTEGER NOT NULL, 'birth_day' INTEGER NOT NULL, 'sex' INTEGER NOT NULL, 'image_color_main' TEXT NOT NULL, 'image_color_sub' TEXT NOT NULL, 'ui_color_main' TEXT NOT NULL, 'ui_color_sub' TEXT NOT NULL, 'ui_training_color_1' TEXT NOT NULL, 'ui_training_color_2' TEXT NOT NULL, 'ui_border_color' TEXT NOT NULL, 'ui_num_color_1' TEXT NOT NULL, 'ui_num_color_2' TEXT NOT NULL, 'ui_turn_color' TEXT NOT NULL, 'ui_wipe_color_1' TEXT NOT NULL, 'ui_wipe_color_2' TEXT NOT NULL, 'ui_wipe_color_3' TEXT NOT NULL, 'ui_speech_color_1' TEXT NOT NULL, 'ui_speech_color_2' TEXT NOT NULL, 'ui_nameplate_color_1' TEXT NOT NULL, 'ui_nameplate_color_2' TEXT NOT NULL, 'height' INTEGER NOT NULL, 'bust' INTEGER NOT NULL, 'scale' INTEGER NOT NULL, 'skin' INTEGER NOT NULL, 'shape' INTEGER NOT NULL, 'socks' INTEGER NOT NULL, 'personal_dress' INTEGER NOT NULL, 'tail_model_id' INTEGER NOT NULL, 'race_running_type' INTEGER NOT NULL, 'ear_random_time_min' INTEGER NOT NULL, 'ear_random_time_max' INTEGER NOT NULL, 'tail_random_time_min' INTEGER NOT NULL, 'tail_random_time_max' INTEGER NOT NULL, 'story_ear_random_time_min' INTEGER NOT NULL, 'story_ear_random_time_max' INTEGER NOT NULL, 'story_tail_random_time_min' INTEGER NOT NULL, 'story_tail_random_time_max' INTEGER NOT NULL, 'attachment_model_id' INTEGER NOT NULL, 'mini_mayu_shader_type' INTEGER NOT NULL, 'start_date' INTEGER NOT NULL, 'chara_category' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM chara_data WHERE id IN (1001, 1002, 1078, 2001, 9001);
INSERT INTO chara_data VALUES(1001,1995,5,2,1,'FCA7FF','FF58D9','EE6DCB','FFDEF9','FF7FDD','F759CD','F759CD','FF7FDD','FA50CD','FA50CD','FCCAEE','FCE3F5','FDEDFE','FF7FDD','FF7FDD','FF7CDC','FEB4EA',1,2,158,1,0,3,0,1,1,110,220,30,90,275,425,130,250,-1,0,1483228800,0);
INSERT INTO chara_data VALUES(1002,1994,5,1,1,'8FE78D','FBFF3B','29BD70','FFCE48','4BD18C','27B36B','27B36B','4BD18C','27B36B','27B36B','BDF2D6','D4F2E2','FFF5CC','4BD18C','4BD18C','43C883','82ECB5',1,0,161,1,1,6,0,1,1,170,270,180,310,275,425,180,310,-1,0,1483228800,0);
INSERT INTO chara_data VALUES(1078,1988,5,27,1,'A8C6FD','5277D6','da483a','ffda93','4C91F1','2B75DD','2B75DD','71ADFF','1667D9','1667D9','BCD5F5','D6E4F6','FFEBF6','4C91F1','4C91F1','4C91F1','8DBCFD',1,3,154,1,0,1,0,1,1,170,270,180,310,275,425,180,310,-1,0,2524608000,0);
INSERT INTO chara_data VALUES(2001,1971,3,15,1,'FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','68D25D','68D25D','66D713','25C4A4',2,2,163,0,0,3,0,2,1,170,270,180,310,275,425,180,310,-1,0,2524608000,0);
INSERT INTO chara_data VALUES(9001,1948,5,2,2,'44A705','FFFFFF','44A705','F3CD00','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','FFFFFF','68D25D','68D25D','66D713','25C4A4',2,2,166,1,2,7,0,-1,1,170,270,180,310,275,425,180,310,-1,0,2524608000,1);
CREATE TABLE IF NOT EXISTS 'card_data' ('id' INTEGER NOT NULL, 'chara_id' INTEGER NOT NULL, 'default_rarity' INTEGER NOT NULL, 'limited_chara' INTEGER NOT NULL, 'available_skill_set_id' INTEGER NOT NULL, 'talent_speed' INTEGER NOT NULL, 'talent_stamina' INTEGER NOT NULL, 'talent_pow' INTEGER NOT NULL, 'talent_guts' INTEGER NOT NULL, 'talent_wiz' INTEGER NOT NULL, 'talent_group_id' INTEGER NOT NULL, 'bg_id' INTEGER NOT NULL, 'get_piece_id' INTEGER NOT NULL, 'running_style' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM card_data WHERE chara_id IN (1001, 1002, 1078, 2001, 9001);
INSERT INTO card_data VALUES(100101,1001,3,0,100101,0,20,0,0,10,100101,8,100101,3);
INSERT INTO card_data VALUES(100102,1001,3,0,100102,0,10,10,10,0,100102,317,100102,3);
INSERT INTO card_data VALUES(100201,1002,3,0,100201,20,0,0,10,0,100201,30,100201,1);
CREATE TABLE IF NOT EXISTS 'card_rarity_data' ('id' INTEGER NOT NULL, 'card_id' INTEGER NOT NULL, 'rarity' INTEGER NOT NULL, 'race_dress_id' INTEGER NOT NULL, 'skill_set' INTEGER NOT NULL, 'speed' INTEGER NOT NULL, 'stamina' INTEGER NOT NULL, 'pow' INTEGER NOT NULL, 'guts' INTEGER NOT NULL, 'wiz' INTEGER NOT NULL, 'max_speed' INTEGER NOT NULL, 'max_stamina' INTEGER NOT NULL, 'max_pow' INTEGER NOT NULL, 'max_guts' INTEGER NOT NULL, 'max_wiz' INTEGER NOT NULL, 'proper_distance_short' INTEGER NOT NULL, 'proper_distance_mile' INTEGER NOT NULL, 'proper_distance_middle' INTEGER NOT NULL, 'proper_distance_long' INTEGER NOT NULL, 'proper_running_style_nige' INTEGER NOT NULL, 'proper_running_style_senko' INTEGER NOT NULL, 'proper_running_style_sashi' INTEGER NOT NULL, 'proper_running_style_oikomi' INTEGER NOT NULL, 'proper_ground_turf' INTEGER NOT NULL, 'proper_ground_dirt' INTEGER NOT NULL, 'get_dress_id_1' INTEGER NOT NULL, 'get_dress_id_2' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM card_rarity_data WHERE card_id IN (SELECT id FROM card_data WHERE chara_id IN (1001, 1002, 1078, 2001, 9001));
INSERT INTO card_rarity_data VALUES(10010103,100101,3,100101,10010103,83,88,98,90,91,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100101);
INSERT INTO card_rarity_data VALUES(10010104,100101,4,100101,10010104,92,98,109,100,101,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100101);
INSERT INTO card_rarity_data VALUES(10010105,100101,5,100101,10010105,102,108,120,110,110,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100101);
INSERT INTO card_rarity_data VALUES(10010203,100102,3,100130,11010103,77,90,103,98,82,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100130);
INSERT INTO card_rarity_data VALUES(10010204,100102,4,100130,11010104,86,100,114,109,91,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100130);
INSERT INTO card_rarity_data VALUES(10010205,100102,5,100130,11010105,94,110,125,119,102,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100130);
INSERT INTO card_rarity_data VALUES(10020103,100201,3,100201,10020103,101,84,77,100,88,1200,1200,1200,1200,1200,4,7,7,3,7,5,3,1,7,1,101,100201);
INSERT INTO card_rarity_data VALUES(10020104,100201,4,100201,10020104,112,93,85,111,99,1200,1200,1200,1200,1200,4,7,7,3,7,5,3,1,7,1,101,100201);
INSERT INTO card_rarity_data VALUES(10020105,100201,5,100201,10020105,124,102,94,122,108,1200,1200,1200,1200,1200,4,7,7,3,7,5,3,1,7,1,101,100201);
CREATE TABLE IF NOT EXISTS 'available_skill_set' ('id' INTEGER NOT NULL, 'available_skill_set_id' INTEGER NOT NULL, 'skill_id' INTEGER NOT NULL, 'need_rank' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM available_skill_set WHERE available_skill_set_id IN (SELECT available_skill_set_id FROM card_data WHERE chara_id IN (1001, 1002, 1078, 2001, 9001));
INSERT INTO available_skill_set VALUES(1,100101,200512,0);
INSERT INTO available_skill_set VALUES(2,100101,201352,0);
INSERT INTO available_skill_set VALUES(3,100101,200732,0);
INSERT INTO available_skill_set VALUES(11,100101,200162,2);
INSERT INTO available_skill_set VALUES(12,100101,201351,3);
INSERT INTO available_skill_set VALUES(13,100101,200612,4);
INSERT INTO available_skill_set VALUES(14,100101,200511,5);
INSERT INTO available_skill_set VALUES(715,100102,200462,0);
INSERT INTO available_skill_set VALUES(716,100102,200592,0);
INSERT INTO available_skill_set VALUES(717,100102,201132,0);
INSERT INTO available_skill_set VALUES(718,100102,200212,2);
INSERT INTO available_skill_set VALUES(719,100102,200591,3);
INSERT INTO available_skill_set VALUES(720,100102,201611,4);
INSERT INTO available_skill_set VALUES(721,100102,200461,5);
INSERT INTO available_skill_set VALUES(15,100201,200432,0);
INSERT INTO available_skill_set VALUES(16,100201,200552,0);
INSERT INTO available_skill_set VALUES(17,100201,200712,0);
INSERT INTO available_skill_set VALUES(25,100201,200022,2);
INSERT INTO available_skill_set VALUES(26,100201,200431,3);
INSERT INTO available_skill_set VALUES(27,100201,200542,4);
INSERT INTO available_skill_set VALUES(28,100201,200551,5);
CREATE TABLE IF NOT EXISTS 'skill_set' ('id' INTEGER NOT NULL, 'skill_id1' INTEGER NOT NULL, 'skill_level1' INTEGER NOT NULL, 'skill_id2' INTEGER NOT NULL, 'skill_level2' INTEGER NOT NULL, 'skill_id3' INTEGER NOT NULL, 'skill_level3' INTEGER NOT NULL, 'skill_id4' INTEGER NOT NULL, 'skill_level4' INTEGER NOT NULL, 'skill_id5' INTEGER NOT NULL, 'skill_level5' INTEGER NOT NULL, 'skill_id6' INTEGER NOT NULL, 'skill_level6' INTEGER NOT NULL, 'skill_id7' INTEGER NOT NULL, 'skill_level7' INTEGER NOT NULL, 'skill_id8' INTEGER NOT NULL, 'skill_level8' INTEGER NOT NULL, 'skill_id9' INTEGER NOT NULL, 'skill_level9' INTEGER NOT NULL, 'skill_id10' INTEGER NOT NULL, 'skill_level10' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM skill_set WHERE id IN (SELECT skill_set FROM card_rarity_data WHERE card_id IN (SELECT id FROM card_data WHERE chara_id IN (1001, 1002, 1078, 2001, 9001)));
INSERT INTO skill_set VALUES(10010103,100011,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10010104,100011,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10010105,100011,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10020103,100021,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10020104,100021,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10020105,100021,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(11010103,110011,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(11010104,110011,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(11010105,110011,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
CREATE TABLE IF NOT EXISTS 'succession_relation_member' ('id' INTEGER NOT NULL, 'relation_type' INTEGER NOT NULL, 'chara_id' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM succession_relation_member WHERE chara_id IN (1001, 1002, 1078, 2001, 9001);
INSERT INTO succession_relation_member VALUES(18,103,1001);
INSERT INTO succession_relation_member VALUES(80,202,1001);
INSERT INTO succession_relation_member VALUES(123,301,1001);
INSERT INTO succession_relation_member VALUES(169,401,1001);
INSERT INTO succession_relation_member VALUES(188,502,1001);
INSERT INTO succession_relation_member VALUES(208,2001,1001);
INSERT INTO succession_relation_member VALUES(220,2102,1001);
INSERT INTO succession_relation_member VALUES(235,2301,1001);
INSERT INTO succession_relation_member VALUES(241,2601,1001);
INSERT INTO succession_relation_member VALUES(308,7003,1001);
INSERT INTO succession_relation_member VALUES(310,7004,1001);
INSERT INTO succession_relation_member VALUES(312,7005,1001);
INSERT INTO succession_relation_member VALUES(314,7006,1001);
INSERT INTO succession_relation_member VALUES(316,7007,1001);
INSERT INTO succession_relation_member VALUES(397,2811,1001);
INSERT INTO succession_relation_member VALUES(448,2903,1001);
INSERT INTO succession_relation_member VALUES(509,3003,1001);
INSERT INTO succession_relation_member VALUES(555,3004,1001);
INSERT INTO succession_relation_member VALUES(570,3101,1001);
INSERT INTO succession_relation_member VALUES(662,2510,1001);
INSERT INTO succession_relation_member VALUES(674,2506,1001);
INSERT INTO succession_relation_member VALUES(686,2516,1001);
INSERT INTO succession_relation_member VALUES(758,2519,1001);
INSERT INTO succession_relation_member VALUES(825,3205,1001);
INSERT INTO succession_relation_member VALUES(835,8001,1001);
INSERT INTO succession_relation_member VALUES(837,8002,1001);
INSERT INTO succession_relation_member VALUES(839,8003,1001);
INSERT INTO succession_relation_member VALUES(841,8004,1001);
INSERT INTO succession_relation_member VALUES(843,8005,1001);
INSERT INTO succession_relation_member VALUES(845,8006,1001);
INSERT INTO succession_relation_member VALUES(847,8007,1001);
INSERT INTO succession_relation_member VALUES(849,8008,1001);
INSERT INTO succession_relation_member VALUES(851,8009,1001);
INSERT INTO succession_relation_member VALUES(853,8010,1001);
INSERT INTO succession_relation_member VALUES(855,8011,1001);
INSERT INTO succession_relation_member VALUES(857,8012,1001);
INSERT INTO succession_relation_member VALUES(859,8013,1001);
INSERT INTO succession_relation_member VALUES(861,8014,1001);
INSERT INTO succession_relation_member VALUES(863,8015,1001);
INSERT INTO succession_relation_member VALUES(865,8016,1001);
INSERT INTO succession_relation_member VALUES(1703,2406,1001);
INSERT INTO succession_relation_member VALUES(1715,2603,1001);
INSERT INTO succession_relation_member VALUES(1740,510,1001);
INSERT INTO succession_relation_member VALUES(1802,8443,1001);
INSERT INTO succession_relation_member VALUES(1876,8479,1001);
INSERT INTO succession_relation_member VALUES(1940,8511,1001);
INSERT INTO succession_relation_member VALUES(1990,8536,1001);
INSERT INTO succession_relation_member VALUES(2198,8640,1001);
INSERT INTO succession_relation_member VALUES(31,104,1002);
INSERT INTO succession_relation_member VALUES(81,202,1002);
INSERT INTO succession_relation_member VALUES(124,301,1002);
INSERT INTO succession_relation_member VALUES(174,402,1002);
INSERT INTO succession_relation_member VALUES(207,2001,1002);
INSERT INTO succession_relation_member VALUES(242,2601,1002);
INSERT INTO succession_relation_member VALUES(302,7000,1002);
INSERT INTO succession_relation_member VALUES(304,7001,1002);
INSERT INTO succession_relation_member VALUES(306,7002,1002);
INSERT INTO succession_relation_member VALUES(329,7013,1002);
INSERT INTO succession_relation_member VALUES(333,7015,1002);
INSERT INTO succession_relation_member VALUES(392,2810,1002);
INSERT INTO succession_relation_member VALUES(416,2901,1002);
INSERT INTO succession_relation_member VALUES(510,3003,1002);
INSERT INTO succession_relation_member VALUES(571,3101,1002);
INSERT INTO succession_relation_member VALUES(648,2512,1002);
INSERT INTO succession_relation_member VALUES(826,3205,1002);
INSERT INTO succession_relation_member VALUES(867,8017,1002);
INSERT INTO succession_relation_member VALUES(869,8018,1002);
INSERT INTO succession_relation_member VALUES(871,8019,1002);
INSERT INTO succession_relation_member VALUES(873,8020,1002);
INSERT INTO succession_relation_member VALUES(875,8021,1002);
INSERT INTO succession_relation_member VALUES(877,8022,1002);
INSERT INTO succession_relation_member VALUES(879,8023,1002);
INSERT INTO succession_relation_member VALUES(881,8024,1002);
INSERT INTO succession_relation_member VALUES(883,8025,1002);
INSERT INTO succession_relation_member VALUES(885,8026,1002);
INSERT INTO succession_relation_member VALUES(887,8027,1002);
INSERT INTO succession_relation_member VALUES(889,8028,1002);
INSERT INTO succession_relation_member VALUES(891,8029,1002);
INSERT INTO succession_relation_member VALUES(893,8030,1002);
INSERT INTO succession_relation_member VALUES(895,8031,1002);
INSERT INTO succession_relation_member VALUES(897,8032,1002);
INSERT INTO succession_relation_member VALUES(1704,2406,1002);
INSERT INTO succession_relation_member VALUES(1716,2603,1002);
INSERT INTO succession_relation_member VALUES(1804,8444,1002);
INSERT INTO succession_relation_member VALUES(1878,8480,1002);
INSERT INTO succession_relation_member VALUES(1942,8512,1002);
INSERT INTO succession_relation_member VALUES(1992,8537,1002);
INSERT INTO succession_relation_member VALUES(2200,8641,1002);
CREATE TABLE IF NOT EXISTS 'succession_relation' ('relation_type' INTEGER NOT NULL, 'relation_point' INTEGER NOT NULL, PRIMARY KEY('relation_type'));
-- SELECT * FROM succession_relation WHERE relation_type IN (SELECT relation_type FROM succession_relation_member WHERE chara_id IN (1001, 1002, 1078, 2001, 9001));
INSERT INTO succession_relation VALUES(103,2);
INSERT INTO succession_relation VALUES(104,2);
INSERT INTO succession_relation VALUES(202,2);
INSERT INTO succession_relation VALUES(301,2);
INSERT INTO succession_relation VALUES(401,2);
INSERT INTO succession_relation VALUES(402,2);
INSERT INTO succession_relation VALUES(502,2);
INSERT INTO succession_relation VALUES(510,2);
INSERT INTO succession_relation VALUES(2001,1);
INSERT INTO succession_relation VALUES(2102,1);
INSERT INTO succession_relation VALUES(2301,1);
INSERT INTO succession_relation VALUES(2406,1);
INSERT INTO succession_relation VALUES(2506,1);
INSERT INTO succession_relation VALUES(2510,1);
INSERT INTO succession_relation VALUES(2512,1);
INSERT INTO succession_relation VALUES(2516,1);
INSERT INTO succession_relation VALUES(2519,1);
INSERT INTO succession_relation VALUES(2601,1);
INSERT INTO succession_relation VALUES(2603,1);
INSERT INTO succession_relation VALUES(2810,1);
INSERT INTO succession_relation VALUES(2811,1);
INSERT INTO succession_relation VALUES(2901,7);
INSERT INTO succession_relation VALUES(2903,7);
INSERT INTO succession_relation VALUES(3003,7);
INSERT INTO succession_relation VALUES(3004,7);
INSERT INTO succession_relation VALUES(3101,7);
INSERT INTO succession_relation VALUES(3205,1);
INSERT INTO succession_relation VALUES(7000,1);
INSERT INTO succession_relation VALUES(7001,1);
INSERT INTO succession_relation VALUES(7002,1);
INSERT INTO succession_relation VALUES(7003,1);
INSERT INTO succession_relation VALUES(7004,1);
INSERT INTO succession_relation VALUES(7005,1);
INSERT INTO succession_relation VALUES(7006,1);
INSERT INTO succession_relation VALUES(7007,1);
INSERT INTO succession_relation VALUES(7013,1);
INSERT INTO succession_relation VALUES(7015,1);
INSERT INTO succession_relation VALUES(8001,1);
INSERT INTO succession_relation VALUES(8002,1);
INSERT INTO succession_relation VALUES(8003,1);
INSERT INTO succession_relation VALUES(8004,1);
INSERT INTO succession_relation VALUES(8005,1);
INSERT INTO succession_relation VALUES(8006,1);
INSERT INTO succession_relation VALUES(8007,1);
INSERT INTO succession_relation VALUES(8008,1);
INSERT INTO succession_relation VALUES(8009,1);
INSERT INTO succession_relation VALUES(8010,1);
INSERT INTO succession_relation VALUES(8011,1);
INSERT INTO succession_relation VALUES(8012,1);
INSERT INTO succession_relation VALUES(8013,1);
INSERT INTO succession_relation VALUES(8014,1);
INSERT INTO succession_relation VALUES(8015,1);
INSERT INTO succession_relation VALUES(8016,1);
INSERT INTO succession_relation VALUES(8017,1);
INSERT INTO succession_relation VALUES(8018,1);
INSERT INTO succession_relation VALUES(8019,1);
INSERT INTO succession_relation VALUES(8020,1);
INSERT INTO succession_relation VALUES(8021,1);
INSERT INTO succession_relation VALUES(8022,1);
INSERT INTO succession_relation VALUES(8023,1);
INSERT INTO succession_relation VALUES(8024,1);
INSERT INTO succession_relation VALUES(8025,1);
INSERT INTO succession_relation VALUES(8026,1);
INSERT INTO succession_relation VALUES(8027,1);
INSERT INTO succession_relation VALUES(8028,1);
INSERT INTO succession_relation VALUES(8029,1);
INSERT INTO succession_relation VALUES(8030,1);
INSERT INTO succession_relation VALUES(8031,1);
INSERT INTO succession_relation VALUES(8032,1);
INSERT INTO succession_relation VALUES(8443,1);
INSERT INTO succession_relation VALUES(8444,1);
INSERT INTO succession_relation VALUES(8479,1);
INSERT INTO succession_relation VALUES(8480,1);
INSERT INTO succession_relation VALUES(8511,1);
INSERT INTO succession_relation VALUES(8512,1);
INSERT INTO succession_relation VALUES(8536,1);
INSERT INTO succession_relation VALUES(8537,1);
INSERT INTO succession_relation VALUES(8640,1);
INSERT INTO succession_relation VALUES(8641,1);
CREATE TABLE IF NOT EXISTS 'home_story_trigger' ('id' INTEGER NOT NULL, 'pos_id' INTEGER NOT NULL, 'home_event_type' INTEGER NOT NULL, 'num' INTEGER NOT NULL, 'story_id' INTEGER NOT NULL, 'chara_id_1' INTEGER NOT NULL, 'chara_id_2' INTEGER NOT NULL, 'chara_id_3' INTEGER NOT NULL, 'condition_type' INTEGER NOT NULL, 'gallery_chara_id' INTEGER NOT NULL, 'disp_order' INTEGER NOT NULL, PRIMARY KEY('id'), UNIQUE('gallery_chara_id','disp_order'));
-- SELECT * FROM home_story_trigger WHERE gallery_chara_id IN (1001, 1002, 1078, 2001, 9001);
INSERT INTO home_story_trigger VALUES(1,410,0,1,1001001,1001,0,0,0,1001,1);
INSERT INTO home_story_trigger VALUES(2,510,0,1,1001002,1001,0,0,1,1001,2);
INSERT INTO home_story_trigger VALUES(3,310,0,1,1001003,1001,0,0,1,1001,3);
INSERT INTO home_story_trigger VALUES(76,120,0,2,1001001,1001,1002,0,2,1001,4);
INSERT INTO home_story_trigger VALUES(77,520,0,2,1001002,1003,1001,0,3,1001,5);
INSERT INTO home_story_trigger VALUES(126,430,0,3,1,1001,1014,1011,1,1001,6);
INSERT INTO home_story_trigger VALUES(4,310,0,1,1002001,1002,0,0,0,1002,1);
INSERT INTO home_story_trigger VALUES(5,210,0,1,1002002,1002,0,0,1,1002,2);
INSERT INTO home_story_trigger VALUES(6,110,0,1,1002003,1002,0,0,1,1002,3);
INSERT INTO home_story_trigger VALUES(78,520,0,2,1002001,1010,1002,0,3,1002,4);
INSERT INTO home_story_trigger VALUES(79,220,0,2,1002002,1002,1018,0,2,1002,5);
+86
View File
@@ -0,0 +1,86 @@
CREATE TABLE IF NOT EXISTS 'text_data' ('id' INTEGER NOT NULL, 'category' INTEGER NOT NULL, 'index' INTEGER NOT NULL, 'text' TEXT NOT NULL, PRIMARY KEY('category','index'));
-- SELECT * FROM text_data WHERE category = 33 AND "index" IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501) OR category = 111 AND "index" IN (SELECT id FROM single_mode_wins_saddle WHERE race_instance_id_1 IN (SELECT id FROM race_instance WHERE race_id IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501)) UNION SELECT id FROM single_mode_wins_saddle WHERE race_instance_id_2 IN (SELECT id FROM race_instance WHERE race_id IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501)) UNION SELECT id FROM single_mode_wins_saddle WHERE race_instance_id_3 IN (SELECT id FROM race_instance WHERE race_id IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501))) OR category IN (237, 119) AND "index" IN (1, 2, 4);
INSERT INTO text_data VALUES(33,33,1005,'Satsuki Sho');
INSERT INTO text_data VALUES(33,33,1010,'Tokyo Yushun (Japanese Derby)');
INSERT INTO text_data VALUES(33,33,1015,'Kikuka Sho');
INSERT INTO text_data VALUES(33,33,1026,'Kikuka Sho');
INSERT INTO text_data VALUES(33,33,1028,'Satsuki Sho');
INSERT INTO text_data VALUES(33,33,1101,'Teio Sho');
INSERT INTO text_data VALUES(33,33,2001,'Nikkei Shinshun Hai');
INSERT INTO text_data VALUES(33,33,2010,'Spring Stakes');
INSERT INTO text_data VALUES(33,33,2035,'Spring Stakes');
INSERT INTO text_data VALUES(33,33,3001,'Kyoto Kimpai');
INSERT INTO text_data VALUES(33,33,4001,'Manyo Stakes');
INSERT INTO text_data VALUES(33,33,4501,'Aster Sho');
INSERT INTO text_data VALUES(111,111,1,'Classic Triple Crown');
INSERT INTO text_data VALUES(111,111,12,'Japanese Derby');
INSERT INTO text_data VALUES(111,111,16,'Kikuka Sho');
INSERT INTO text_data VALUES(111,111,18,'Satsuki Sho');
INSERT INTO text_data VALUES(111,111,36,'Teio Sho');
INSERT INTO text_data VALUES(111,111,40,'Nikkei Shinshun Hai');
INSERT INTO text_data VALUES(111,111,49,'Spring S.');
INSERT INTO text_data VALUES(111,111,74,'Kyoto Kimpai');
INSERT INTO text_data VALUES(111,111,144,'Classic Triple Crown');
INSERT INTO text_data VALUES(111,111,148,'Kikuka Sho');
INSERT INTO text_data VALUES(111,111,149,'Spring S.');
INSERT INTO text_data VALUES(111,111,154,'Classic Triple Crown');
INSERT INTO text_data VALUES(111,111,155,'Satsuki Sho');
INSERT INTO text_data VALUES(119,119,1,'The Beginning: URA Finale');
INSERT INTO text_data VALUES(119,119,2,'Unity Cup: Shine On, Team Spirit!');
INSERT INTO text_data VALUES(119,119,4,'Trackblazer: Start of the Climax');
INSERT INTO text_data VALUES(237,237,1,'URA Finale');
INSERT INTO text_data VALUES(237,237,2,'Unity Cup');
INSERT INTO text_data VALUES(237,237,4,'TS Climax');
CREATE TABLE IF NOT EXISTS 'race' ('id' INTEGER NOT NULL, 'group' INTEGER NOT NULL, 'grade' INTEGER NOT NULL, 'course_set' INTEGER NOT NULL, 'thumbnail_id' INTEGER NOT NULL, 'ff_cue_name' TEXT NOT NULL, 'ff_cuesheet_name' TEXT NOT NULL, 'ff_anim' INTEGER NOT NULL, 'ff_camera' INTEGER NOT NULL, 'ff_camera_sub' INTEGER NOT NULL, 'ff_sub' INTEGER NOT NULL, 'goal_gate' INTEGER NOT NULL, 'goal_flower' INTEGER NOT NULL, 'audience' INTEGER NOT NULL, 'entry_num' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM race WHERE id IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501);
INSERT INTO race VALUES(1005,1,100,10504,1005,'snd_mfx_1001_CL','snd_mfx_1001_CL',1005,101,0,0,1,1,0,18);
INSERT INTO race VALUES(1010,1,100,10606,1010,'snd_mfx_1001_CL','snd_mfx_1001_CL',1010,101,0,0,5,5,0,18);
INSERT INTO race VALUES(1015,1,100,10810,1015,'snd_mfx_1002_CL','snd_mfx_1002_CL',1015,102,0,0,3,3,0,18);
INSERT INTO race VALUES(1026,1,100,10810,1015,'snd_mfx_1002_CL','snd_mfx_1002_CL',1015,102,0,0,3,3,0,17);
INSERT INTO race VALUES(1028,1,100,10604,1028,'snd_mfx_1001_CL','snd_mfx_1001_CL',1028,101,0,0,9,0,0,18);
INSERT INTO race VALUES(1101,1,100,11103,1101,'snd_mfx_1015_CL','snd_mfx_1015_CL',1101,313,0,0,1,0,0,16);
INSERT INTO race VALUES(2001,1,200,10809,2001,'snd_mfx_1008_CL','snd_mfx_1008_CL',2001,208,0,0,0,0,0,18);
INSERT INTO race VALUES(2010,1,200,10503,2010,'snd_mfx_1005_CL','snd_mfx_1005_CL',2010,205,0,0,0,0,0,16);
INSERT INTO race VALUES(2035,1,200,10503,2010,'snd_mfx_1005_CL','snd_mfx_1005_CL',2010,205,0,0,0,0,0,5);
INSERT INTO race VALUES(3001,1,300,10805,3001,'snd_mfx_1008_CL','snd_mfx_1008_CL',3001,208,0,0,0,0,0,18);
INSERT INTO race VALUES(4001,1,400,10810,4001,'snd_mfx_1013_CL','snd_mfx_1013_CL',4001,313,0,0,0,0,0,18);
INSERT INTO race VALUES(4501,1,700,10502,4501,'snd_mfx_1010_CL','snd_mfx_1010_CL',4501,310,0,0,0,0,0,16);
CREATE TABLE IF NOT EXISTS 'race_instance' ('id' INTEGER NOT NULL, 'race_id' INTEGER NOT NULL, 'npc_group_id' INTEGER NOT NULL, 'date' INTEGER NOT NULL, 'time' INTEGER NOT NULL, 'clock_time' INTEGER NOT NULL, 'race_number' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM race_instance WHERE race_id IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501);
INSERT INTO race_instance VALUES(100501,1005,199,414,2,0,11);
INSERT INTO race_instance VALUES(101001,1010,199,526,2,0,11);
INSERT INTO race_instance VALUES(701301,1010,0,0,2,0,11);
INSERT INTO race_instance VALUES(101501,1015,199,1020,2,0,11);
INSERT INTO race_instance VALUES(102601,1026,199,1020,2,0,11);
INSERT INTO race_instance VALUES(102801,1028,199,414,2,0,11);
INSERT INTO race_instance VALUES(110101,1101,303,626,4,0,11);
INSERT INTO race_instance VALUES(200101,2001,103,117,2,0,11);
INSERT INTO race_instance VALUES(201001,2010,102,317,2,0,11);
INSERT INTO race_instance VALUES(203501,2035,102,317,2,0,11);
INSERT INTO race_instance VALUES(300101,3001,102,105,2,0,11);
INSERT INTO race_instance VALUES(400101,4001,104,105,2,0,10);
INSERT INTO race_instance VALUES(450101,4501,102,907,2,0,9);
CREATE TABLE IF NOT EXISTS 'single_mode_wins_saddle' ('id' INTEGER NOT NULL, 'priority' INTEGER NOT NULL, 'group_id' INTEGER NOT NULL, 'condition' INTEGER NOT NULL, 'win_saddle_type' INTEGER NOT NULL, 'race_instance_id_1' INTEGER NOT NULL, 'race_instance_id_2' INTEGER NOT NULL, 'race_instance_id_3' INTEGER NOT NULL, 'race_instance_id_4' INTEGER NOT NULL, 'race_instance_id_5' INTEGER NOT NULL, 'race_instance_id_6' INTEGER NOT NULL, 'race_instance_id_7' INTEGER NOT NULL, 'race_instance_id_8' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM single_mode_wins_saddle WHERE id IN (SELECT id FROM single_mode_wins_saddle WHERE race_instance_id_1 IN (SELECT id FROM race_instance WHERE race_id IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501)) UNION SELECT id FROM single_mode_wins_saddle WHERE race_instance_id_2 IN (SELECT id FROM race_instance WHERE race_id IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501)) UNION SELECT id FROM single_mode_wins_saddle WHERE race_instance_id_3 IN (SELECT id FROM race_instance WHERE race_id IN (1005, 1010, 1015, 1026, 1028, 1101, 2001, 2010, 2035, 3001, 4001, 4501)));
INSERT INTO single_mode_wins_saddle VALUES(1,1,1,0,0,100501,101001,101501,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(12,15,22,0,3,101001,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(16,20,28,0,3,101501,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(18,23,17,0,3,100501,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(36,41,39,0,3,110101,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(40,45,45,0,2,200101,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(49,54,54,0,2,201001,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(74,80,80,0,1,300101,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(144,2,1,0,0,100501,101001,102601,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(148,21,28,0,3,102601,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(149,55,54,0,2,203501,0,0,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(154,2,1,0,0,102801,101001,101501,0,0,0,0,0);
INSERT INTO single_mode_wins_saddle VALUES(155,23,17,0,3,102801,0,0,0,0,0,0,0);
CREATE TABLE IF NOT EXISTS 'single_mode_scenario' ('id' INTEGER NOT NULL, 'sort_id' INTEGER NOT NULL, 'scenario_image_id' INTEGER NOT NULL, 'prologue_id' INTEGER NOT NULL, 'turn_set_id' INTEGER NOT NULL, 'hint_rate' INTEGER NOT NULL, 'start_date' INTEGER NOT NULL, 'end_date' INTEGER NOT NULL, 'bg_id' INTEGER NOT NULL, 'bg_sub_id' INTEGER NOT NULL, 'bg_offset_x' INTEGER NOT NULL, 'sec_bg_id' INTEGER NOT NULL, 'sec_bg_sub_id' INTEGER NOT NULL, 'label_font_color' TEXT NOT NULL, 'label_bg_color' TEXT NOT NULL, 'chara_program_change_flag' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM single_mode_scenario WHERE id IN (1, 2, 4);
INSERT INTO single_mode_scenario VALUES(1,1,1,80000001,1,0,1577836800,2524608000,5,5110,375,37,110,'FFFFFF','9CD127',0);
INSERT INTO single_mode_scenario VALUES(2,2,2,80000002,2,0,1762466400,2524608000,124,2111,0,79,110,'FFFFFF','18B1FF',0);
INSERT INTO single_mode_scenario VALUES(4,3,4,80000004,4,0,1773352800,2524608000,24,110,0,56,111,'FFFFFF','EABA00',1);
+164
View File
@@ -0,0 +1,164 @@
CREATE TABLE IF NOT EXISTS 'text_data' ('id' INTEGER NOT NULL, 'category' INTEGER NOT NULL, 'index' INTEGER NOT NULL, 'text' TEXT NOT NULL, PRIMARY KEY('category','index'));
-- SELECT * FROM text_data WHERE category IN (47, 48) AND "index" IN (10351, 100011, 100351, 110241, 200011, 200012, 200013, 200014, 200021, 200022, 200023, 200361, 200362, 200831, 201801, 300011, 900011, 900351, 910241) OR category = 4 AND "index" IN (SELECT card_id FROM card_rarity_data WHERE skill_set IN (SELECT id FROM skill_set WHERE skill_id1 IN (10351, 100011, 100351, 110241, 200011, 200012, 200013, 200014, 200021, 200022, 200023, 200361, 200362, 200831, 201801, 300011, 900011, 900351, 910241)));
INSERT INTO text_data VALUES(47,47,10351,'V Is for Victory!');
INSERT INTO text_data VALUES(47,47,100011,'Shooting Star');
INSERT INTO text_data VALUES(47,47,100351,'Our Ticket to Win!');
INSERT INTO text_data VALUES(47,47,110241,'Flowery☆Maneuver');
INSERT INTO text_data VALUES(47,47,200011,'Right-Handed ◎');
INSERT INTO text_data VALUES(47,47,200012,'Right-Handed ○');
INSERT INTO text_data VALUES(47,47,200013,'Right-Handed ×');
INSERT INTO text_data VALUES(47,47,200014,'Right-Handed Demon');
INSERT INTO text_data VALUES(47,47,200021,'Left-Handed ◎');
INSERT INTO text_data VALUES(47,47,200022,'Left-Handed ○');
INSERT INTO text_data VALUES(47,47,200023,'Left-Handed ×');
INSERT INTO text_data VALUES(47,47,200361,'Beeline Burst');
INSERT INTO text_data VALUES(47,47,200362,'Straightaway Adept');
INSERT INTO text_data VALUES(47,47,200831,'Subdued Front Runners');
INSERT INTO text_data VALUES(47,47,201801,'♡ 3D Nail Art');
INSERT INTO text_data VALUES(47,47,300011,'Unquenched Thirst');
INSERT INTO text_data VALUES(47,47,900011,'Shooting Star');
INSERT INTO text_data VALUES(47,47,900351,'Our Ticket to Win!');
INSERT INTO text_data VALUES(47,47,910241,'Flowery☆Maneuver');
INSERT INTO text_data VALUES(48,48,10351,'Moderately increase velocity with winning ambition when positioned toward the front on the final straight after engaging in a challenge on the final corner or later.');
INSERT INTO text_data VALUES(48,48,100011,'Ride the momentum to increase velocity and very slightly increase acceleration after passing another runner toward the front late-race.');
INSERT INTO text_data VALUES(48,48,100351,'Increase velocity with winning ambition when positioned toward the front on the final straight after engaging in a challenge on the final corner or later.');
INSERT INTO text_data VALUES(48,48,110241,'Increase velocity when passing another runner toward the front on the final corner. If passing toward the back, increase acceleration instead.');
INSERT INTO text_data VALUES(48,48,200011,'Increase performance on right-handed tracks.');
INSERT INTO text_data VALUES(48,48,200012,'Moderately increase performance on right-handed tracks.');
INSERT INTO text_data VALUES(48,48,200013,'Moderately decrease performance on right-handed tracks.');
INSERT INTO text_data VALUES(48,48,200014,'Increase proficiency in right-handed tracks, increasing Speed and Power.');
INSERT INTO text_data VALUES(48,48,200021,'Increase performance on left-handed tracks.');
INSERT INTO text_data VALUES(48,48,200022,'Moderately increase performance on left-handed tracks.');
INSERT INTO text_data VALUES(48,48,200023,'Moderately decrease performance on left-handed tracks.');
INSERT INTO text_data VALUES(48,48,200361,'Increase velocity on a straight.');
INSERT INTO text_data VALUES(48,48,200362,'Slightly increase velocity on a straight.');
INSERT INTO text_data VALUES(48,48,200831,'Slightly increase fatigue for front runners early-race.');
INSERT INTO text_data VALUES(48,48,201801,'Moderately decrease performance on firm ground.');
INSERT INTO text_data VALUES(48,48,300011,'Moderately increase performance with the desire to race.');
INSERT INTO text_data VALUES(48,48,900011,'Slightly increase velocity and very minimally increase acceleration after passing another runner toward the front late-race.');
INSERT INTO text_data VALUES(48,48,900351,'Slightly increase velocity when positioned toward the front on the final straight after engaging in a challenge on the final corner or later.');
INSERT INTO text_data VALUES(48,48,910241,'Slightly increase velocity when passing another runner toward the front on the final corner. If passing toward the back, slightly increase acceleration instead.');
INSERT INTO text_data VALUES(4,4,100101,'[Special Dreamer] Special Week');
INSERT INTO text_data VALUES(4,4,102402,'[Sunlight Bouquet] Mayano Top Gun');
INSERT INTO text_data VALUES(4,4,103501,'[Get to Winning!] Winning Ticket');
CREATE TABLE IF NOT EXISTS 'skill_data' ('id' INTEGER NOT NULL, 'rarity' INTEGER NOT NULL, 'group_id' INTEGER NOT NULL, 'group_rate' INTEGER NOT NULL, 'filter_switch' INTEGER NOT NULL, 'grade_value' INTEGER NOT NULL, 'skill_category' INTEGER NOT NULL, 'tag_id' TEXT NOT NULL, 'unique_skill_id_1' INTEGER NOT NULL, 'unique_skill_id_2' INTEGER NOT NULL, 'exp_type' INTEGER NOT NULL, 'potential_per_default' INTEGER NOT NULL, 'activate_lot' INTEGER NOT NULL, 'precondition_1' TEXT NOT NULL, 'condition_1' TEXT NOT NULL, 'float_ability_time_1' INTEGER NOT NULL, 'ability_time_usage_1' INTEGER NOT NULL, 'float_cooldown_time_1' INTEGER NOT NULL, 'ability_type_1_1' INTEGER NOT NULL, 'ability_value_usage_1_1' INTEGER NOT NULL, 'ability_value_level_usage_1_1' INTEGER NOT NULL, 'float_ability_value_1_1' INTEGER NOT NULL, 'target_type_1_1' INTEGER NOT NULL, 'target_value_1_1' INTEGER NOT NULL, 'ability_type_1_2' INTEGER NOT NULL, 'ability_value_usage_1_2' INTEGER NOT NULL, 'ability_value_level_usage_1_2' INTEGER NOT NULL, 'float_ability_value_1_2' INTEGER NOT NULL, 'target_type_1_2' INTEGER NOT NULL, 'target_value_1_2' INTEGER NOT NULL, 'ability_type_1_3' INTEGER NOT NULL, 'ability_value_usage_1_3' INTEGER NOT NULL, 'ability_value_level_usage_1_3' INTEGER NOT NULL, 'float_ability_value_1_3' INTEGER NOT NULL, 'target_type_1_3' INTEGER NOT NULL, 'target_value_1_3' INTEGER NOT NULL, 'precondition_2' TEXT NOT NULL, 'condition_2' TEXT NOT NULL, 'float_ability_time_2' INTEGER NOT NULL, 'ability_time_usage_2' INTEGER NOT NULL, 'float_cooldown_time_2' INTEGER NOT NULL, 'ability_type_2_1' INTEGER NOT NULL, 'ability_value_usage_2_1' INTEGER NOT NULL, 'ability_value_level_usage_2_1' INTEGER NOT NULL, 'float_ability_value_2_1' INTEGER NOT NULL, 'target_type_2_1' INTEGER NOT NULL, 'target_value_2_1' INTEGER NOT NULL, 'ability_type_2_2' INTEGER NOT NULL, 'ability_value_usage_2_2' INTEGER NOT NULL, 'ability_value_level_usage_2_2' INTEGER NOT NULL, 'float_ability_value_2_2' INTEGER NOT NULL, 'target_type_2_2' INTEGER NOT NULL, 'target_value_2_2' INTEGER NOT NULL, 'ability_type_2_3' INTEGER NOT NULL, 'ability_value_usage_2_3' INTEGER NOT NULL, 'ability_value_level_usage_2_3' INTEGER NOT NULL, 'float_ability_value_2_3' INTEGER NOT NULL, 'target_type_2_3' INTEGER NOT NULL, 'target_value_2_3' INTEGER NOT NULL, 'popularity_add_param_1' INTEGER NOT NULL, 'popularity_add_value_1' INTEGER NOT NULL, 'popularity_add_param_2' INTEGER NOT NULL, 'popularity_add_value_2' INTEGER NOT NULL, 'disp_order' INTEGER NOT NULL, 'icon_id' INTEGER NOT NULL, 'plate_type' INTEGER NOT NULL, 'disable_singlemode' INTEGER NOT NULL, 'disable_count_condition' INTEGER NOT NULL, 'is_general_skill' INTEGER NOT NULL, 'start_date' INTEGER NOT NULL, 'end_date' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM skill_data WHERE id IN (10351, 100011, 100351, 110241, 200011, 200012, 200013, 200014, 200021, 200022, 200023, 200361, 200362, 200831, 201801, 300011, 900011, 900351, 910241);
INSERT INTO skill_data VALUES(10351,3,1035,1,0,240,5,'401',0,0,1,0,0,'is_finalcorner==1&blocked_side_continuetime>=2','is_finalcorner==1&corner==0&order<=5',50000,1,5000000,27,1,1,2500,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,30,0,0,10,20013,0,1,0,0,1483228800,2524608000);
INSERT INTO skill_data VALUES(100011,5,10001,1,0,340,5,'401/403',0,0,1,0,0,'','phase>=2&order>=1&order_rate<=50&change_order_onetime<0',50000,1,5000000,27,1,1,3500,1,0,31,1,1,1000,1,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,60,0,0,10,20013,0,1,0,0,1483228800,2524608000);
INSERT INTO skill_data VALUES(100351,4,10035,1,0,340,5,'401',0,0,1,0,0,'is_finalcorner==1&blocked_side_continuetime>=2','is_finalcorner==1&corner==0&order<=5',50000,1,5000000,27,1,1,3500,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,60,0,0,10,20013,0,1,0,0,1483228800,2524608000);
INSERT INTO skill_data VALUES(110241,5,11024,1,0,340,5,'401/403',0,0,1,0,0,'','is_finalcorner==1&corner!=0&order_rate<=40&change_order_onetime<0',50000,1,5000000,27,1,1,3500,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','is_finalcorner==1&corner!=0&order_rate>=50&order_rate<=80&change_order_onetime<0',40000,1,5000000,31,1,1,4000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,1,30,3,30,10,20013,0,1,0,0,1483228800,2524608000);
INSERT INTO skill_data VALUES(200011,1,20001,2,0,174,0,'401',0,0,0,10,0,'','rotation==1',-1,1,0,1,1,1,600000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,45,0,0,1005,10011,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200012,1,20001,1,0,129,0,'401',0,0,0,10,0,'','rotation==1',-1,1,0,1,1,1,400000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,30,0,0,1010,10011,0,0,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200013,1,20001,-1,0,-129,0,'401',0,0,0,10,0,'','rotation==1',-1,1,0,1,1,1,-400000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,-30,0,0,1020,10014,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200014,2,20001,3,0,461,0,'401/403',0,0,0,10,0,'','rotation==1',-1,1,0,1,1,1,600000,1,0,3,1,1,600000,1,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,30,3,30,1000,10012,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200021,1,20002,2,0,174,0,'401',0,0,0,10,0,'','rotation==2',-1,1,0,1,1,1,600000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,45,0,0,1030,10011,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200022,1,20002,1,0,129,0,'401',0,0,0,10,0,'','rotation==2',-1,1,0,1,1,1,400000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,30,0,0,1040,10011,0,0,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200023,1,20002,-1,0,-129,0,'401',0,0,0,10,0,'','rotation==2',-1,1,0,1,1,1,-400000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,-30,0,0,1050,10014,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200361,2,20036,2,0,508,4,'401',0,0,0,10,1,'','straight_random==1',24000,1,300000,27,1,1,3500,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,60,0,0,1980,20012,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200362,1,20036,1,0,217,4,'401',0,0,0,10,1,'','straight_random==1',24000,1,300000,27,1,1,1500,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,20,0,0,1990,20011,0,0,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(200831,1,20083,1,0,217,1,'301/406',0,0,0,10,1,'','running_style_count_nige_otherself>=1&phase_random==0&accumulatetime>=5',0,1,5000000,9,1,1,-100,18,1,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,2,10,5,10,2820,30051,0,0,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(201801,1,20180,-1,0,-129,0,'401',0,0,0,10,0,'','ground_condition==1',-1,1,0,1,1,1,-400000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,-30,0,0,20,10014,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(300011,1,30001,1,0,0,0,'402',0,0,0,0,0,'','track_id==10008',-1,1,0,2,1,1,400000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,2,30,0,0,70000,10021,1,1,0,0,1483228800,2524608000);
INSERT INTO skill_data VALUES(900011,1,90001,2,0,180,5,'401/403',100011,0,0,0,1,'','phase>=2&order>=1&order_rate<=50&change_order_onetime<0',30000,1,5000000,27,1,1,1500,1,0,31,1,1,500,1,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,15,0,0,30,20011,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(900351,1,90035,2,0,180,5,'401',100351,10351,0,0,1,'is_finalcorner==1&blocked_side_continuetime>=2','is_finalcorner==1&corner==0&order<=5',30000,1,5000000,27,1,1,1500,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','',0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,15,0,0,30,20011,0,1,0,1,1483228800,2524608000);
INSERT INTO skill_data VALUES(910241,1,91024,2,0,180,5,'401/403',110241,0,0,0,1,'','is_finalcorner==1&corner!=0&order_rate<=40&change_order_onetime<0',30000,1,5000000,27,1,1,1500,1,0,0,1,1,0,0,0,0,1,1,0,0,0,'','is_finalcorner==1&corner!=0&order_rate>=50&order_rate<=80&change_order_onetime<0',24000,1,5000000,31,1,1,2000,1,0,0,1,1,0,0,0,0,1,1,0,0,0,1,7,3,7,30,20011,0,1,0,1,1483228800,2524608000);
CREATE TABLE IF NOT EXISTS 'single_mode_skill_need_point' ('id' INTEGER NOT NULL, 'need_skill_point' INTEGER NOT NULL, 'status_type' INTEGER NOT NULL, 'status_value' INTEGER NOT NULL, 'solvable_type' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM single_mode_skill_need_point WHERE id IN (10351, 100011, 100351, 110241, 200011, 200012, 200013, 200014, 200021, 200022, 200023, 200361, 200362, 200831, 201801, 300011, 900011, 900351, 910241);
INSERT INTO single_mode_skill_need_point VALUES(200011,110,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200012,90,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200013,50,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200014,130,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200021,110,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200022,90,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200023,50,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200361,170,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200362,170,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(200831,130,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(201801,50,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(900011,200,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(900351,200,0,0,0);
INSERT INTO single_mode_skill_need_point VALUES(910241,200,0,0,0);
CREATE TABLE IF NOT EXISTS 'card_data' ('id' INTEGER NOT NULL, 'chara_id' INTEGER NOT NULL, 'default_rarity' INTEGER NOT NULL, 'limited_chara' INTEGER NOT NULL, 'available_skill_set_id' INTEGER NOT NULL, 'talent_speed' INTEGER NOT NULL, 'talent_stamina' INTEGER NOT NULL, 'talent_pow' INTEGER NOT NULL, 'talent_guts' INTEGER NOT NULL, 'talent_wiz' INTEGER NOT NULL, 'talent_group_id' INTEGER NOT NULL, 'bg_id' INTEGER NOT NULL, 'get_piece_id' INTEGER NOT NULL, 'running_style' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM card_data WHERE id IN (SELECT card_id FROM card_rarity_data WHERE skill_set IN (SELECT id FROM skill_set WHERE skill_id1 IN (10351, 100011, 100351, 110241, 200011, 200012, 200013, 200014, 200021, 200022, 200023, 200361, 200362, 200831, 201801, 300011, 900011, 900351, 910241)));
INSERT INTO card_data VALUES(100101,1001,3,0,100101,0,20,0,0,10,100101,8,100101,3);
INSERT INTO card_data VALUES(102402,1024,3,0,102402,10,10,0,0,10,102402,291,102402,2);
INSERT INTO card_data VALUES(103501,1035,1,0,103501,0,10,20,0,0,103501,8,103501,3);
CREATE TABLE IF NOT EXISTS 'card_rarity_data' ('id' INTEGER NOT NULL, 'card_id' INTEGER NOT NULL, 'rarity' INTEGER NOT NULL, 'race_dress_id' INTEGER NOT NULL, 'skill_set' INTEGER NOT NULL, 'speed' INTEGER NOT NULL, 'stamina' INTEGER NOT NULL, 'pow' INTEGER NOT NULL, 'guts' INTEGER NOT NULL, 'wiz' INTEGER NOT NULL, 'max_speed' INTEGER NOT NULL, 'max_stamina' INTEGER NOT NULL, 'max_pow' INTEGER NOT NULL, 'max_guts' INTEGER NOT NULL, 'max_wiz' INTEGER NOT NULL, 'proper_distance_short' INTEGER NOT NULL, 'proper_distance_mile' INTEGER NOT NULL, 'proper_distance_middle' INTEGER NOT NULL, 'proper_distance_long' INTEGER NOT NULL, 'proper_running_style_nige' INTEGER NOT NULL, 'proper_running_style_senko' INTEGER NOT NULL, 'proper_running_style_sashi' INTEGER NOT NULL, 'proper_running_style_oikomi' INTEGER NOT NULL, 'proper_ground_turf' INTEGER NOT NULL, 'proper_ground_dirt' INTEGER NOT NULL, 'get_dress_id_1' INTEGER NOT NULL, 'get_dress_id_2' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM card_rarity_data WHERE skill_set IN (SELECT id FROM skill_set WHERE skill_id1 IN (10351, 100011, 100351, 110241, 200011, 200012, 200013, 200014, 200021, 200022, 200023, 200361, 200362, 200831, 201801, 300011, 900011, 900351, 910241));
INSERT INTO card_rarity_data VALUES(10010103,100101,3,100101,10010103,83,88,98,90,91,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100101);
INSERT INTO card_rarity_data VALUES(10010104,100101,4,100101,10010104,92,98,109,100,101,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100101);
INSERT INTO card_rarity_data VALUES(10010105,100101,5,100101,10010105,102,108,120,110,110,1200,1200,1200,1200,1200,2,5,7,7,1,7,7,5,7,1,101,100101);
INSERT INTO card_rarity_data VALUES(10240203,102402,3,102426,11240103,83,100,82,90,95,1200,1200,1200,1200,1200,4,4,7,7,7,7,6,6,7,3,101,102426);
INSERT INTO card_rarity_data VALUES(10240204,102402,4,102426,11240104,92,112,91,100,105,1200,1200,1200,1200,1200,4,4,7,7,7,7,6,6,7,3,101,102426);
INSERT INTO card_rarity_data VALUES(10240205,102402,5,102426,11240105,102,123,100,110,115,1200,1200,1200,1200,1200,4,4,7,7,7,7,6,6,7,3,101,102426);
INSERT INTO card_rarity_data VALUES(10350101,103501,1,101,10350101,87,68,91,74,80,1200,1200,1200,1200,1200,1,2,7,6,1,6,7,1,7,1,101,0);
INSERT INTO card_rarity_data VALUES(10350102,103501,2,101,10350102,93,72,97,78,85,1200,1200,1200,1200,1200,1,2,7,6,1,6,7,1,7,1,101,0);
INSERT INTO card_rarity_data VALUES(10350103,103501,3,103501,10350103,98,76,102,83,91,1200,1200,1200,1200,1200,1,2,7,6,1,6,7,1,7,1,101,103501);
INSERT INTO card_rarity_data VALUES(10350104,103501,4,103501,10350104,109,85,114,92,100,1200,1200,1200,1200,1200,1,2,7,6,1,6,7,1,7,1,101,103501);
INSERT INTO card_rarity_data VALUES(10350105,103501,5,103501,10350105,120,93,125,101,111,1200,1200,1200,1200,1200,1,2,7,6,1,6,7,1,7,1,101,103501);
CREATE TABLE IF NOT EXISTS 'skill_set' ('id' INTEGER NOT NULL, 'skill_id1' INTEGER NOT NULL, 'skill_level1' INTEGER NOT NULL, 'skill_id2' INTEGER NOT NULL, 'skill_level2' INTEGER NOT NULL, 'skill_id3' INTEGER NOT NULL, 'skill_level3' INTEGER NOT NULL, 'skill_id4' INTEGER NOT NULL, 'skill_level4' INTEGER NOT NULL, 'skill_id5' INTEGER NOT NULL, 'skill_level5' INTEGER NOT NULL, 'skill_id6' INTEGER NOT NULL, 'skill_level6' INTEGER NOT NULL, 'skill_id7' INTEGER NOT NULL, 'skill_level7' INTEGER NOT NULL, 'skill_id8' INTEGER NOT NULL, 'skill_level8' INTEGER NOT NULL, 'skill_id9' INTEGER NOT NULL, 'skill_level9' INTEGER NOT NULL, 'skill_id10' INTEGER NOT NULL, 'skill_level10' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM skill_set WHERE skill_id1 IN (10351, 100011, 100351, 110241, 200011, 200012, 200013, 200014, 200021, 200022, 200023, 200361, 200362, 200831, 201801, 300011, 900011, 900351, 910241);
INSERT INTO skill_set VALUES(8001,200012,1,200433,1,200532,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8007,200022,1,200572,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8008,200362,1,200562,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8011,200012,1,200592,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8016,200022,1,200472,1,200502,1,200632,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8028,200012,1,200492,1,200572,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8034,200022,1,200612,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8037,200362,1,200632,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8043,200012,1,200401,1,200532,1,200712,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8053,200362,1,200502,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8054,200022,1,200352,1,200612,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8057,200012,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8068,200022,1,200572,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8072,200022,1,200612,1,200752,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8078,200012,1,200401,1,200202,1,200622,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8083,200022,1,200332,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8101,200362,1,200352,1,200552,1,200682,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8103,200012,1,200052,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8107,200012,1,200372,1,200582,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8114,200012,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8117,200022,1,200332,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8123,200362,1,200532,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8124,200012,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(8133,200022,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(100013,100351,1,200591,1,201401,1,201392,1,201422,1,200492,1,200432,1,200612,1,200032,1,200732,1);
INSERT INTO skill_set VALUES(100037,100351,1,201401,1,200612,1,200462,1,200732,1,200032,1,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(100046,100351,1,201401,1,200612,1,200462,1,200732,1,200032,1,201691,1,200441,1,0,0,0,0);
INSERT INTO skill_set VALUES(400007,200012,1,200492,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(400012,200022,1,200612,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(400019,200012,1,200401,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(400028,200022,1,200352,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(400030,200012,1,200871,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(1100101,100011,1,200511,1,200612,1,200602,1,200032,1,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(1100102,100011,1,200511,1,200491,1,200602,1,200032,1,200132,1,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(1103401,200361,1,200461,1,202002,1,200172,1,200292,1,200752,1,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(1103501,100351,1,200282,1,200612,1,200462,1,200732,1,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(1106701,200361,1,201242,1,201262,1,201522,1,200352,1,200382,1,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10010103,100011,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10010104,100011,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10010105,100011,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10350101,10351,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10350102,10351,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10350103,100351,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10350104,100351,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(10350105,100351,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(11240103,110241,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(11240104,110241,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(11240105,110241,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32100101,100011,1,200511,1,200612,1,201402,1,200592,1,200342,1,200562,1,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32100102,100011,1,200511,1,200611,1,201401,1,200592,1,200342,1,200332,1,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32100106,100011,1,200511,1,201401,1,200592,1,200342,1,200332,1,200732,1,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32101301,200361,1,200562,1,200462,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32101304,200361,1,200462,1,200742,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32103502,100351,1,200591,1,201401,1,201392,1,200732,1,201422,1,200492,1,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32801301,200012,1,201352,1,200432,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32802801,200362,1,200992,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(32804001,200362,1,200622,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(51100132,100011,1,200511,1,200612,1,200732,1,200592,1,0,0,0,0,0,0,0,0,0,0);
INSERT INTO skill_set VALUES(51103531,100351,1,202081,1,201402,1,200612,1,200602,1,0,0,0,0,0,0,0,0,0,0);
+113
View File
@@ -0,0 +1,113 @@
CREATE TABLE IF NOT EXISTS 'text_data' ('id' INTEGER NOT NULL, 'category' INTEGER NOT NULL, 'index' INTEGER NOT NULL, 'text' TEXT NOT NULL, PRIMARY KEY('category','index'));
-- SELECT * FROM text_data WHERE category IN (147, 172) AND "index" in (101, 102, 103, 201, 202, 203, 1101, 1102, 1103, 1000101, 1000102, 1000103, 2000101, 2000102, 2000103, 3000101, 3000102, 3000103, 10010101, 10010102, 10010103);
INSERT INTO text_data VALUES(147,147,101,'Speed');
INSERT INTO text_data VALUES(147,147,102,'Speed');
INSERT INTO text_data VALUES(147,147,103,'Speed');
INSERT INTO text_data VALUES(147,147,201,'Stamina');
INSERT INTO text_data VALUES(147,147,202,'Stamina');
INSERT INTO text_data VALUES(147,147,203,'Stamina');
INSERT INTO text_data VALUES(147,147,1101,'Turf');
INSERT INTO text_data VALUES(147,147,1102,'Turf');
INSERT INTO text_data VALUES(147,147,1103,'Turf');
INSERT INTO text_data VALUES(147,147,1000101,'February S.');
INSERT INTO text_data VALUES(147,147,1000102,'February S.');
INSERT INTO text_data VALUES(147,147,1000103,'February S.');
INSERT INTO text_data VALUES(147,147,2000101,'Right-Handed ○');
INSERT INTO text_data VALUES(147,147,2000102,'Right-Handed ○');
INSERT INTO text_data VALUES(147,147,2000103,'Right-Handed ○');
INSERT INTO text_data VALUES(147,147,3000101,'URA Finale');
INSERT INTO text_data VALUES(147,147,3000102,'URA Finale');
INSERT INTO text_data VALUES(147,147,3000103,'URA Finale');
INSERT INTO text_data VALUES(147,147,10010101,'Shooting Star');
INSERT INTO text_data VALUES(147,147,10010102,'Shooting Star');
INSERT INTO text_data VALUES(147,147,10010103,'Shooting Star');
INSERT INTO text_data VALUES(172,172,101,'A Spark that increases Speed.');
INSERT INTO text_data VALUES(172,172,102,'A Spark that increases Speed.');
INSERT INTO text_data VALUES(172,172,103,'A Spark that increases Speed.');
INSERT INTO text_data VALUES(172,172,201,'A Spark that increases Stamina.');
INSERT INTO text_data VALUES(172,172,202,'A Spark that increases Stamina.');
INSERT INTO text_data VALUES(172,172,203,'A Spark that increases Stamina.');
INSERT INTO text_data VALUES(172,172,1101,'A Spark that increases Turf Aptitude.');
INSERT INTO text_data VALUES(172,172,1102,'A Spark that increases Turf Aptitude.');
INSERT INTO text_data VALUES(172,172,1103,'A Spark that increases Turf Aptitude.');
INSERT INTO text_data VALUES(172,172,1000101,'A Spark that increases Power and gives a skill hint for "Winter Runner ○".');
INSERT INTO text_data VALUES(172,172,1000102,'A Spark that increases Power and gives a skill hint for "Winter Runner ○".');
INSERT INTO text_data VALUES(172,172,1000103,'A Spark that increases Power and gives a skill hint for "Winter Runner ○".');
INSERT INTO text_data VALUES(172,172,2000101,'A Spark that gives a skill hint for "Right-Handed ○".');
INSERT INTO text_data VALUES(172,172,2000102,'A Spark that gives a skill hint for "Right-Handed ○".');
INSERT INTO text_data VALUES(172,172,2000103,'A Spark that gives a skill hint for "Right-Handed ○".');
INSERT INTO text_data VALUES(172,172,3000101,'A Spark that increases Speed and Stamina.');
INSERT INTO text_data VALUES(172,172,3000102,'A Spark that increases Speed and Stamina.');
INSERT INTO text_data VALUES(172,172,3000103,'A Spark that increases Speed and Stamina.');
INSERT INTO text_data VALUES(172,172,10010101,'A Spark that gives a skill hint for "Shooting Star".');
INSERT INTO text_data VALUES(172,172,10010102,'A Spark that gives a skill hint for "Shooting Star".');
INSERT INTO text_data VALUES(172,172,10010103,'A Spark that gives a skill hint for "Shooting Star".');
CREATE TABLE IF NOT EXISTS 'succession_factor' ('factor_id' INTEGER NOT NULL, 'factor_group_id' INTEGER NOT NULL, 'rarity' INTEGER NOT NULL, 'grade' INTEGER NOT NULL, 'factor_type' INTEGER NOT NULL, 'effect_group_id' INTEGER NOT NULL, 'start_date' INTEGER NOT NULL, 'end_date' INTEGER NOT NULL, PRIMARY KEY('factor_id'));
-- SELECT * FROM succession_factor WHERE factor_id IN (101, 102, 103, 201, 202, 203, 1101, 1102, 1103, 1000101, 1000102, 1000103, 2000101, 2000102, 2000103, 3000101, 3000102, 3000103, 10010101, 10010102, 10010103);
INSERT INTO succession_factor VALUES(101,1,1,1,1,11,1483228800,2524608000);
INSERT INTO succession_factor VALUES(102,1,2,1,1,12,1483228800,2524608000);
INSERT INTO succession_factor VALUES(103,1,3,1,1,13,1483228800,2524608000);
INSERT INTO succession_factor VALUES(201,2,1,1,1,11,1483228800,2524608000);
INSERT INTO succession_factor VALUES(202,2,2,1,1,12,1483228800,2524608000);
INSERT INTO succession_factor VALUES(203,2,3,1,1,13,1483228800,2524608000);
INSERT INTO succession_factor VALUES(1101,11,1,1,2,21,1483228800,2524608000);
INSERT INTO succession_factor VALUES(1102,11,2,1,2,22,1483228800,2524608000);
INSERT INTO succession_factor VALUES(1103,11,3,1,2,23,1483228800,2524608000);
INSERT INTO succession_factor VALUES(1000101,10001,1,1,5,51,1483228800,2524608000);
INSERT INTO succession_factor VALUES(1000102,10001,2,1,5,52,1483228800,2524608000);
INSERT INTO succession_factor VALUES(1000103,10001,3,1,5,53,1483228800,2524608000);
INSERT INTO succession_factor VALUES(2000101,20001,1,1,4,41,1483228800,2524608000);
INSERT INTO succession_factor VALUES(2000102,20001,2,1,4,42,1483228800,2524608000);
INSERT INTO succession_factor VALUES(2000103,20001,3,1,4,43,1483228800,2524608000);
INSERT INTO succession_factor VALUES(3000101,30001,1,1,6,51,1483228800,2524608000);
INSERT INTO succession_factor VALUES(3000102,30001,2,1,6,52,1483228800,2524608000);
INSERT INTO succession_factor VALUES(3000103,30001,3,1,6,53,1483228800,2524608000);
INSERT INTO succession_factor VALUES(10010101,100101,1,2,3,31,1483228800,2524608000);
INSERT INTO succession_factor VALUES(10010102,100101,2,2,3,32,1483228800,2524608000);
INSERT INTO succession_factor VALUES(10010103,100101,3,2,3,33,1483228800,2524608000);
CREATE TABLE IF NOT EXISTS 'succession_factor_effect' ('id' INTEGER NOT NULL, 'factor_group_id' INTEGER NOT NULL, 'effect_id' INTEGER NOT NULL, 'target_type' INTEGER NOT NULL, 'value_1' INTEGER NOT NULL, 'value_2' INTEGER NOT NULL, PRIMARY KEY('id'));
-- SELECT * FROM succession_factor_effect WHERE factor_group_id IN (SELECT factor_group_id FROM succession_factor WHERE factor_id IN (101, 102, 103, 201, 202, 203, 1101, 1102, 1103, 1000101, 1000102, 1000103, 2000101, 2000102, 2000103, 3000101, 3000102, 3000103, 10010101, 10010102, 10010103));
INSERT INTO succession_factor_effect VALUES(1,1,1,1,1,0);
INSERT INTO succession_factor_effect VALUES(2,1,2,1,4,0);
INSERT INTO succession_factor_effect VALUES(3,1,3,1,7,0);
INSERT INTO succession_factor_effect VALUES(4,1,4,1,10,0);
INSERT INTO succession_factor_effect VALUES(5,1,5,1,13,0);
INSERT INTO succession_factor_effect VALUES(6,1,6,1,16,0);
INSERT INTO succession_factor_effect VALUES(7,1,7,1,19,0);
INSERT INTO succession_factor_effect VALUES(8,1,8,1,22,0);
INSERT INTO succession_factor_effect VALUES(9,1,9,1,25,0);
INSERT INTO succession_factor_effect VALUES(10,1,10,1,28,0);
INSERT INTO succession_factor_effect VALUES(11,2,1,2,1,0);
INSERT INTO succession_factor_effect VALUES(12,2,2,2,4,0);
INSERT INTO succession_factor_effect VALUES(13,2,3,2,7,0);
INSERT INTO succession_factor_effect VALUES(14,2,4,2,10,0);
INSERT INTO succession_factor_effect VALUES(15,2,5,2,13,0);
INSERT INTO succession_factor_effect VALUES(16,2,6,2,16,0);
INSERT INTO succession_factor_effect VALUES(17,2,7,2,19,0);
INSERT INTO succession_factor_effect VALUES(18,2,8,2,22,0);
INSERT INTO succession_factor_effect VALUES(19,2,9,2,25,0);
INSERT INTO succession_factor_effect VALUES(20,2,10,2,28,0);
INSERT INTO succession_factor_effect VALUES(51,11,1,11,1,0);
INSERT INTO succession_factor_effect VALUES(52,11,2,11,2,0);
INSERT INTO succession_factor_effect VALUES(1144,10001,1,3,3,0);
INSERT INTO succession_factor_effect VALUES(1145,10001,1,41,200202,1);
INSERT INTO succession_factor_effect VALUES(1146,10001,2,3,6,0);
INSERT INTO succession_factor_effect VALUES(1147,10001,2,41,200202,1);
INSERT INTO succession_factor_effect VALUES(1148,10001,3,3,9,0);
INSERT INTO succession_factor_effect VALUES(1149,10001,3,41,200202,1);
INSERT INTO succession_factor_effect VALUES(344,20001,1,41,200012,1);
INSERT INTO succession_factor_effect VALUES(345,20001,2,41,200012,2);
INSERT INTO succession_factor_effect VALUES(346,20001,3,41,200012,3);
INSERT INTO succession_factor_effect VALUES(347,20001,4,41,200012,4);
INSERT INTO succession_factor_effect VALUES(348,20001,5,41,200012,5);
INSERT INTO succession_factor_effect VALUES(1324,30001,1,1,10,0);
INSERT INTO succession_factor_effect VALUES(1325,30001,1,2,10,0);
INSERT INTO succession_factor_effect VALUES(1326,30001,2,1,20,0);
INSERT INTO succession_factor_effect VALUES(1327,30001,2,2,20,0);
INSERT INTO succession_factor_effect VALUES(1328,30001,3,1,30,0);
INSERT INTO succession_factor_effect VALUES(1329,30001,3,2,30,0);
INSERT INTO succession_factor_effect VALUES(71,100101,1,41,900011,1);
INSERT INTO succession_factor_effect VALUES(73,100101,2,41,900011,2);
INSERT INTO succession_factor_effect VALUES(75,100101,3,41,900011,3);
+2 -2
View File
@@ -4,7 +4,7 @@
interface Props {
id: string;
characters: Character[];
characters: Character[] | null;
value: number;
class?: ClassValue | null;
optionClass?: ClassValue | null;
@@ -18,7 +18,7 @@
{#if !required}
<option value="0" class={optionClass}></option>
{/if}
{#each characters as c (c.chara_id)}
{#each characters ?? [] as c (c.chara_id)}
<option value={c.chara_id} class={optionClass}>{c.name}</option>
{/each}
</select>
+15 -17
View File
@@ -3,39 +3,37 @@
ABILITY_SCALE_NAME,
ABILITY_TYPE_FORMAT,
DURATION_SCALE_NAME,
skills,
Target,
TARGET_FORMAT,
tenThousandths,
ZERO_SKILL,
type Ability,
type Skill,
} from './data/skill';
interface CommonProps {
hint?: string;
interface Props {
skill: Skill | null | undefined;
mention?: boolean;
}
type Props = CommonProps & ({ skill: number; name?: never } | { name: string; skill?: never });
const ZERO_SKILL: Skill = {
skill_id: 0,
name: 'Skill data is loading...',
description: 'Skill data is still loading, or else something went wrong.',
group: 0,
rarity: 1,
group_rate: 1,
wit_check: false,
activations: [],
icon_id: 0,
};
let { hint, mention, skill, name }: Props = $props();
let { mention, skill }: Props = $props();
function splitCond(c: string): string[] {
return c.replaceAll('&', ' & ').split('@');
}
const s: Readonly<Skill> = $derived.by(() => {
const l =
skill != null ? skills.global.filter((s) => s.skill_id === skill) : skills.global.filter((s) => s.name.includes(name!));
if (name != null) {
console.warn(`skills specified as ${name} (${hint}):`, l);
}
if (l.length === 0) {
return ZERO_SKILL;
}
return l[0];
});
const s = $derived(skill ?? ZERO_SKILL);
const activationClass = $derived(s.activations.length === 1 ? null : 'block my-2 border rounded-md');
const spanClass = $derived(mention ? 'italic' : 'font-bold');
+8 -10
View File
@@ -1,19 +1,17 @@
<script lang="ts">
import { sparks } from '$lib/data/spark';
import { type Spark } from '$lib/data/spark';
interface Props {
spark: number;
region?: keyof typeof sparks;
spark: Spark | null | undefined;
}
const { spark, region = 'global' }: Props = $props();
const { spark }: Props = $props();
const cur = $derived(sparks[region].find((s) => s.spark_id === spark));
const curClass = $derived.by(() => {
if (cur == null) {
if (spark == null) {
return [];
}
switch (cur.type) {
switch (spark.type) {
case 1:
return ['stat'];
case 2:
@@ -32,17 +30,17 @@
return [];
}
});
const stars = $derived('★'.repeat(cur?.rarity ?? 0));
const stars = $derived('★'.repeat(spark?.rarity ?? 0));
</script>
{#if cur != null}
{#if spark != null}
<div
class={[
curClass,
'spark mx-1 flex items-center rounded-xl px-2 py-1 transition ease-in hover:shadow-md hover:ease-out motion-safe:duration-75 motion-safe:hover:-translate-y-0.5 dark:shadow-neutral-950',
]}
>
<span>{cur.name}</span>
<span>{spark.name}</span>
<span class="ml-2 text-xl text-amber-800 text-shadow-md dark:text-amber-300">{stars}</span>
</div>
{/if}
+4 -5
View File
@@ -1,5 +1,3 @@
import globalJSON from '../../../../global/affinity.json';
/**
* Precomputed character pair and trio affinity.
*/
@@ -24,9 +22,10 @@ export interface Affinity {
affinity: number;
}
export const affinity = {
global: globalJSON as Affinity[],
} as const;
export async function affinity(): Promise<Affinity[]> {
const resp = await fetch('/api/global/affinity');
return resp.json();
}
export function lookup(aff: Affinity[], chara_a: number, chara_b: number, chara_c?: number): number {
const [a, b, c] = [chara_a, chara_b, chara_c ?? Infinity].sort((a, b) => a - b);
+7 -8
View File
@@ -1,5 +1,4 @@
import type { RegionalName } from '$lib/regional-name';
import globalJSON from '../../../../global/character.json';
/**
* Character definitions.
@@ -16,11 +15,11 @@ export interface Character {
name: string;
}
export const character = {
global: globalJSON as Character[],
};
export async function character(): Promise<Character[]> {
const resp = await fetch('/api/global/character');
return resp.json();
}
export const charaNames = globalJSON.reduce(
(m, c) => m.set(c.chara_id, { en: c.name }),
new Map<Character['chara_id'], RegionalName>(),
);
export function charaNames(charas: Character[]) {
return charas.reduce((m, c) => m.set(c.chara_id, { en: c.name }), new Map<number, RegionalName>());
}
+18 -22
View File
@@ -1,5 +1,4 @@
import type { RegionalName } from '$lib/regional-name';
import globalJSON from '../../../../global/conversation.json';
/**
* Lobby conversation data.
@@ -17,10 +16,6 @@ export interface Conversation {
* Location ID of the conversation.
*/
location: 110 | 120 | 130 | 210 | 220 | 310 | 410 | 420 | 430 | 510 | 520 | 530;
/**
* English name of the location, for convenience.
*/
location_name: string;
/**
* First character in the conversation.
* Not necessarily equal to chara_id.
@@ -40,16 +35,17 @@ export interface Conversation {
condition_type: 0 | 1 | 2 | 3 | 4;
}
export const conversation = {
global: globalJSON as Conversation[],
};
export async function conversation(): Promise<Conversation[]> {
const resp = await fetch('/api/global/conversation');
return resp.json();
}
export const byChara = {
global: globalJSON.reduce(
export function byChara(convos: Conversation[]) {
return convos.reduce(
(m, c) => m.set(c.chara_id, (m.get(c.chara_id) ?? []).concat(c as Conversation)),
new Map<Conversation['chara_id'], Conversation[]>(),
),
};
new Map<number, Conversation[]>(),
);
}
export const locations: Record<Conversation['location'], { name: RegionalName; group: 1 | 2 | 3 | 4 | 5 }> = {
110: { name: { en: 'right side front' }, group: 1 },
@@ -74,12 +70,12 @@ function locCharas(convos: Conversation[], locGroup: 1 | 2 | 3 | 4 | 5) {
return [...m].toSorted((a, b) => b[1] - a[1]); // descending
}
export const groupPopulars = {
global: {
1: locCharas(conversation.global, 1),
2: locCharas(conversation.global, 2),
3: locCharas(conversation.global, 3),
4: locCharas(conversation.global, 4),
5: locCharas(conversation.global, 5),
},
};
export function groupPopulars(convos: Conversation[]) {
return {
1: locCharas(convos, 1),
2: locCharas(convos, 2),
3: locCharas(convos, 3),
4: locCharas(convos, 4),
5: locCharas(convos, 5),
};
}
+8 -21
View File
@@ -1,6 +1,3 @@
import skillGlobal from '../../../../global/skill.json';
import groupGlobal from '../../../../global/skill-group.json';
/**
* Skill data.
*/
@@ -323,22 +320,12 @@ export interface SkillGroup {
skill_bad?: number;
}
export const skills = {
global: skillGlobal as Skill[],
} as const;
export async function skills(): Promise<Skill[]> {
const resp = await fetch('/api/global/skill');
return resp.json();
}
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;
export async function skillGroups(): Promise<SkillGroup[]> {
const resp = await fetch('/api/global/skill-group');
return resp.json();
}
+4 -5
View File
@@ -1,5 +1,3 @@
import globalJSON from '../../../../global/spark.json';
/**
* Sparks, or succession factors.
*/
@@ -48,6 +46,7 @@ export interface SparkEffect {
value2: number;
}
export const sparks = {
global: globalJSON as Spark[],
} as const;
export async function sparks(): Promise<Spark[]> {
const resp = await fetch('/api/global/spark');
return resp.json();
}
+4 -5
View File
@@ -1,5 +1,3 @@
import globalJSON from '../../../../global/uma.json';
/**
* Uma or character card definitions.
*/
@@ -70,6 +68,7 @@ export interface Uma {
export type AptitudeLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
export const uma = {
global: globalJSON as Uma[],
};
export async function uma(): Promise<Uma[]> {
const resp = await fetch('/api/global/uma');
return resp.json();
}
+55 -38
View File
@@ -1,30 +1,42 @@
<script lang="ts">
import { character, charaNames } from '$lib/data/character';
import { byChara, locations, groupPopulars } from '$lib/data/convo';
import { character, charaNames, type Character } from '$lib/data/character';
import { byChara, locations, groupPopulars, conversation, type Conversation } from '$lib/data/convo';
import CharaPick from '$lib/CharaPick.svelte';
import { onMount } from 'svelte';
const characters = character.global.filter((c) => byChara.global.has(c.chara_id));
let conversations: Conversation[] = $state([]);
const convoMap = $derived(byChara(conversations));
const popular = $derived(groupPopulars(conversations));
let characters: Character[] = $state([]);
const names = $derived(charaNames(characters ?? []));
const minSuggest = 8;
onMount(async () => {
const [convos, charas] = await Promise.all([conversation(), character()]);
conversations = convos;
characters = charas.filter((c) => convoMap.has(c.chara_id));
});
let charaID = $state(1001);
let convo = $state(1);
const options = $derived(byChara.global.get(charaID) ?? []);
const options = $derived(convoMap.get(charaID) ?? []);
const cur = $derived(options.find((c) => c.number === convo));
const cur1Name = $derived(cur?.chara_1 && charaNames.get(cur.chara_1)?.en);
const cur2Name = $derived(cur?.chara_2 && charaNames.get(cur.chara_2)?.en);
const cur3Name = $derived(cur?.chara_3 && charaNames.get(cur.chara_3)?.en);
const cur1Name = $derived(cur?.chara_1 && names.get(cur.chara_1)?.en);
const cur2Name = $derived(cur?.chara_2 && names.get(cur.chara_2)?.en);
const cur3Name = $derived(cur?.chara_3 && names.get(cur.chara_3)?.en);
const alone = $derived([cur?.chara_1, cur?.chara_2, cur?.chara_3].filter((x) => x != null).length == 1 ? ' alone' : '');
const suggested = $derived.by(() => {
const suggested = $derived.by(async () => {
if (cur == null) {
return [];
}
const u = groupPopulars.global[locations[cur.location].group].filter(
(s) => charaNames.get(s[0]) != null && s[0] !== cur.chara_1 && s[0] !== cur.chara_2 && s[0] !== cur.chara_3,
const u = popular[locations[cur.location].group].filter(
(s) => names.get(s[0]) != null && s[0] !== cur.chara_1 && s[0] !== cur.chara_2 && s[0] !== cur.chara_3,
);
const r = u.length <= minSuggest ? u : u.filter((s) => s[1] >= u[minSuggest][1]);
return r.map(([chara_id, count]) => ({ chara_id, count }));
return r.map(([chara_id, count]) => ({ chara_id, chara_name: names.get(chara_id), count }));
});
const curLoc = $derived(cur != null ? locations[cur.location] : null);
</script>
<h1 class="text-4xl">Lobby Conversations</h1>
@@ -42,32 +54,37 @@
</select>
</div>
</div>
{#if cur}
<div class="shadow-sm transition-shadow hover:shadow-md">
<div class="mt-8 flex text-center text-lg">
<span class="flex-1">{cur1Name}{alone}</span>
{#if cur2Name}
<span class="flex-1">{cur2Name}</span>
{/if}
{#if cur3Name}
<span class="flex-1">{cur3Name}</span>
{/if}
{#await suggested}
<div class="mt-4 w-full text-center text-3xl">Loading...</div>
{:then suggested}
{#if cur}
<div class="shadow-sm transition-shadow hover:shadow-md">
<div class="mt-8 flex text-center text-lg">
<span class="flex-1">{cur1Name}{alone}</span>
{#if cur2Name}
<span class="flex-1">{cur2Name}</span>
{/if}
{#if cur3Name}
<span class="flex-1">{cur3Name}</span>
{/if}
</div>
<div class="flex w-full text-center text-lg">
<span class="flex-1">at {curLoc?.name.en}</span>
</div>
</div>
<div class="flex w-full text-center text-lg">
<span class="flex-1">at {locations[cur.location].name.en}</span>
<div class="mt-4 block text-center">
<span>Other characters who appear here most often:</span>
</div>
</div>
<div class="mt-4 block text-center">
<span>Other characters who appear here most often:</span>
</div>
<div class="mt-4 grid text-center shadow-sm transition-shadow ease-in hover:shadow-md hover:ease-out md:grid-cols-4">
{#each suggested as s (s.chara_id)}
<span>{charaNames.get(s.chara_id)?.en}: {s.count}&#xd7;</span>
{/each}
</div>
<div class="mt-4 block text-center">
<span>
Set these characters to fixed positions (main, upgrades, story, races) to maximize the chance of getting this conversation.
</span>
</div>
{/if}
<div class="mt-4 grid text-center shadow-sm transition-shadow ease-in hover:shadow-md hover:ease-out md:grid-cols-4">
{#each suggested as s (s.chara_id)}
<span>{s.chara_name?.en}: {s.count}&#xd7;</span>
{/each}
</div>
<div class="mt-4 block text-center">
<span>
Set these characters to fixed positions (main, upgrades, story, races) to maximize the chance of getting this
conversation.
</span>
</div>
{/if}
{/await}
+2 -2
View File
@@ -20,13 +20,13 @@
tocViewed = true;
obs.disconnect();
}
}
};
$effect(() => {
if (tocElem == null) {
return;
}
const obs = new IntersectionObserver(observeTOC, {rootMargin: "20% 0px"});
const obs = new IntersectionObserver(observeTOC, { rootMargin: '20% 0px' });
obs.observe(tocElem);
});
+229 -185
View File
@@ -20,12 +20,93 @@
} from '$lib/race';
import Skill from '$lib/Skill.svelte';
import StatChart from '$lib/StatChart.svelte';
import { onMount } from 'svelte';
import Article from '../Article.svelte';
import Sec from '../Sec.svelte';
import * as skill from '$lib/data/skill';
let raceLen = $state(2000);
let secSpeedStyle = $state(RunningStyle.FrontRunner);
let allSkillsList: skill.Skill[] = $state([]);
onMount(async () => {
allSkillsList = await skill.skills();
});
const allSkills = $derived(allSkillsList.reduce((m, s) => m.set(s.skill_id, s), new Map<number, skill.Skill>()));
const skills = $derived({
view: allSkills.get(100021),
sfv: allSkills.get(100101),
uma2: allSkills.get(100321),
falcoUlt: allSkills.get(100461),
palmerUlt: allSkills.get(100641),
kfc: allSkills.get(110041),
cacao: allSkills.get(110261),
right: allSkills.get(200012),
left: allSkills.get(200022),
firm: allSkills.get(200152),
compSpirit: allSkills.get(200282),
professor: allSkills.get(200331),
cornerAdept: allSkills.get(200332),
cornerConn: allSkills.get(200341),
rushingGale: allSkills.get(200371),
conc: allSkills.get(200431),
focus: allSkills.get(200432),
centerStage: allSkills.get(200451),
pp: allSkills.get(200452),
ramp: allSkills.get(200462),
nsm: allSkills.get(200491),
nn: allSkills.get(200492),
homestretchHaste: allSkills.get(200512),
ttl: allSkills.get(200531),
earlyLead: allSkills.get(200532),
escapeArtist: allSkills.get(200541),
fastPaced: allSkills.get(200542),
unrestrained: allSkills.get(200551),
encroaching: allSkills.get(200641),
spurt: allSkills.get(200642),
turboSprint: allSkills.get(200651),
tunes: allSkills.get(200721),
greed: allSkills.get(201081),
speedEater: allSkills.get(201082),
siphon: allSkills.get(201221),
murmurGold: allSkills.get(201161),
longStraights: allSkills.get(201172),
blast: allSkills.get(201173),
straights: allSkills.get(201242),
corners: allSkills.get(201252),
sixthSense: allSkills.get(201261),
dd: allSkills.get(201262),
topRunner: allSkills.get(201271),
leadersPride: allSkills.get(201272),
moxie: allSkills.get(201282),
eyes: allSkills.get(201441),
savvy: allSkills.get(201522),
lucky7: allSkills.get(201562),
gw: allSkills.get(201601),
thh: allSkills.get(201611),
loneWolf: allSkills.get(201641),
slipstream: allSkills.get(201651),
pto: allSkills.get(201661),
risky: allSkills.get(202032),
runaway: allSkills.get(202051),
burningWIT: allSkills.get(210051),
ignitedWIT: allSkills.get(210052),
radiant: allSkills.get(210061),
redshiftInherit: allSkills.get(900041),
pulseInherit: allSkills.get(900061),
anglingInherit: allSkills.get(900201),
pumpInherit: allSkills.get(900271),
inesInherit: allSkills.get(900311),
uma2Inherit: allSkills.get(900321),
beyondInherit: allSkills.get(900591),
vcInherit: allSkills.get(900681),
pastaInherit: allSkills.get(900141),
kfcInherit: allSkills.get(910041),
barcaroleInherit: allSkills.get(910151),
cacaoInherit: allSkills.get(910261),
mummyCreekInherit: allSkills.get(910451),
});
function mean2(x: [number, number]): number {
return (x[0] + x[1]) * 0.5;
}
@@ -193,7 +274,7 @@
At the start of late race, if they have enough HP remaining for their last spurt, horses accelerate from the mid race base
target speed to their spurt speed, which varies by speed stat, distance aptitude, running style, race distance, and guts stat,
in decreasing order of effect. "Last spurt" and "last spurt phase" are different and unrelated things; the latter is only used
mechanically in the condition for <Skill skill={200512} hint="homestretch haste" mention />.
mechanically in the condition for <Skill skill={skills.homestretchHaste} mention />.
</p>
<p>
Speed skills add a flat amount of target speed, generally +0.15 m/s for white skills, +0.25 m/s for double circle skills and
@@ -204,8 +285,8 @@
<Sec h={3} id="runaway">Runaway</Sec>
<p>
The skill <Skill skill={202051} hint="runaway" /> converts front runners into the <i>Great Escape</i> running style. However,
no player has ever uttered the words "Great Escape" when talking about Umamusume, presumably because Runaway is a much cooler
The skill <Skill skill={skills.runaway} /> converts front runners into the <i>Great Escape</i> running style. However, no
player has ever uttered the words "Great Escape" when talking about Umamusume, presumably because Runaway is a much cooler
name. ("Great Escape" is a direct translation of Japanese 大逃げ <i>oonige</i>, whereas "Front Runner" is a more liberal
localization of 逃げ <i>nige</i> that technically just means "escape.")
</p>
@@ -239,32 +320,31 @@
</p>
<ul class="mb-4 list-disc pl-4">
<li>
<Skill skill={900201} hint="angling" />, sometimes called Rod, is currently the second best skill in the game. Its condition
<Skill skill={skills.anglingInherit} />, sometimes called Rod, is currently the second best skill in the game. Its condition
is to be in first place on any late race corner, which is the case immediately at the start of late race on all medium
tracks,
<a href="#niigata-1600">all but one mile</a>, and some sprints.
</li>
<li>
On long distance tracks, <Skill skill={900681} hint="vc" /> takes that role instead. The front two horses get it, and it has half
On long distance tracks, <Skill skill={skills.vcInherit} /> takes that role instead. The front two horses get it, and it has half
the acceleration value.
</li>
<li>
On those sprints where Angling is dead, the front-specific option is <Skill skill={900141} hint="pasta" /> (VPP, or Pasta). Multi-front
builds also have access to <Skill skill={910451} hint="mummy creek" /> (HCreek). It takes both of them to equal Angling, so such
sprints may be better served gambling on <Skill skill={200651} hint="turbo sprint" mention />, <Skill
skill={200371}
hint="rushing gale"
On those sprints where Angling is dead, the front-specific option is <Skill skill={skills.pastaInherit} /> (VPP, or Pasta). Multi-front
builds also have access to <Skill skill={skills.mummyCreekInherit} /> (HCreek). It takes both of them to equal Angling, so such
sprints may be better served gambling on <Skill skill={skills.turboSprint} mention />, <Skill
skill={skills.rushingGale}
mention
/>, and possibly <Skill skill={200551} hint="unrestrained" mention /> instead. Front runners are especially strong on sprints
for <a href="#spot-struggle">other reasons</a> anyway.
/>, and possibly <Skill skill={skills.unrestrained} mention /> instead. Front runners are especially strong on sprints for
<a href="#spot-struggle">other reasons</a> anyway.
</li>
</ul>
<p>
<Skill skill={200491} hint="nsm" /> is the best skill in the game. Unfortunately, for the most part, it's bad on front runners;
generally not a win condition. Activating NSM requires not being in first, which means whoever <i>was</i> used Angling and is
pulling away from you before you accumulate the blocked time to activate it. That said, on VC tracks specifically, NSM or its
white version <Skill skill={200492} hint="nn" mention /> can be an option for multi-front builds, since the two frontmost horses
get VC and final corner lane movement hasn't happened.
<Skill skill={skills.nsm} /> is the best skill in the game. Unfortunately, for the most part, it's bad on front runners; generally
not a win condition. Activating NSM requires not being in first, which means whoever <i>was</i> used Angling and is pulling
away from you before you accumulate the blocked time to activate it. That said, on VC tracks specifically, NSM or its white
version <Skill skill={skills.nn} mention /> can be an option for multi-front builds, since the two frontmost horses get VC and final
corner lane movement hasn't happened.
</p>
<Sec h={2} id="positioning">Positioning Mechanics</Sec>
@@ -357,14 +437,13 @@
/>
</div>
<p>
Front runners have access to the skill <Skill skill={201262} hint="dd" />, which forces a horse who uses it to move outward to
a specific distance from the rail. DD almost always ends shortly before the horse has finished accelerating to early race
speed, so it does not convert the move lane speed modifier into distance.
Front runners have access to the skill <Skill skill={skills.dd} />, which forces a horse who uses it to move outward to a
specific distance from the rail. DD almost always ends shortly before the horse has finished accelerating to early race speed,
so it does not convert the move lane speed modifier into distance.
</p>
<p>
We get advantage from move lane speed modifier by following DD with <Skill skill={200452} hint="pp" /> or <Skill
skill={210052}
hint="ignited wit"
We get advantage from move lane speed modifier by following DD with <Skill skill={skills.pp} /> or <Skill
skill={skills.ignitedWIT}
/>. DD created an opportunity for those return skills to convert into huge forward speed. This setup is called
<i>lane combo</i>.
</p>
@@ -379,13 +458,12 @@
an efficient pickup as a short burst of high speed to gain position.
</p>
<p>
The gold versions of lane combo skills &ndash; <Skill skill={201261} hint="gold dd" mention />, <Skill
skill={200451}
hint="gold pp"
The gold versions of lane combo skills &ndash; <Skill skill={skills.sixthSense} mention />, <Skill
skill={skills.centerStage}
mention
/>, <Skill skill={210051} hint="burning wit" mention /> &ndash; are excellent to take on parents, but they generally make lane combo
itself less effective. They have stronger lane change movement boosts, which does not affect the forward speed boost and is likely
to make it last a shorter time, since the horse will return to the rail more quickly.
/>, <Skill skill={skills.burningWIT} mention /> &ndash; are excellent to take on parents, but they generally make lane combo itself
less effective. They have stronger lane change movement boosts, which does not affect the forward speed boost and is likely to make
it last a shorter time, since the horse will return to the rail more quickly.
</p>
<p>
For the same reasons, Ignited WIT is a bit stronger than PP, because it has a <i>smaller</i> effect value and also a longer base
@@ -509,9 +587,9 @@
<p>
For the most part, there's nothing you can do about the no-overtake zone. The exception to this is with Smart Falcon, who has
a speed unique that can fire on any mid race straight. On Tokyo 1600 (both turf and dirt), the first corner is far enough into
mid race that <Skill skill={100461} hint="falco ult" /> is likely to fire before entering the no-overtake zone, helping to propel
her into a lead that other front runners can't challenge until the corner begins &ndash; which is especially strong because it's
harder to pass on corners, and on those tracks, that corner lasts all the way into late race.
mid race that <Skill skill={skills.falcoUlt} /> is likely to fire before entering the no-overtake zone, helping to propel her into
a lead that other front runners can't challenge until the corner begins &ndash; which is especially strong because it's harder to
pass on corners, and on those tracks, that corner lasts all the way into late race.
</p>
<Sec h={2} id="skills">Skills</Sec>
@@ -522,37 +600,34 @@
</p>
<ul class="mb-4 list-disc pl-4">
<li>
On miles, <Skill skill={201082} hint="speed eater" /> is the strongest speed skill in the game, because it is both a full strength
speed skill (i.e. +0.15 target speed for base 3s) and a full strength speed debuff. It plays both offense and defense simultaneously.
On miles, <Skill skill={skills.speedEater} /> is the strongest speed skill in the game, because it is both a full strength speed
skill (i.e. +0.15 target speed for base 3s) and a full strength speed debuff. It plays both offense and defense simultaneously.
Every front runner should have it on every mile. Speed Eater technically has a gold version, <Skill
skill={201081}
hint="gold speed eater"
skill={skills.greed}
mention
/>, but Cygames apparently recognized that it must not ever be allowed to exist, because there's still no source of it on
JP.
</li>
<li>
<Skill skill={201611} hint="thh" /> is a full strength speed skill that is triggered by other skills, so it excels at stacking
&ndash; triggering THH with another speed skill is very likely to secure a pass.
<Skill skill={skills.thh} /> is a full strength speed skill that is triggered by other skills, so it excels at stacking &ndash;
triggering THH with another speed skill is very likely to secure a pass.
</li>
<li>
<Skill skill={200462} hint="ramp" /> is only a half strength speed skill (base 1.8s), but it fires upon overtake, which helps
to turn that into a complete pass.
<Skill skill={skills.ramp} /> is only a half strength speed skill (base 1.8s), but it fires upon overtake, which helps to turn
that into a complete pass.
</li>
<li>
<Skill skill={201661} hint="pto" /> and <Skill skill={201651} hint="slipstream" /> are mechanically similar full-strength speed
skills with cooldowns, so they can fire multiple times per race. Slipstream requires not being in first, and it's always wasted
if it triggers during the <a href="#no-zone">no-overtake zone</a>, so it's marginally weaker. Both want multi-front builds.
<Skill skill={skills.pto} /> and <Skill skill={skills.slipstream} /> are mechanically similar full-strength speed skills with
cooldowns, so they can fire multiple times per race. Slipstream requires not being in first, and it's always wasted if it triggers
during the <a href="#no-zone">no-overtake zone</a>, so it's marginally weaker. Both want multi-front builds.
</li>
</ul>
<Sec h={3} id="gate-skills">Gate Skills</Sec>
<p>
Gate skills are <Skill skill={201601} hint="gw" /> (GW), <Skill skill={200532} hint="early lead" />, and <Skill
skill={200431}
hint="conc"
/> (Conc), as well as all green skills including <Skill skill={202051} hint="runaway" mention /> (but excluding <Skill
skill={201562}
hint="lucky 7"
Gate skills are <Skill skill={skills.gw} /> (GW), <Skill skill={skills.earlyLead} />, and <Skill skill={skills.conc} /> (Conc),
as well as all green skills including <Skill skill={skills.runaway} mention /> (but excluding <Skill
skill={skills.lucky7}
mention
/>). These skills activate the moment the race starts. Other running styles can largely ignore them, but for front runners,
they are critical.
@@ -574,33 +649,31 @@
/>
</div>
<p>
GW must be combined with <Skill skill={200532} hint="el" /> if you want any chance of being first out of early race. The gold version
of EL, <Skill skill={200531} hint="ttl" />, is highly accessible as the skill from the Mihono Bourbon Wit event SSR. In
practice, I've found it adds fewer lengths over the white, and hence less positioning ability, than a mid race gold speed
skill. It is still absolutely <i>good</i>, and Bourbon Wit is a very usable card (treat her as a speed card that gives extra
energy on wit), but I don't consider it mandatory anymore.
GW must be combined with <Skill skill={skills.earlyLead} /> if you want any chance of being first out of early race. The gold version
of EL, <Skill skill={skills.ttl} />, is highly accessible as the skill from the Mihono Bourbon Wit event SSR. In practice,
I've found it adds fewer lengths over the white, and hence less positioning ability, than a mid race gold speed skill. It is
still absolutely <i>good</i>, and Bourbon Wit is a very usable card (treat her as a speed card that gives extra energy on
wit), but I don't consider it mandatory anymore.
</p>
<p>
Conc is less critical. It's worth taking on horses who have it, but it isn't worth using support card slots just to get it. On
the other hand, its white version, <Skill skill={200432} hint="focus" />, is bad; its only real use is as a backup gate skill
for GW when you don't have enough greens available.
the other hand, its white version, <Skill skill={skills.focus} />, is bad; its only real use is as a backup gate skill for GW
when you don't have enough greens available.
</p>
<Sec h={3} id="spurt-skills">Spurt Skills</Sec>
<p>
Because they are post-Angling, skills that activate in the final spurt are typically less interesting to front runners.
Notable exceptions are <Skill skill={910151} hint="barcarole" /> and, on Tokyo turf, <Skill
skill={900311}
hint="ines inherit"
Notable exceptions are <Skill skill={skills.barcaroleInherit} /> and, on Tokyo turf, <Skill
skill={skills.inesInherit}
/><!-- skill name ends with ! -->
These are good inherits for those who don't have easy access to <Skill skill={910261} hint="cacao" mention /> and <Skill
skill={910041}
hint="kfc"
These are good inherits for those who don't have easy access to <Skill skill={skills.cacaoInherit} mention /> and <Skill
skill={skills.kfcInherit}
mention
/>.
</p>
<p>
<Skill skill={900061} hint="pulse" /> is another spurt skill that may come up sometimes and is interesting to think about. It's
<Skill skill={skills.pulseInherit} /> is another spurt skill that may come up sometimes and is interesting to think about. It's
a 0.25 speed skill that requires being in at best second place to activate. Is it worth it to take?
</p>
<p>
@@ -618,9 +691,8 @@
<Sec h={3} id="others-skills">Other Horses' Skills</Sec>
<p>There are two categories of other horses' skills to think about.</p>
<p>
The first is stamina debuffs: <Skill skill={201441} hint="eyes" />, <Skill skill={201161} hint="murmur" />, <Skill
skill={201221}
hint="siphon"
The first is stamina debuffs: <Skill skill={skills.eyes} />, <Skill skill={skills.murmurGold} />, <Skill
skill={skills.siphon}
/>. My conclusion is that Global doesn't understand how to build debuffers, so front runners can mostly ignore these &ndash;
assume one will hit you, but not three.
</p>
@@ -631,9 +703,9 @@
</p>
<p>
The other category of skills to think about is other horses' uniques.
<Skill skill={100101} hint="sfv" />, <Skill skill={100321} hint="u=ma2" />, and a number of others need tight positioning,
requiring something like 2-4 or 3-4 in CM. The number of front runners in the match can dictate whether it's ever possible for
those uniques to activate. This is the fundamental idea behind <a href="#triple-front">triple front</a> builds.
<Skill skill={skills.sfv} />, <Skill skill={skills.uma2} />, and a number of others need tight positioning, requiring
something like 2-4 or 3-4 in CM. The number of front runners in the match can dictate whether it's ever possible for those
uniques to activate. This is the fundamental idea behind <a href="#triple-front">triple front</a> builds.
</p>
<Sec h={3} id="skill-timing">Skill Timing</Sec>
@@ -692,11 +764,10 @@
the early and mid race skills you can manage.
<a href="#gate-skills">Gate skills</a> are especially important, because runaways have a tremendously higher early race target
speed. If there is a real difference between a correctly built runaway blocker and a runaway ace, it's that <Skill
skill={910261}
hint="cacao"
skill={skills.cacaoInherit}
mention
/>
and <Skill skill={910041} hint="kfc" mention /> are arguably more appropriate inherits than Angling, but the argument isn't strong
and <Skill skill={skills.kfcInherit} mention /> are arguably more appropriate inherits than Angling, but the argument isn't strong
if you're still able to hit a good stat line on your runaway.
</p>
<p>
@@ -729,21 +800,19 @@
<Sec h={3} id="long-double-front">Long Double Front</Sec>
<p>
At long distances, double front has different implications.
<Skill skill={900681} hint="vc" /> is the front runner win condition, and the front two horses both get it. With late race being
before the final corner and hence extra move lane not having started, you can take advantage of <Skill
skill={200492}
hint="nn"
/> to turn the <i>second place</i> front runner into the winner.
<Skill skill={skills.vcInherit} /> is the front runner win condition, and the front two horses both get it. With late race being
before the final corner and hence extra move lane not having started, you can take advantage of <Skill skill={skills.nn} /> to turn
the <i>second place</i> front runner into the winner.
</p>
<p>
Building for second place to bunny with NN does not mean manipulating stats and skills to force one of the front runners into
second. Both fronts need to be strong enough to be adjacent at late race start, and you have to beat other people's front
runners, too. (Especially since VC and <Skill skill={200641} hint="encroaching" mention /> are currently the only consistent accels
for long distance.) So, double front for long is ultimately about the same as double front for other distances, just with different
runners, too. (Especially since VC and <Skill skill={skills.encroaching} mention /> are currently the only consistent accels for
long distance.) So, double front for long is ultimately about the same as double front for other distances, just with different
goals when choosing the legacy.
</p>
<p>
Given that NN is good, it's also worth considering the gold version, <Skill skill={200491} hint="nsm" /><!-- ends with ! -->
Given that NN is good, it's also worth considering the gold version, <Skill skill={skills.nsm} /><!-- ends with ! -->
NSM is a better skill for the long double front build, but for now, the only way to get it while running MANT is from Yukino Bijin
Wit. That card's numbers are not great. It's still possible to get a good stat line using her, but it will take more careers to
get there than to just get NN from Fine Motion.
@@ -752,8 +821,7 @@
If you're feeling adventurous, runaway is another consideration for double (and triple) front on long distances. Your ace
(hopefully) still gets VC, but the runaway paces up the race. As mentioned in the <a href="#solo-front">solo front</a>
section, this especially punishes end closers, who tend to be popular on long tracks because of <Skill
skill={200642}
hint="straight spurt"
skill={skills.spurt}
mention
/>. It also keeps your ace in <a href="#front-modes">overtake mode</a>, which is better than speed-up mode, for position keep.
</p>
@@ -771,9 +839,8 @@
One type of support that all front runners do automatically is helping to kill position-based pace chaser skills. Such skills
have conditions that translate into needing to be in 3rd or 4th, or sometimes 2nd through 4th, to activate. When you're
bringing three front runners, if anyone else brings a single other one, they're going to occupy 2-4 naturally. If there happen
to be three others, then even late/end win cons like <Skill skill={900591} hint="beyond" mention /> and <Skill
skill={900271}
hint="pump"
to be three others, then even late/end win cons like <Skill skill={skills.beyondInherit} mention /> and <Skill
skill={skills.pumpInherit}
mention
/> are dead.
</p>
@@ -794,10 +861,9 @@
An SS support doesn't strictly need as much in the way of mid race skills, since her main job is to give the others overtake
mode and then be passed. It's nonetheless a good idea to take those skills for less risk if your other fronts have a poor
showing. In addition to the normal front win cons, it's potentially strong to take unconditional gambles like <Skill
skill={200341}
hint="corner conn"
skill={skills.cornerConn}
mention
/> and <Skill skill={210061} hint="radiant" mention />.
/> and <Skill skill={skills.radiant} mention />.
</p>
<p>
SS supports do best on medium and long tracks, because you want to be building <i>everyone</i> for spot struggle on miles and
@@ -808,11 +874,7 @@
<Sec h={3} id="chariot">Chariot</Sec>
<p>
A front runner build that I have seen but not attempted is the chariot: a runaway with a strong mid race but insufficient HP
to spurt stays in front of one or two other front runners who have <Skill
skill={200491}
hint="nsm"
mention
/><!-- ends with ! -->
to spurt stays in front of one or two other front runners who have <Skill skill={skills.nsm} mention /><!-- ends with ! -->
</p>
<p>
I saw a team using a chariot in CM13 round 2 day 2; that team had an overall eleven wins at the time. Maybe their horses just
@@ -828,7 +890,7 @@
<p>Most front runners enjoy easy careers thanks to strong kits and little chance to be blocked.</p>
<p>
Currently, this chapter is about MANT (a.k.a. Trackblazer). Unity Cup is still useful &ndash; obviously for runaways, but also
for spinning up an <Skill skill={210052} hint="ignited wit" mention /> legacy.
for spinning up an <Skill skill={skills.ignitedWIT} mention /> legacy.
</p>
<Sec h={3} id="support-cards">Support Cards</Sec>
@@ -838,47 +900,41 @@
</p>
<ul class="mb-4 list-disc pl-4">
<li>
Ines Fujin gives <Skill skill={201082} hint="speed eater" mention />, <Skill skill={201661} hint="pto" mention />, and <Skill
skill={201651}
hint="slipstream"
Ines Fujin gives <Skill skill={skills.speedEater} mention />, <Skill skill={skills.pto} mention />, and <Skill
skill={skills.slipstream}
mention
/>. Her guts SSR is the Kitasan Black of guts cards, with 15% training effectiveness and 80 specialty priority. She also has
a relatively new wit SR that is decently strong even at LB0, though still objectively beaten by Marv SR until MLB.
</li>
<li>
Marvelous Sunday, both power SSR and wit SR versions, give <Skill skill={200462} hint="ramp" mention /> and <Skill
skill={201611}
hint="thh"
Marvelous Sunday, both power SSR and wit SR versions, give <Skill skill={skills.ramp} mention /> and <Skill
skill={skills.thh}
mention
/> as hints. The SSR has 15% race bonus, while the SR has 10% race bonus and gives +3 hints. The numbers on both are otherwise
disappointing, but those elements alone are enough to justify using them.
</li>
<li>
Cards that give generic or distance-specific gold speed skills, especially mid race ones, are very valuable to front
runners. Since <Skill skill={200331} hint="professor" mention /> is strong, Kitasan Black is still around despite only 5% race
bonus. El Condor Pasa gives <Skill skill={200721} hint="tunes" mention />.
runners. Since <Skill skill={skills.professor} mention /> is strong, Kitasan Black is still around despite only 5% race bonus.
El Condor Pasa gives <Skill skill={skills.tunes} mention />.
</li>
<li>
For parent runs, Smart Falcon SSR is mandatory. She gives guaranteed <Skill skill={201601} hint="gw" mention /> in her chain,
and her gold skill is <Skill skill={200451} hint="gold pp" mention />, which you want for building a
For parent runs, Smart Falcon SSR is mandatory. She gives guaranteed <Skill skill={skills.gw} mention /> in her chain, and her
gold skill is <Skill skill={skills.centerStage} mention />, which you want for building a
<a href="#lane-combo">lane combo</a> legacy. On ace runs, she is acceptable if you own her at MLB, but you'd rather get her skills
from inheritance and use the card slot for a strong speed skill.
</li>
<li>
Seiun Sky SSR is another good parent card for parents. Her chain starts with <Skill skill={201262} hint="dd" mention /> and ends
with
<Skill skill={200541} hint="escape artist" mention /> (albeit agemasen). She also carries a <Skill
skill={201611}
hint="thh"
mention
/> hint. Unfortunately, between being a stamina card and not being Super Creek, she isn't viable for ace runs.
Seiun Sky SSR is another good parent card for parents. Her chain starts with <Skill skill={skills.dd} mention /> and ends with
<Skill skill={skills.escapeArtist} mention /> (albeit agemasen). She also carries a <Skill skill={skills.thh} mention /> hint.
Unfortunately, between being a stamina card and not being Super Creek, she isn't viable for ace runs.
</li>
<li>Other parenting cards include Kawakami Princess speed and Hishi Akebono guts, for gold versions of lane combo skills.</li>
</ul>
<p>
A future sight advisory: Maruzensky's speed SSR is coming in early July. At this point, you should probably be saving all your
carats for her. She is an extremely strong stat stick and gives <Skill skill={201271} hint="top runner" mention />, the gold
version of <Skill skill={201272} hint="leader's pride" mention />.
carats for her. She is an extremely strong stat stick and gives <Skill skill={skills.topRunner} mention />, the gold version
of <Skill skill={skills.leadersPride} mention />.
</p>
<Sec h={3} id="career-skills">Taking Skills</Sec>
@@ -890,38 +946,37 @@
your chance of getting skills you don't have hints for.
</p>
<p>
<Skill skill={200532} hint="early lead" /> is a snap take skill. The <i>only</i> time to sit on EL is when you happen to get
the first +1 hint the turn before inspiration. (Even then, I'd probably still take it, since there's still a race to run.)
Early Lead is one of the strongest skills in terms of lengths gained, it applies to all tracks and conditions, and
<Skill skill={skills.earlyLead} /> is a snap take skill. The <i>only</i> time to sit on EL is when you happen to get the first
+1 hint the turn before inspiration. (Even then, I'd probably still take it, since there's still a race to run.) Early Lead is
one of the strongest skills in terms of lengths gained, it applies to all tracks and conditions, and
<i>it saves late starts</i>, which are your only source of losses on most races after junior year. Moreover, it has a base
cost of only 120 SP; even if you do get Fast Learner after taking it, your opportunity cost was 12 SP. If you prune EL and a
hint lands on <Skill skill={200542} hint="fast-paced" mention /> or <Skill skill={201272} hint="leader's pride" mention /> instead,
you gave up that potential 12 SP to save 18. It's incredibly good to take early.
hint lands on <Skill skill={skills.fastPaced} mention /> or <Skill skill={skills.leadersPride} mention /> instead, you gave up that
potential 12 SP to save 18. It's incredibly good to take early.
</p>
<p>
On parent runs, or exactly one of your three CM horses, <Skill skill={201641} hint="lone wolf" /> is another snap take. Base cost
of 60 SP for +40 speed, which can secure a lot of races, especially early in career. Be extremely careful not to take it on multiple
horses on a team. Save and quit from the career if you need to check. It's technically better to have it on two horses than zero,
but it's tremendously better than that to have it on one.
On parent runs, or exactly one of your three CM horses, <Skill skill={skills.loneWolf} /> is another snap take. Base cost of 60
SP for +40 speed, which can secure a lot of races, especially early in career. Be extremely careful not to take it on multiple horses
on a team. Save and quit from the career if you need to check. It's technically better to have it on two horses than zero, but it's
tremendously better than that to have it on one.
</p>
<p>
<Skill skill={900201} hint="angling" /> is a strong consideration as a mid-career take. Inheritance events are more likely to activate
<Skill skill={skills.anglingInherit} /> is a strong consideration as a mid-career take. Inheritance events are more likely to activate
green sparks than white sparks, so the risk of missing out on SP by taking it early is higher. However, Angling is an almost automatic
win condition for career (outside of <a href="#3k">3Ks</a> and <a href="#niigata-1600">Niigata 1600</a>). Taking Angling early
can save a lot of clocks, and it can rescue runs that don't get what is normally the minimum speed to win races before summer.
</p>
<p>
I've had debate about this one, but I feel that <Skill skill={201522} hint="savvy" /> is a skill you will pretty much always want
at least the first level of. Wit is a strong stat for front runners, and Savvy is a guaranteed Groundwork trigger. It's also the
second cheapest front-specific skill, after Dodging Danger. On parent runs, it could arguably be worth sitting on it until the +2
or +3 hint, because taking the second level gives a slightly boosted chance to generate the spark, and hints save twice as much
SP on the double circle.
I've had debate about this one, but I feel that <Skill skill={skills.savvy} /> is a skill you will pretty much always want at least
the first level of. Wit is a strong stat for front runners, and Savvy is a guaranteed Groundwork trigger. It's also the second cheapest
front-specific skill, after Dodging Danger. On parent runs, it could arguably be worth sitting on it until the +2 or +3 hint, because
taking the second level gives a slightly boosted chance to generate the spark, and hints save twice as much SP on the double circle.
</p>
<p>
<Skill skill={201242} hint="front straights" /> and <Skill skill={201252} hint="front corners" /> are strong and cheap. If you've
taken Early Lead and Angling, they probably won't change the outcomes of any races, but it's still reasonable to take the first
level to prune. As a corollary, outside parent runs, you should have a specific distance in mind, so your distance straights/corners
should usually be even quicker takes.
<Skill skill={skills.straights} /> and <Skill skill={skills.corners} /> are strong and cheap. If you've taken Early Lead and Angling,
they probably won't change the outcomes of any races, but it's still reasonable to take the first level to prune. As a corollary,
outside parent runs, you should have a specific distance in mind, so your distance straights/corners should usually be even quicker
takes.
</p>
<Sec h={3} id="niigata-1600">Niigata Junior Stakes</Sec>
@@ -969,9 +1024,9 @@
tested much without them, because I don't like throwing away my runs.
</p>
<p>
<Skill skill={201282} hint="moxie" /> is the only front-specific recovery skill you can get from MANT rivals. It also is a guaranteed
option in Bourbon Wit's first chain event. That makes it pretty often available for the 3Ks. However, if you end up overstam at
the end of the run, buying Moxie can be as much as -162 SP, which is certainly not a trivial amount.
<Skill skill={skills.moxie} /> is the only front-specific recovery skill you can get from MANT rivals. It also is a guaranteed option
in Bourbon Wit's first chain event. That makes it pretty often available for the 3Ks. However, if you end up overstam at the end
of the run, buying Moxie can be as much as -162 SP, which is certainly not a trivial amount.
</p>
<p>
An alternative option to buying a recovery is to switch to Late Surger for those races. I've won Kikuka Sho with circa 300
@@ -1005,44 +1060,36 @@
<ul class="mb-4 list-disc pl-4">
<li>
Valentine's Mihono Bourbon (VBourbon) is the easiest font runner to train because she has <Skill
skill={201601}
hint="gw"
skill={skills.gw}
mention
/> built in, whereas most others have to get it from either inheritance or cards that aren't terribly strong. She is also the
strongest front runner, because <Skill skill={110261} hint="cacao" mention /> is full strength, mid race, extremely forgiving
in activation, and even has a heal. And she has <Skill skill={200431} hint="conc" mention /> and <Skill
skill={200541}
hint="escape artist"
mention
/> built in, both strong distance-agnostic front runner skills with awkward sources otherwise. And she's a great parent for all
the same reasons that her unique is strong (except that the heal becomes irrelevant).
strongest front runner, because <Skill skill={skills.cacao} mention /> is full strength, mid race, extremely forgiving in activation,
and even has a heal. And she has <Skill skill={skills.conc} mention /> and <Skill skill={skills.escapeArtist} mention /> built
in, both strong distance-agnostic front runner skills with awkward sources otherwise. And she's a great parent for all the same
reasons that her unique is strong (except that the heal becomes irrelevant).
</li>
<li>
Summer Maruzensky (SMaru or KFC) is just shy of VBourbon in strength with her unique, <Skill
skill={110041}
hint="kfc"
mention
/>. It uses a heal to trigger instead of having a heal built in, but that means it sometimes has carryover potential. Using
Summer Maruzensky (SMaru or KFC) is just shy of VBourbon in strength with her unique, <Skill skill={skills.kfc} mention />.
It uses a heal to trigger instead of having a heal built in, but that means it sometimes has carryover potential. Using
VBourbon as a parent for SMaru works as a trigger, too, which is especially good for shorter distances that don't want to
spend SP on pure recoveries.
</li>
<li>
Seiun Sky is the source of <Skill skill={900201} hint="angling" mention /> and therefore the best horse for front runner parenting
Seiun Sky is the source of <Skill skill={skills.anglingInherit} mention /> and therefore the best horse for front runner parenting
in the game, forever. As a runner, she is outshone by the former two and others who will come later, but she is an excellent choice
for a <a href="#double-front">second front runner</a> in a team comp.
</li>
<li>
Kitasan Black is the definitive front runner of long distance, both as a parent and as a competitor.
<Skill skill={201173} hint="blast" mention /> is the gold version of <Skill skill={201172} hint="long straights" mention />,
which is extremely strong to have built in (especially noting that hints on Long Straights take 27 SP off the price of
Blast).
<Skill skill={skills.blast} mention /> is the gold version of <Skill skill={skills.longStraights} mention />, which is
extremely strong to have built in (especially noting that hints on Long Straights take 27 SP off the price of Blast).
</li>
<li>
Silence Suzuka and Mejiro Palmer are currently the only runaways in the game. Both of them have uniques that are
inconsistent normally but very consistent as runaways:
<Skill skill={100021} hint="view" mention /> and <Skill skill={100641} hint="palmer" mention />. Outside of Team Trials,
runaways aren't very useful in the MANT era. (My Suzuka runaway remains my #1 top scorer in TT, though. Runaway and Conc are
both busted for points.)
<Skill skill={skills.view} mention /> and <Skill skill={skills.palmerUlt} mention />. Outside of Team Trials, runaways
aren't very useful in the MANT era. (My Suzuka runaway remains my #1 top scorer in TT, though. Runaway and Conc are both
busted for points.)
</li>
</ul>
<p>
@@ -1053,8 +1100,8 @@
<Sec h={3} id="coc">Christmas Oguri Cap</Sec>
<p>
As is perhaps expected, Christmas Oguri Cap (COC) is very strong as a front runner on specific tracks. The front-specific heal
is <Skill skill={201282} hint="moxie" mention />, which activates at the very start of the first uphill. When that first
uphill is at the start of late race, as in the case of Tokyo 1600 Dirt, COC is online as a front runner.
is <Skill skill={skills.moxie} mention />, which activates at the very start of the first uphill. When that first uphill is at
the start of late race, as in the case of Tokyo 1600 Dirt, COC is online as a front runner.
</p>
<p>
I've been told COC is very hard to train as a front runner. Something about aptitudes. I don't have any variety of Oguri, but
@@ -1062,8 +1109,7 @@
</p>
<p>
Another rare manifestation of Front COC is as a third place horse in a <a href="#triple-front">triple front</a> build where <Skill
skill={900321}
hint="u=ma2"
skill={skills.uma2Inherit}
mention
/> is strong. I convinced Werseter, the Umadump guy, to try this for CM12 after he accidentally made an 8★ front Tachyon parent.
He reported a 7% win rate for her. (But a much higher win rate overall &ndash; after all, he was using triple front.)
@@ -1111,37 +1157,36 @@
<p>Front runners are more reliant than other styles on getting certain skills from inspiration:</p>
<ul class="mb-4 list-disc pl-4">
<li>
Building a full <Skill skill={201601} hint="gw" /> legacy is the first step to front runner training. Fronts without it are non-competitive
Building a full <Skill skill={skills.gw} /> legacy is the first step to front runner training. Fronts without it are non-competitive
even as supports. The cards that give it aren't great for ace builds. There is no gold version of it yet, so getting it from inheritance
is truly ideal.
</li>
<li>
<Skill skill={200452} hint="pp" /> similarly doesn't come from good cards (since Sweep Tosho has fallen off). As a piece of
<Skill skill={skills.pp} /> similarly doesn't come from good cards (since Sweep Tosho has fallen off). As a piece of
<a href="#lane-combo">lane combo</a>, it isn't applicable to every race, but it's very strong when it is. The white skill is
better than the gold skill, so it is another ideal inherit.
</li>
<li>
<Skill skill={210052} hint="ignited wit" /> is the stronger lane combo piece. The only sources of it are inheritance and Unity
Cup; which is to say, the source of it is inheritance. When searching databases for borrows, looking for Ignited WIT is a shortcut
for listing good front runner legacies, since it's only valuable to fronts and somewhat hard to get even when you focus on it.
<Skill skill={skills.ignitedWIT} /> is the stronger lane combo piece. The only sources of it are inheritance and Unity Cup; which
is to say, the source of it is inheritance. When searching databases for borrows, looking for Ignited WIT is a shortcut for listing
good front runner legacies, since it's only valuable to fronts and somewhat hard to get even when you focus on it.
</li>
<li>
Almost all green skills are valuable to inherit at one point or another as GW triggers.
<Skill skill={200012} hint="right" /> and <Skill skill={200022} hint="left" /> are particularly good; one of the two is always
the best green available for a race.
<Skill skill={200152} hint="firm" /> is a bit less strong, and slightly superseded by the existence of Narita Top Road, but it
applies to the majority of CM races. Season greens are great, but a bit narrow; distance greens are good. Weather greens are acceptable,
<Skill skill={skills.right} /> and <Skill skill={skills.left} /> are particularly good; one of the two is always the best green
available for a race.
<Skill skill={skills.firm} /> is a bit less strong, and slightly superseded by the existence of Narita Top Road, but it applies
to the majority of CM races. Season greens are great, but a bit narrow; distance greens are good. Weather greens are acceptable,
but while guts is better on front runners than other styles, it isn't good.
<Skill skill={200282} hint="comp spirit" /> is good if you like to do <a href="#triple-front">triple front</a> builds,
because it activates when there are four of the runner's style in CM.
<Skill skill={201522} hint="savvy" /> is slightly less valuable as an inherit, since it can come from rivals.
<Skill skill={skills.compSpirit} /> is good if you like to do <a href="#triple-front">triple front</a> builds, because it
activates when there are four of the runner's style in CM.
<Skill skill={skills.savvy} /> is slightly less valuable as an inherit, since it can come from rivals.
</li>
<li>
All generic speed skills that aren't strictly late race are good inherits.
<Skill skill={202032} hint="risky" /> is the best one by ratio of strength to difficulty to acquire.
<Skill skill={201611} hint="thh" />, <Skill skill={201661} hint="pto" />, <Skill skill={200462} hint="ramp" />, and <Skill
skill={200332}
hint="corner adept"
<Skill skill={skills.risky} /> is the best one by ratio of strength to difficulty to acquire.
<Skill skill={skills.thh} />, <Skill skill={skills.pto} />, <Skill skill={skills.ramp} />, and <Skill
skill={skills.cornerAdept}
/> are also strong, but available as hints or chain skills from some usable-to-good cards.
</li>
<li>Obviously, getting front-runner-specific skills from inheritance makes them cheaper and more consistent to get.</li>
@@ -1300,7 +1345,7 @@
<Sec h={3} id="cm13">CM13 &ndash; Taurus Cup (Tokyo Derby)</Sec>
<p>
Maruzensky's unique, <Skill skill={900041} hint="redshift" mention />, is live for approximately everyone. Filling the ranks
Maruzensky's unique, <Skill skill={skills.redshiftInherit} mention />, is live for approximately everyone. Filling the ranks
with front runners should be a strong means to delay it for later positions, especially COC.
</p>
<p>
@@ -1425,8 +1470,8 @@
</p>
<p>As a sprint, this is also where spot struggle shines. Time for Unity Cup guts builds.</p>
<p>
I forgot that <Skill skill={200551} hint="unrestrained" mention /> requires being in first place, so I thought it would be reasonable
to base my team around gambling on it. That turned out not to be a great decision.
I forgot that <Skill skill={skills.unrestrained} mention /> requires being in first place, so I thought it would be reasonable to
base my team around gambling on it. That turned out not to be a great decision.
</p>
<ol class="mb-4 list-decimal pl-4">
<li>
@@ -1447,13 +1492,12 @@
</li>
</ol>
<p>
The plan worked exactly as I hoped, but what the plan lacked was <Skill skill={900141} hint="vpp" mention />. While I was
The plan worked exactly as I hoped, but what the plan lacked was <Skill skill={skills.pastaInherit} mention />. While I was
suppressing it on other teams' front runners with my outstanding mid race, they were also gambling on <Skill
skill={200651}
hint="gale"
skill={skills.turboSprint}
mention
/> and <Skill skill={200371} hint="turbo sprint" mention />, and I didn't have enough accel to beat them. I found a mention in
my chat history that, as of round 2 day 1, I had a 78% top two rate with a 32% win rate. Lessons learned. Still managed to get
2nd in the finals, and also my first ever group A round 2 sweep.
/> and <Skill skill={skills.rushingGale} mention />, and I didn't have enough accel to beat them. I found a mention in my chat
history that, as of round 2 day 1, I had a 78% top two rate with a 32% win rate. Lessons learned. Still managed to get 2nd in
the finals, and also my first ever group A round 2 sweep.
</p>
</Article>
+3
View File
@@ -12,6 +12,9 @@ const config = {
return isExternalLibrary ? undefined : true;
},
experimental: {
async: true,
},
},
kit: {
adapter: adapter(),
+8
View File
@@ -4,6 +4,7 @@ import { playwright } from '@vitest/browser-playwright';
import { sveltekit } from '@sveltejs/kit/vite';
export default defineConfig({
clearScreen: false,
plugins: [tailwindcss(), sveltekit()],
test: {
expect: { requireAssertions: true },
@@ -33,4 +34,11 @@ export default defineConfig({
},
],
},
server: {
proxy: {
'/api': {
target: 'http://localhost:13669',
},
},
},
});