Files
GaCy-GO/internal/common/currency_wars_roles.go
T
2026-10-28 18:54:43 +07:00

118 lines
4.5 KiB
Go

package common
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
// CapturedRole retains native handbook-default configuration. This is not the
// complete shop pool: hidden, variant and OtherRoles entries are not captured.
type CurrencyWarsCapturedRole struct {
Config CurrencyWarsRoleConfig `json:"Config"`
Price uint32 `json:"Price"`
MaxStar uint32 `json:"MaxStar"`
DefaultStar uint32 `json:"DefaultStar"`
DefaultSellPrice uint32 `json:"DefaultSellPrice"`
}
type CurrencyWarsRoleConfig struct {
ID uint32 `json:"ID"`
SeasonID uint32 `json:"SeasonID"`
AvatarID uint32 `json:"AvatarID"`
SpecialAvatarID uint32 `json:"SpecialAvatarID"`
Rarity uint32 `json:"Rarity"`
IsInBook bool `json:"IsInBook"`
IsInPool bool `json:"IsInPool"`
IsExpert uint32 `json:"IsExpert"`
IsFakeAvatar uint32 `json:"IsFakeAvatar"`
FrontBackType uint32 `json:"FrontBackType"`
EquipmentID uint32 `json:"EquipmentID"`
TraitList []uint32 `json:"TraitList"`
BackendRankList []uint32 `json:"BackendRankList"`
ChargeType []uint32 `json:"ChargeType"`
}
// Stage wrappers were captured on a fresh profile without talents. Future
// run modifiers must be applied separately; these are not final reward totals.
type CurrencyWarsCapturedStage struct {
ID uint32 `json:"ID"`
EnemyHardLevel uint32 `json:"EnemyHardLevel"`
Exp uint32 `json:"Exp"`
WeeklyScore uint32 `json:"WeeklyScore"`
OverChargeExp uint32 `json:"OverChargeExp"`
OverChargeWeeklyScore uint32 `json:"OverChargeWeeklyScore"`
ScoreRule uint32 `json:"ScoreRule"`
OCScoreRule uint32 `json:"OCScoreRule"`
EnvironmentBuffList []uint32 `json:"EnvironmentBuffList"`
}
func (c *CurrencyWarsCatalog) validateCapturedGameplay() error {
digest, err := hex.DecodeString(c.Provenance.GameplaySHA256)
if err != nil || len(digest) != sha256.Size || len(c.CapturedRoles) == 0 {
return fmt.Errorf("CW role/stage capture or provenance is missing")
}
c.roleByID = make(map[uint32]CurrencyWarsCapturedRole)
for _, role := range c.CapturedRoles {
r := role.Config
if r.ID == 0 || r.AvatarID == 0 || r.SeasonID != c.ActiveSeasonID || role.DefaultStar == 0 || role.DefaultStar > role.MaxStar {
return fmt.Errorf("invalid CW captured role %d", r.ID)
}
if _, duplicate := c.roleByID[r.ID]; duplicate {
return fmt.Errorf("duplicate CW role %d", r.ID)
}
c.roleByID[r.ID] = role
}
for _, role := range c.CapturedRoles {
price, err := c.BaseSalePrice(role.Config.ID, role.DefaultStar)
if err != nil || price != role.DefaultSellPrice {
return fmt.Errorf("CW captured default sale price differs from star/rarity table for role %d", role.Config.ID)
}
}
c.stageByID = make(map[uint32]CurrencyWarsCapturedStage)
for _, stage := range c.CapturedStages {
division, found := c.divisionByID[stage.ID]
if !found || division.SeasonID != c.ActiveSeasonID || stage.EnemyHardLevel == 0 {
return fmt.Errorf("CW stage %d has no matching active division", stage.ID)
}
if _, duplicate := c.stageByID[stage.ID]; duplicate {
return fmt.Errorf("duplicate CW captured stage %d", stage.ID)
}
c.stageByID[stage.ID] = stage
}
if len(c.stageByID) != len(c.SeasonDivisions(c.ActiveSeasonID)) {
return fmt.Errorf("CW stage capture does not cover every active division")
}
if err := c.validateRoleVariants(); err != nil {
return err
}
if err := c.validatePlayerLevels(); err != nil {
return err
}
if err := c.validateNodeRules(); err != nil {
return err
}
return c.validateMonsterCamps()
}
func (c *CurrencyWarsCatalog) CapturedRole(id uint32) (CurrencyWarsCapturedRole, error) {
id = c.CurrentRoleID(id)
role, found := c.roleByID[id]
if !found {
return CurrencyWarsCapturedRole{}, fmt.Errorf("CW role %d has not been captured", id)
}
role.Config.TraitList = append([]uint32(nil), role.Config.TraitList...)
role.Config.BackendRankList = append([]uint32(nil), role.Config.BackendRankList...)
role.Config.ChargeType = append([]uint32(nil), role.Config.ChargeType...)
return role, nil
}
func (c *CurrencyWarsCatalog) CapturedStage(id uint32) (CurrencyWarsCapturedStage, error) {
stage, found := c.stageByID[id]
if !found {
return CurrencyWarsCapturedStage{}, fmt.Errorf("CW stage %d has not been captured", id)
}
stage.EnvironmentBuffList = append([]uint32(nil), stage.EnvironmentBuffList...)
return stage, nil
}