Restore event and mini hubs, current shop and hunt calendars, and regular seasons; preserve all published windows. Implement story/quiz routes, native shop purchases, avatar sets, currencies, rank-change items and permanent buffs with rollback and reward coverage tests. Refresh client buff stats and update Cash Shop to 0.1.1.
90 lines
2.6 KiB
Go
90 lines
2.6 KiB
Go
package gamedata
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
// Avatar sets are design bundles: ownership is checked on each member by the
|
|
// client, rather than on a synthetic AvatarSet ItemDBInfo.
|
|
type AvatarRewardDesign struct {
|
|
Sets map[uint64][]BattleReward
|
|
Items map[uint64]map[uint64]bool
|
|
}
|
|
|
|
func LoadAvatarRewardDesign(root, version string) (*AvatarRewardDesign, error) {
|
|
db, done, err := openStatDatabase(root, version)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer done()
|
|
return loadAvatarRewardDesign(db)
|
|
}
|
|
func loadAvatarRewardDesign(db *sql.DB) (*AvatarRewardDesign, error) {
|
|
d := &AvatarRewardDesign{Sets: map[uint64][]BattleReward{}, Items: map[uint64]map[uint64]bool{49: {}, 50: {}, 61: {}}}
|
|
for _, table := range []struct {
|
|
name string
|
|
typ uint64
|
|
field int
|
|
}{{"AvatarItemTable", 49, 3}, {"AvatarMotionTable", 50, 5}, {"AvatarCharTable", 61, 6}} {
|
|
if err := readCashMetadata(db, table.name, func(raw []byte) error {
|
|
id, err := optionalScalar(raw, table.field)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if id == 0 {
|
|
return fmt.Errorf("gamedata: invalid avatar member")
|
|
}
|
|
d.Items[table.typ][id] = true
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if err := readCashMetadata(db, "AvatarSetTable", func(raw []byte) error {
|
|
id, err := optionalScalar(raw, 6)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
members, err := eventGameRewards(raw, 5, 4, 3)
|
|
if err != nil || id == 0 || len(members) == 0 {
|
|
return fmt.Errorf("gamedata: invalid avatar set")
|
|
}
|
|
for _, member := range members {
|
|
if !d.Items[member.Type][member.ID] || member.Count == 0 || member.Count > math.MaxInt32 {
|
|
return fmt.Errorf("gamedata: avatar set %d has invalid member %d:%d", id, member.Type, member.ID)
|
|
}
|
|
}
|
|
d.Sets[id] = members
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return d, nil
|
|
}
|
|
func (d *AvatarRewardDesign) Expand(rewards []BattleReward) ([]BattleReward, error) {
|
|
var out []BattleReward
|
|
for _, reward := range rewards {
|
|
if reward.Type != 62 {
|
|
out = append(out, reward)
|
|
continue
|
|
}
|
|
if d == nil || reward.Count == 0 || reward.Count > math.MaxInt32 {
|
|
return nil, fmt.Errorf("gamedata: avatar reward design or quantity unavailable")
|
|
}
|
|
members, ok := d.Sets[reward.ID]
|
|
if !ok || len(members) == 0 {
|
|
return nil, fmt.Errorf("gamedata: unknown avatar set %d", reward.ID)
|
|
}
|
|
for _, member := range members {
|
|
if !d.Items[member.Type][member.ID] || member.Count == 0 || member.Count > math.MaxInt32/reward.Count {
|
|
return nil, fmt.Errorf("gamedata: invalid avatar set member")
|
|
}
|
|
member.Count *= reward.Count
|
|
out = append(out, member)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|