feat(all): implement SkyWay progression, battles and dispatch; fix field reward position ordering

This commit is contained in:
2026-10-08 19:55:29 +08:00
parent 06a83ada28
commit baae9fdddb
26 changed files with 1576 additions and 130 deletions
@@ -9,7 +9,7 @@ import (
type DispatchDesign struct {
Group, ID, Pack, Difficulty, TypeGroup, TypeID, AP, ClearTime, Growth uint64
GroundID uint64
GroundID, APType uint64
Rewards []BattleReward
boxes map[uint64]*dispatchGroup
}
@@ -63,6 +63,7 @@ func LoadDispatchDesign(root, version string, group, id uint64) (*DispatchDesign
}
ids, err = packedInts(raw, 15)
boss, _ = optionalScalar(raw, 4)
d.APType, _ = optionalScalar(raw, 1)
}
if err != nil {
return nil, err
@@ -47,7 +47,7 @@ func loadFieldPacks(db *sql.DB) (map[int]FieldPack, error) {
values[field] = v[0]
}
}
if values[55] != 3 && values[55] != 10 {
if values[55] != 3 && values[55] != 5 && values[55] != 10 {
continue
}
if id <= 0 || values[25] != uint64(id) {
@@ -4,51 +4,6 @@ import (
"fmt"
)
type SkyWayOverwhelmRule struct {
Group, Map, APType, Boss, BossAP uint64
Monsters, AP []uint64
}
func LoadSkyWayOverwhelm(root, version string) ([]SkyWayOverwhelmRule, error) {
db, done, e := openStatDatabase(root, version)
if e != nil {
return nil, e
}
defer done()
rows, e := db.Query("SELECT ProtoBuf FROM SkyWayFieldTable")
if e != nil {
return nil, e
}
defer func() { _ = rows.Close() }()
var out []SkyWayOverwhelmRule
for rows.Next() {
var b []byte
if e = rows.Scan(&b); e != nil {
return nil, e
}
r := SkyWayOverwhelmRule{}
for f, p := range map[int]*uint64{10: &r.Group, 13: &r.Map, 1: &r.APType, 4: &r.Boss, 3: &r.BossAP} {
*p, e = optionalScalar(b, f)
if e != nil {
return nil, e
}
}
r.Monsters, e = packedInts(b, 15)
if e != nil {
return nil, e
}
r.AP, e = packedInts(b, 14)
if e != nil {
return nil, e
}
if len(r.Monsters) != len(r.AP) {
return nil, fmt.Errorf("gamedata: malformed skyway AP mapping")
}
out = append(out, r)
}
return out, rows.Err()
}
type OverwhelmQuestRule struct {
Type, Count uint64
Targets []uint64
@@ -0,0 +1,157 @@
package gamedata
import "fmt"
type SkyWayStage struct {
Group, ID, Difficulty, Map, Point, APType, Boss, BossAP, PositionGroup uint64
Monsters, AP []uint64
}
type SkyWayDesign struct {
Pack int
Stages []SkyWayStage
Monsters map[uint64]HuntingMonster
SafeMaps map[int]bool
}
func LoadSkyWayDesign(root, version string) (*SkyWayDesign, error) {
db, done, err := openStatDatabase(root, version)
if err != nil {
return nil, err
}
defer done()
d := &SkyWayDesign{Monsters: map[uint64]HuntingMonster{}, SafeMaps: map[int]bool{}}
rows, err := db.Query("SELECT ProtoBuf FROM SkyWayFieldTable ORDER BY groupId,difficulty")
if err != nil {
return nil, err
}
for rows.Next() {
var raw []byte
if err = rows.Scan(&raw); err != nil {
_ = rows.Close()
return nil, err
}
var s SkyWayStage
for f, p := range map[int]*uint64{1: &s.APType, 3: &s.BossAP, 4: &s.Boss, 6: &s.Difficulty, 10: &s.Group, 12: &s.ID, 13: &s.Map, 17: &s.Point, 18: &s.PositionGroup} {
*p, err = optionalScalar(raw, f)
if err != nil {
_ = rows.Close()
return nil, err
}
}
s.Monsters, err = packedInts(raw, 15)
if err != nil {
_ = rows.Close()
return nil, err
}
s.AP, err = packedInts(raw, 14)
if err != nil {
_ = rows.Close()
return nil, err
}
if s.Group == 0 || s.Group > 7 || s.ID == 0 || s.Boss == 0 || s.Map == 0 || len(s.AP) != len(s.Monsters) || (s.APType != 1 && s.APType != 2) {
_ = rows.Close()
return nil, fmt.Errorf("gamedata: invalid SkyWay stage")
}
d.Stages = append(d.Stages, s)
}
if err = rows.Err(); err != nil {
_ = rows.Close()
return nil, err
}
if err = rows.Close(); err != nil {
return nil, err
}
for _, s := range d.Stages {
var pack int
if err = db.QueryRow("SELECT packId FROM MapTable WHERE id=?", s.Map).Scan(&pack); err != nil {
return nil, err
}
if d.Pack != 0 && d.Pack != pack {
return nil, fmt.Errorf("gamedata: SkyWay maps span packs")
}
d.Pack = pack
}
if d.Pack == 0 {
return nil, fmt.Errorf("gamedata: no SkyWay pack")
}
rows, err = db.Query("SELECT id,ProtoBuf FROM MapTable WHERE packId=?", d.Pack)
if err != nil {
return nil, err
}
for rows.Next() {
var id int
var raw []byte
if err = rows.Scan(&id, &raw); err != nil {
_ = rows.Close()
return nil, err
}
typ, e := optionalScalar(raw, 22)
if e != nil {
_ = rows.Close()
return nil, e
}
if typ == 0 {
d.SafeMaps[id] = true
}
}
if err = rows.Err(); err != nil {
_ = rows.Close()
return nil, err
}
if err = rows.Close(); err != nil {
return nil, err
}
pdb, release, err := openPackDatabase(root, version, d.Pack)
if err != nil {
return nil, err
}
defer release()
for _, s := range d.Stages {
for _, id := range append(append([]uint64(nil), s.Monsters...), s.Boss) {
if _, ok := d.Monsters[id]; ok {
continue
}
var raw []byte
if err = pdb.QueryRow("SELECT ProtoBuf FROM FieldMonsterTable WHERE id=?", id).Scan(&raw); err != nil {
return nil, err
}
m := HuntingMonster{ID: id, Rewards: map[uint64][]BattleReward{}}
m.Type, err = optionalScalar(raw, 30)
if err != nil {
return nil, err
}
m.Decks, err = packedInts(raw, 2)
if err != nil || len(m.Decks) == 0 {
return nil, fmt.Errorf("gamedata: SkyWay monster missing decks")
}
for _, deck := range m.Decks {
if err = pdb.QueryRow("SELECT ProtoBuf FROM BattleDeckTable WHERE id=?", deck).Scan(&raw); err != nil {
return nil, err
}
rs, e := parallelRewards(raw, 36, 34, 33)
if e != nil {
return nil, e
}
for _, r := range rs {
m.Rewards[deck] = append(m.Rewards[deck], BattleReward(r))
}
}
d.Monsters[id] = m
}
}
return d, nil
}
func (d *SkyWayDesign) Stage(group, id uint64) (SkyWayStage, bool) {
for _, s := range d.Stages {
if s.Group == group && s.ID == id {
return s, true
}
}
return SkyWayStage{}, false
}
// Battle modes are the current protocol's contiguous SKY_WAY enum family.
func SkyWayMode(group uint64) uint64 { return 8 + group }
func IsSkyWayMode(mode uint64) bool { return mode >= 9 && mode <= 15 }
@@ -70,7 +70,7 @@ func loadStoryCatalog(db *sql.DB) (*StoryCatalog, error) {
_ = rows.Close()
return nil, err
}
if kind != 0 && kind != 1000 {
if kind != 0 && kind != 5 && kind != 1000 {
continue
}
protoID, err := read(25)