Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a34b5aef4 | ||
|
|
0ffd2c099a | ||
|
|
6828b1bd88 |
@@ -28,6 +28,8 @@ type AffinityDetail struct {
|
||||
|
||||
// Conversation describes a lobby conversation.
|
||||
type Conversation struct {
|
||||
// ID is the conversation ID.
|
||||
ID int32 `json:"id"`
|
||||
// CharacterID is the ID of the character who has the conversation as
|
||||
// a gallery entry.
|
||||
CharacterID CharacterID `json:"chara_id"`
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"git.sunturtle.xyz/zephyr/horse"
|
||||
"git.sunturtle.xyz/zephyr/horse/umadump/ism"
|
||||
)
|
||||
|
||||
// For each gain type from inspiration (stats, caps, aptitudes, skills):
|
||||
// find all activated sparks that can give that gain type.
|
||||
// Then, find all subsets of effect groups among those sparks
|
||||
// that have effects summing to those observed
|
||||
// (or more if the .end_info result is maxed),
|
||||
// with the restriction that one comes from each spark.
|
||||
// Deduplicate permutations.
|
||||
// Lastly, find the intersection of possible effects from sparks that appear
|
||||
// multiple times.
|
||||
//
|
||||
// To some extent, we rely on there being no sparks with the random stats effect type,
|
||||
// although we also don't know the distribution of stats from overcapped skill hints.
|
||||
|
||||
type target struct {
|
||||
spark *horse.Spark
|
||||
group int
|
||||
effect horse.SparkEffect
|
||||
}
|
||||
|
||||
func skilltargets(sparks []horse.Spark) map[horse.SkillID]map[horse.SparkID][]target {
|
||||
r := make(map[horse.SkillID]map[horse.SparkID][]target)
|
||||
for i, sp := range sparks {
|
||||
for j, g := range sp.Effects {
|
||||
for _, e := range g {
|
||||
if e.Target != horse.SparkSkillHint {
|
||||
continue
|
||||
}
|
||||
if r[horse.SkillID(e.Value1)] == nil {
|
||||
r[horse.SkillID(e.Value1)] = make(map[horse.SparkID][]target)
|
||||
}
|
||||
r[horse.SkillID(e.Value1)][sp.ID] = append(r[horse.SkillID(e.Value1)][sp.ID], target{&sparks[i], j, e})
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func resolveSkills(tab tables, career *ism.WorkIdleSingleMode)
|
||||
|
||||
// skillsparks appends all activated sparks that can yield a given skill to r.
|
||||
// It assumes that no spark can give multiple skills.
|
||||
func skillsparks(tab tables, sparks []ism.Factor, id horse.SkillID, r []*horse.Spark) []*horse.Spark {
|
||||
for _, f := range sparks {
|
||||
sp := tab.sparks[f.FactorID]
|
||||
if sp == nil {
|
||||
// ATM we do generally exclude Carnival Bonus from spark info.
|
||||
// Better to check.
|
||||
slog.Warn("skipped spark", slog.Int("id", int(f.FactorID)))
|
||||
continue
|
||||
}
|
||||
if sparkHasSkill(sp, id) {
|
||||
r = append(r, sp)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func sparkHasSkill(sp *horse.Spark, id horse.SkillID) bool {
|
||||
for _, g := range sp.Effects {
|
||||
for _, e := range g {
|
||||
if e.Target != horse.SparkSkillHint {
|
||||
continue
|
||||
}
|
||||
return id == horse.SkillID(e.Value1)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"git.sunturtle.xyz/zephyr/horse/mdb"
|
||||
"git.sunturtle.xyz/zephyr/horse/umadump/ism"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"zombiezen.com/go/sqlite"
|
||||
"zombiezen.com/go/sqlite/sqlitex"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
mdbf string
|
||||
dirs []string
|
||||
files []string
|
||||
)
|
||||
flag.StringVar(&mdbf, "-mdb", "", "`path` to master.mdb")
|
||||
flag.Func("d", "idle_single_mode `dir`ectory (may be specified multiple times)", func(s string) error {
|
||||
dirs = append(dirs, s)
|
||||
return nil
|
||||
})
|
||||
flag.Parse()
|
||||
|
||||
files = slices.Clone(flag.Args())
|
||||
for _, dir := range dirs {
|
||||
ds, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
slog.Error("getting files in directory", slog.String("dir", dir), slog.Any("err", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, d := range ds {
|
||||
if d.IsDir() {
|
||||
continue
|
||||
}
|
||||
files = append(files, filepath.Join(dir, d.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
stop()
|
||||
}()
|
||||
|
||||
db, err := sqlitex.NewPool(mdbf, sqlitex.PoolOptions{Flags: sqlite.OpenReadOnly})
|
||||
if err != nil {
|
||||
slog.Error("mdb", slog.Any("err", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
slog.Info("load skills")
|
||||
skills, err := mdb.Skills(ctx, db)
|
||||
if err != nil {
|
||||
slog.Error("skills", slog.Any("err", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
slog.Info("load sparks")
|
||||
sparks, err := mdb.Sparks(ctx, db)
|
||||
if err != nil {
|
||||
slog.Error("sparks", slog.Any("err", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
slog.Info("load umas")
|
||||
umas, err := mdb.Umas(ctx, db)
|
||||
if err != nil {
|
||||
slog.Error("umas", slog.Any("err", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
slog.Info("mdb",
|
||||
slog.Int("sparks", len(sparks)),
|
||||
slog.Int("skills", len(skills)),
|
||||
slog.Int("umas", len(umas)),
|
||||
)
|
||||
tab := makeTables(skills, sparks, umas)
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
g.SetLimit(64)
|
||||
for _, file := range files {
|
||||
g.Go(func() error {
|
||||
return dofile(ctx, file)
|
||||
})
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
slog.Error("process", slog.Any("err", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func dofile(ctx context.Context, file string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var career ism.WorkIdleSingleMode
|
||||
d := json.NewDecoder(f)
|
||||
d.DisallowUnknownFields()
|
||||
if err := d.Decode(&career); err != nil {
|
||||
return fmt.Errorf("%s: %w", file, err)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import "git.sunturtle.xyz/zephyr/horse"
|
||||
|
||||
type tables struct {
|
||||
skills map[horse.SkillID]*horse.Skill
|
||||
groups map[horse.SkillGroupID]map[int8]*horse.Skill
|
||||
sparks map[horse.SparkID]*horse.Spark
|
||||
umas map[horse.UmaID]*horse.Uma
|
||||
}
|
||||
|
||||
func makeTables(skills []horse.Skill, sparks []horse.Spark, umas []horse.Uma) tables {
|
||||
tab := tables{
|
||||
skills: make(map[horse.SkillID]*horse.Skill, len(skills)),
|
||||
groups: make(map[horse.SkillGroupID]map[int8]*horse.Skill, len(skills)/2),
|
||||
sparks: make(map[horse.SparkID]*horse.Spark, len(sparks)),
|
||||
umas: make(map[horse.UmaID]*horse.Uma, len(umas)),
|
||||
}
|
||||
for i, sk := range skills {
|
||||
tab.skills[sk.ID] = &skills[i]
|
||||
if tab.groups[sk.Group] == nil {
|
||||
tab.groups[sk.Group] = make(map[int8]*horse.Skill, 4)
|
||||
}
|
||||
// Only add the skill to the groups table if they're rare, the base
|
||||
// version of the common skill, or there isn't anything else there.
|
||||
if sk.Rarity == 2 || sk.GroupRate == 1 || tab.groups[sk.Group][sk.Rarity] == nil {
|
||||
tab.groups[sk.Group][sk.Rarity] = &skills[i]
|
||||
}
|
||||
}
|
||||
for i, sp := range sparks {
|
||||
tab.sparks[sp.ID] = &sparks[i]
|
||||
}
|
||||
for i, u := range umas {
|
||||
tab.umas[u.ID] = &umas[i]
|
||||
}
|
||||
return tab
|
||||
}
|
||||
+8
-7
@@ -89,13 +89,14 @@ func Umas(ctx context.Context, db *sqlitex.Pool) ([]horse.Uma, error) {
|
||||
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),
|
||||
ID: s.ColumnInt32(0),
|
||||
CharacterID: horse.CharacterID(s.ColumnInt(1)),
|
||||
Number: s.ColumnInt(2),
|
||||
Location: horse.LobbyConversationLocationID(s.ColumnInt(3)),
|
||||
Chara1: horse.CharacterID(s.ColumnInt(4)),
|
||||
Chara2: horse.CharacterID(s.ColumnInt(5)),
|
||||
Chara3: horse.CharacterID(s.ColumnInt(6)),
|
||||
ConditionType: s.ColumnInt(7),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -238,12 +238,14 @@ func TestConversations(t *testing.T) {
|
||||
}
|
||||
want := []horse.Conversation{
|
||||
{
|
||||
ID: 1,
|
||||
CharacterID: 1001,
|
||||
Number: 1,
|
||||
Location: 410,
|
||||
Chara1: 1001,
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
CharacterID: 1001,
|
||||
Number: 2,
|
||||
Location: 510,
|
||||
@@ -251,6 +253,7 @@ func TestConversations(t *testing.T) {
|
||||
ConditionType: 1,
|
||||
},
|
||||
{
|
||||
ID: 3,
|
||||
CharacterID: 1001,
|
||||
Number: 3,
|
||||
Location: 310,
|
||||
@@ -258,6 +261,7 @@ func TestConversations(t *testing.T) {
|
||||
ConditionType: 1,
|
||||
},
|
||||
{
|
||||
ID: 76,
|
||||
CharacterID: 1001,
|
||||
Number: 4,
|
||||
Location: 120,
|
||||
@@ -266,6 +270,7 @@ func TestConversations(t *testing.T) {
|
||||
ConditionType: 2,
|
||||
},
|
||||
{
|
||||
ID: 77,
|
||||
CharacterID: 1001,
|
||||
Number: 5,
|
||||
Location: 520,
|
||||
@@ -274,6 +279,7 @@ func TestConversations(t *testing.T) {
|
||||
ConditionType: 3,
|
||||
},
|
||||
{
|
||||
ID: 126,
|
||||
CharacterID: 1001,
|
||||
Number: 6,
|
||||
Location: 430,
|
||||
@@ -283,12 +289,14 @@ func TestConversations(t *testing.T) {
|
||||
ConditionType: 1,
|
||||
},
|
||||
{
|
||||
ID: 4,
|
||||
CharacterID: 1002,
|
||||
Number: 1,
|
||||
Location: 310,
|
||||
Chara1: 1002,
|
||||
},
|
||||
{
|
||||
ID: 5,
|
||||
CharacterID: 1002,
|
||||
Number: 2,
|
||||
Location: 210,
|
||||
@@ -296,6 +304,7 @@ func TestConversations(t *testing.T) {
|
||||
ConditionType: 1,
|
||||
},
|
||||
{
|
||||
ID: 6,
|
||||
CharacterID: 1002,
|
||||
Number: 3,
|
||||
Location: 110,
|
||||
@@ -303,6 +312,7 @@ func TestConversations(t *testing.T) {
|
||||
ConditionType: 1,
|
||||
},
|
||||
{
|
||||
ID: 78,
|
||||
CharacterID: 1002,
|
||||
Number: 4,
|
||||
Location: 520,
|
||||
@@ -311,6 +321,7 @@ func TestConversations(t *testing.T) {
|
||||
ConditionType: 3,
|
||||
},
|
||||
{
|
||||
ID: 79,
|
||||
CharacterID: 1002,
|
||||
Number: 5,
|
||||
Location: 220,
|
||||
|
||||
+123
-77
@@ -18,6 +18,8 @@ var (
|
||||
skillGroupSQL string
|
||||
//go:embed sql/skill.sql
|
||||
skillSQL string
|
||||
//go:embed sql/skill-group-skills.sql
|
||||
skillGroupSkillsSQL string
|
||||
)
|
||||
|
||||
// SkillGroups retrieves all skill groups.
|
||||
@@ -35,84 +37,128 @@ func SkillGroups(ctx context.Context, db *sqlitex.Pool) ([]horse.SkillGroup, err
|
||||
|
||||
// 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),
|
||||
// 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 sk []horse.Skill
|
||||
{
|
||||
stmt := conn.Prep(skillSQL)
|
||||
defer stmt.Reset()
|
||||
for {
|
||||
ok, err := stmt.Step()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
s := horse.Skill{
|
||||
ID: horse.SkillID(stmt.ColumnInt(0)),
|
||||
Name: stmt.ColumnText(1),
|
||||
Description: stmt.ColumnText(2),
|
||||
GroupSkills: make([]horse.GroupSkill, 0, 3),
|
||||
Group: horse.SkillGroupID(stmt.ColumnInt32(3)),
|
||||
Rarity: int8(stmt.ColumnInt(5)),
|
||||
GroupRate: int8(stmt.ColumnInt(6)),
|
||||
GradeValue: stmt.ColumnInt32(7),
|
||||
WitCheck: stmt.ColumnBool(8),
|
||||
Activations: trimActivations([]horse.Activation{
|
||||
{
|
||||
Precondition: stmt.ColumnText(9),
|
||||
Condition: stmt.ColumnText(10),
|
||||
Duration: horse.TenThousandths(stmt.ColumnInt(11)),
|
||||
DurScale: horse.DurScale(stmt.ColumnInt(12)),
|
||||
Cooldown: horse.TenThousandths(stmt.ColumnInt(13)),
|
||||
Abilities: trimAbilities([]horse.Ability{
|
||||
{
|
||||
Type: horse.AbilityType(stmt.ColumnInt(14)),
|
||||
ValueUsage: horse.AbilityValueUsage(stmt.ColumnInt(15)),
|
||||
Value: horse.TenThousandths(stmt.ColumnInt(16)),
|
||||
Target: horse.AbilityTarget(stmt.ColumnInt(17)),
|
||||
TargetValue: stmt.ColumnInt32(18),
|
||||
},
|
||||
{
|
||||
Type: horse.AbilityType(stmt.ColumnInt(19)),
|
||||
ValueUsage: horse.AbilityValueUsage(stmt.ColumnInt(20)),
|
||||
Value: horse.TenThousandths(stmt.ColumnInt(21)),
|
||||
Target: horse.AbilityTarget(stmt.ColumnInt(22)),
|
||||
TargetValue: stmt.ColumnInt32(23),
|
||||
},
|
||||
{
|
||||
Type: horse.AbilityType(stmt.ColumnInt(24)),
|
||||
ValueUsage: horse.AbilityValueUsage(stmt.ColumnInt(25)),
|
||||
Value: horse.TenThousandths(stmt.ColumnInt(26)),
|
||||
Target: horse.AbilityTarget(stmt.ColumnInt(27)),
|
||||
TargetValue: stmt.ColumnInt32(28),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Precondition: stmt.ColumnText(29),
|
||||
Condition: stmt.ColumnText(30),
|
||||
Duration: horse.TenThousandths(stmt.ColumnInt(31)),
|
||||
DurScale: horse.DurScale(stmt.ColumnInt(32)),
|
||||
Cooldown: horse.TenThousandths(stmt.ColumnInt(33)),
|
||||
Abilities: trimAbilities([]horse.Ability{
|
||||
{
|
||||
Type: horse.AbilityType(stmt.ColumnInt(34)),
|
||||
ValueUsage: horse.AbilityValueUsage(stmt.ColumnInt(35)),
|
||||
Value: horse.TenThousandths(stmt.ColumnInt(36)),
|
||||
Target: horse.AbilityTarget(stmt.ColumnInt(37)),
|
||||
TargetValue: stmt.ColumnInt32(38),
|
||||
},
|
||||
{
|
||||
Type: horse.AbilityType(stmt.ColumnInt(39)),
|
||||
ValueUsage: horse.AbilityValueUsage(stmt.ColumnInt(40)),
|
||||
Value: horse.TenThousandths(stmt.ColumnInt(41)),
|
||||
Target: horse.AbilityTarget(stmt.ColumnInt(42)),
|
||||
TargetValue: stmt.ColumnInt32(43),
|
||||
},
|
||||
{
|
||||
Type: horse.AbilityType(stmt.ColumnInt(44)),
|
||||
ValueUsage: horse.AbilityValueUsage(stmt.ColumnInt(45)),
|
||||
Value: horse.TenThousandths(stmt.ColumnInt(46)),
|
||||
Target: horse.AbilityTarget(stmt.ColumnInt(47)),
|
||||
TargetValue: stmt.ColumnInt32(48),
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
UniqueOwner: stmt.ColumnText(52), // TODO(zeph): should be id, not name
|
||||
Tags: parseTags(stmt.ColumnText(54)),
|
||||
SPCost: stmt.ColumnInt(49),
|
||||
IconID: stmt.ColumnInt(53),
|
||||
}
|
||||
sk = append(sk, s)
|
||||
}
|
||||
})
|
||||
}
|
||||
stmt := conn.Prep(skillGroupSkillsSQL)
|
||||
defer stmt.Reset()
|
||||
cur := sk
|
||||
for {
|
||||
ok, err := stmt.Step()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
// We sort skills by ID first, so we can add group skills in a single pass.
|
||||
id := stmt.ColumnInt(0)
|
||||
for len(cur) != 0 && cur[0].ID != horse.SkillID(id) {
|
||||
cur = cur[1:]
|
||||
}
|
||||
g := horse.GroupSkill{
|
||||
ID: horse.SkillID(stmt.ColumnInt(1)),
|
||||
GroupRate: int8(stmt.ColumnInt(2)),
|
||||
Rarity: int8(stmt.ColumnInt(3)),
|
||||
}
|
||||
cur[0].GroupSkills = append(cur[0].GroupSkills, g)
|
||||
}
|
||||
return sk, nil
|
||||
}
|
||||
|
||||
func parseTags(s string) []uint16 {
|
||||
|
||||
+182
-95
@@ -49,11 +49,16 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 100351, GroupRate: 1, Rarity: 4},
|
||||
{ID: 10351, GroupRate: 1, Rarity: 3},
|
||||
{ID: 900351, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 1035,
|
||||
Rarity: 3,
|
||||
GroupRate: 1,
|
||||
GradeValue: 240,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "is_finalcorner==1&blocked_side_continuetime>=2",
|
||||
@@ -81,11 +86,15 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 100011, GroupRate: 1, Rarity: 5},
|
||||
{ID: 900011, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 10001,
|
||||
Rarity: 5,
|
||||
GroupRate: 1,
|
||||
GradeValue: 340,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -120,11 +129,16 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 100351, GroupRate: 1, Rarity: 4},
|
||||
{ID: 10351, GroupRate: 1, Rarity: 3},
|
||||
{ID: 900351, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 10035,
|
||||
Rarity: 4,
|
||||
GroupRate: 1,
|
||||
GradeValue: 340,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "is_finalcorner==1&blocked_side_continuetime>=2",
|
||||
@@ -152,11 +166,15 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 110241, GroupRate: 1, Rarity: 5},
|
||||
{ID: 910241, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 11024,
|
||||
Rarity: 5,
|
||||
GroupRate: 1,
|
||||
GradeValue: 340,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -200,11 +218,17 @@ func TestSkills(t *testing.T) {
|
||||
ID: 200011,
|
||||
Name: "Right-Handed ◎",
|
||||
Description: "Increase performance on right-handed tracks.",
|
||||
Group: 20001,
|
||||
Rarity: 1,
|
||||
GroupRate: 2,
|
||||
GradeValue: 174,
|
||||
WitCheck: false,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200013, GroupRate: -1, Rarity: 1},
|
||||
{ID: 200012, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200011, GroupRate: 2, Rarity: 1},
|
||||
{ID: 200014, GroupRate: 3, Rarity: 2},
|
||||
},
|
||||
Group: 20001,
|
||||
Rarity: 1,
|
||||
GroupRate: 2,
|
||||
GradeValue: 174,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -231,11 +255,17 @@ func TestSkills(t *testing.T) {
|
||||
ID: 200012,
|
||||
Name: "Right-Handed ○",
|
||||
Description: "Moderately increase performance on right-handed tracks.",
|
||||
Group: 20001,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 129,
|
||||
WitCheck: false,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200013, GroupRate: -1, Rarity: 1},
|
||||
{ID: 200012, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200011, GroupRate: 2, Rarity: 1},
|
||||
{ID: 200014, GroupRate: 3, Rarity: 2},
|
||||
},
|
||||
Group: 20001,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 129,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -262,11 +292,17 @@ func TestSkills(t *testing.T) {
|
||||
ID: 200013,
|
||||
Name: "Right-Handed ×",
|
||||
Description: "Moderately decrease performance on right-handed tracks.",
|
||||
Group: 20001,
|
||||
Rarity: 1,
|
||||
GroupRate: -1,
|
||||
GradeValue: -129,
|
||||
WitCheck: false,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200013, GroupRate: -1, Rarity: 1},
|
||||
{ID: 200012, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200011, GroupRate: 2, Rarity: 1},
|
||||
{ID: 200014, GroupRate: 3, Rarity: 2},
|
||||
},
|
||||
Group: 20001,
|
||||
Rarity: 1,
|
||||
GroupRate: -1,
|
||||
GradeValue: -129,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -293,11 +329,17 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200013, GroupRate: -1, Rarity: 1},
|
||||
{ID: 200012, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200011, GroupRate: 2, Rarity: 1},
|
||||
{ID: 200014, GroupRate: 3, Rarity: 2},
|
||||
},
|
||||
Group: 20001,
|
||||
Rarity: 2,
|
||||
GroupRate: 3,
|
||||
GradeValue: 461,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -331,11 +373,16 @@ func TestSkills(t *testing.T) {
|
||||
ID: 200021,
|
||||
Name: "Left-Handed ◎",
|
||||
Description: "Increase performance on left-handed tracks.",
|
||||
Group: 20002,
|
||||
Rarity: 1,
|
||||
GroupRate: 2,
|
||||
GradeValue: 174,
|
||||
WitCheck: false,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200023, GroupRate: -1, Rarity: 1},
|
||||
{ID: 200022, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200021, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 20002,
|
||||
Rarity: 1,
|
||||
GroupRate: 2,
|
||||
GradeValue: 174,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -362,11 +409,16 @@ func TestSkills(t *testing.T) {
|
||||
ID: 200022,
|
||||
Name: "Left-Handed ○",
|
||||
Description: "Moderately increase performance on left-handed tracks.",
|
||||
Group: 20002,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 129,
|
||||
WitCheck: false,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200023, GroupRate: -1, Rarity: 1},
|
||||
{ID: 200022, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200021, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 20002,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 129,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -393,11 +445,16 @@ func TestSkills(t *testing.T) {
|
||||
ID: 200023,
|
||||
Name: "Left-Handed ×",
|
||||
Description: "Moderately decrease performance on left-handed tracks.",
|
||||
Group: 20002,
|
||||
Rarity: 1,
|
||||
GroupRate: -1,
|
||||
GradeValue: -129,
|
||||
WitCheck: false,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200023, GroupRate: -1, Rarity: 1},
|
||||
{ID: 200022, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200021, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 20002,
|
||||
Rarity: 1,
|
||||
GroupRate: -1,
|
||||
GradeValue: -129,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -424,11 +481,15 @@ func TestSkills(t *testing.T) {
|
||||
ID: 200361,
|
||||
Name: "Beeline Burst",
|
||||
Description: "Increase velocity on a straight.",
|
||||
Group: 20036,
|
||||
Rarity: 2,
|
||||
GroupRate: 2,
|
||||
GradeValue: 508,
|
||||
WitCheck: true,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200362, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200361, GroupRate: 2, Rarity: 2},
|
||||
},
|
||||
Group: 20036,
|
||||
Rarity: 2,
|
||||
GroupRate: 2,
|
||||
GradeValue: 508,
|
||||
WitCheck: true,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -455,11 +516,15 @@ func TestSkills(t *testing.T) {
|
||||
ID: 200362,
|
||||
Name: "Straightaway Adept",
|
||||
Description: "Slightly increase velocity on a straight.",
|
||||
Group: 20036,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 217,
|
||||
WitCheck: true,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200362, GroupRate: 1, Rarity: 1},
|
||||
{ID: 200361, GroupRate: 2, Rarity: 2},
|
||||
},
|
||||
Group: 20036,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 217,
|
||||
WitCheck: true,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -486,11 +551,14 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 200831, GroupRate: 1, Rarity: 1},
|
||||
},
|
||||
Group: 20083,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 217,
|
||||
WitCheck: true,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -517,11 +585,14 @@ func TestSkills(t *testing.T) {
|
||||
ID: 201801,
|
||||
Name: "♡ 3D Nail Art",
|
||||
Description: "Moderately decrease performance on firm ground.",
|
||||
Group: 20180,
|
||||
Rarity: 1,
|
||||
GroupRate: -1,
|
||||
GradeValue: -129,
|
||||
WitCheck: false,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 201801, GroupRate: -1, Rarity: 1},
|
||||
},
|
||||
Group: 20180,
|
||||
Rarity: 1,
|
||||
GroupRate: -1,
|
||||
GradeValue: -129,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -548,11 +619,14 @@ func TestSkills(t *testing.T) {
|
||||
ID: 300011,
|
||||
Name: "Unquenched Thirst",
|
||||
Description: "Moderately increase performance with the desire to race.",
|
||||
Group: 30001,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 0,
|
||||
WitCheck: false,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 300011, GroupRate: 1, Rarity: 1},
|
||||
},
|
||||
Group: 30001,
|
||||
Rarity: 1,
|
||||
GroupRate: 1,
|
||||
GradeValue: 0,
|
||||
WitCheck: false,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -579,11 +653,15 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 100011, GroupRate: 1, Rarity: 5},
|
||||
{ID: 900011, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 10001,
|
||||
Rarity: 1,
|
||||
GroupRate: 2,
|
||||
GradeValue: 180,
|
||||
WitCheck: true,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
@@ -618,11 +696,16 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 100351, GroupRate: 1, Rarity: 4},
|
||||
{ID: 10351, GroupRate: 1, Rarity: 3},
|
||||
{ID: 900351, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 10035,
|
||||
Rarity: 1,
|
||||
GroupRate: 2,
|
||||
GradeValue: 180,
|
||||
WitCheck: true,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "is_finalcorner==1&blocked_side_continuetime>=2",
|
||||
@@ -650,11 +733,15 @@ func TestSkills(t *testing.T) {
|
||||
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,
|
||||
GroupSkills: []horse.GroupSkill{
|
||||
{ID: 110241, GroupRate: 1, Rarity: 5},
|
||||
{ID: 910241, GroupRate: 2, Rarity: 1},
|
||||
},
|
||||
Group: 11024,
|
||||
Rarity: 1,
|
||||
GroupRate: 2,
|
||||
GradeValue: 180,
|
||||
WitCheck: true,
|
||||
Activations: []horse.Activation{
|
||||
{
|
||||
Precondition: "",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
SELECT
|
||||
id,
|
||||
gallery_chara_id,
|
||||
disp_order,
|
||||
pos_id,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
WITH g AS (
|
||||
SELECT
|
||||
l.id AS l,
|
||||
r.id AS r,
|
||||
r.group_rate,
|
||||
r.rarity
|
||||
FROM skill_data l
|
||||
JOIN skill_data r ON l.group_id = r.group_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
s.id AS l,
|
||||
i.id AS r,
|
||||
i.group_rate,
|
||||
i.rarity
|
||||
FROM skill_data s
|
||||
JOIN skill_data i ON s.id IN (i.unique_skill_id_1, i.unique_skill_id_2)
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
s.id AS l,
|
||||
i.id AS r,
|
||||
i.group_rate,
|
||||
i.rarity
|
||||
FROM skill_data s
|
||||
JOIN skill_data i ON i.id IN (s.unique_skill_id_1, s.unique_skill_id_2)
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
l.id AS l,
|
||||
r.id AS r,
|
||||
r.group_rate,
|
||||
r.rarity
|
||||
FROM skill_data i
|
||||
JOIN skill_data l ON l.id IN (i.unique_skill_id_1, i.unique_skill_id_2)
|
||||
JOIN skill_data r ON r.id IN (i.unique_skill_id_1, i.unique_skill_id_2)
|
||||
WHERE l.id != r.id
|
||||
)
|
||||
SELECT * FROM g
|
||||
ORDER BY l, group_rate, rarity DESC
|
||||
@@ -30,6 +30,7 @@ type Skill struct {
|
||||
ID SkillID `json:"skill_id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
GroupSkills []GroupSkill `json:"group_skills"`
|
||||
Group SkillGroupID `json:"group"`
|
||||
Rarity int8 `json:"rarity"`
|
||||
GroupRate int8 `json:"group_rate"`
|
||||
@@ -42,6 +43,12 @@ type Skill struct {
|
||||
IconID int `json:"icon_id"`
|
||||
}
|
||||
|
||||
type GroupSkill struct {
|
||||
ID SkillID `json:"skill_id"`
|
||||
GroupRate int8 `json:"group_rate"`
|
||||
Rarity int8 `json:"rarity"`
|
||||
}
|
||||
|
||||
// Activation is the parameters controlling when a skill activates.
|
||||
type Activation struct {
|
||||
Precondition string `json:"precondition,omitzero"`
|
||||
|
||||
Reference in New Issue
Block a user