package common import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" ) // Handbook camp rows describe available monsters and battle areas. They do // not specify a node's complete wave recipe, star/level, or battle template. type CurrencyWarsMonsterCamp struct { ID uint32 `json:"ID"` SeasonID uint32 `json:"SeasonID"` Common CurrencyWarsCampConfig `json:"Common"` Season CurrencyWarsCampSeasonConfig `json:"Season"` } type CurrencyWarsCampConfig struct { ID uint32 `json:"ID"` MonsterList []uint32 `json:"MonsterList"` BattleAreaList []uint32 `json:"BattleAreaList"` BossBattleArea uint32 `json:"BossBattleArea"` InitialRandomCode uint32 `json:"InitialRandomCode"` IfRandomEnabled uint32 `json:"IfRandomEnabled"` IconPath string `json:"IconPath"` ShowPicPath string `json:"ShowPicPath"` CampName json.RawMessage `json:"CampName"` CampDesc json.RawMessage `json:"CampDesc"` } type CurrencyWarsCampSeasonConfig struct { SeasonID uint32 `json:"SeasonID"` CampID uint32 `json:"CampID"` MonsterList []uint32 `json:"MonsterList"` } func (c *CurrencyWarsCatalog) validateMonsterCamps() error { c.campByID = make(map[[2]uint32]CurrencyWarsMonsterCamp) if len(c.MonsterCamps) == 0 && c.Provenance.MonsterCampsSHA256 == "" { return nil } digest, err := hex.DecodeString(c.Provenance.MonsterCampsSHA256) if err != nil || len(digest) != sha256.Size || len(c.MonsterCamps) == 0 { return fmt.Errorf("CW camp capture provenance is missing") } for _, camp := range c.MonsterCamps { key := [2]uint32{camp.SeasonID, camp.ID} if camp.ID == 0 || camp.SeasonID != c.ActiveSeasonID || camp.Common.ID != camp.ID || camp.Season.CampID != camp.ID || camp.Season.SeasonID != camp.SeasonID { return fmt.Errorf("invalid CW monster camp %v", key) } if _, exists := c.campByID[key]; exists { return fmt.Errorf("duplicate CW camp %v", key) } for _, list := range [][]uint32{camp.Common.MonsterList, camp.Season.MonsterList} { if len(list) == 0 { return fmt.Errorf("CW camp %v has an empty monster list", key) } for _, id := range list { if id == 0 { return fmt.Errorf("CW camp %v contains an empty monster ID", key) } } } for _, id := range camp.Common.BattleAreaList { if id == 0 { return fmt.Errorf("CW camp %v contains an empty battle area", key) } } c.campByID[key] = camp } return nil } func (c *CurrencyWarsCatalog) MonsterCamp(seasonID, campID uint32) (CurrencyWarsMonsterCamp, error) { row, exists := c.campByID[[2]uint32{seasonID, campID}] if !exists { return CurrencyWarsMonsterCamp{}, fmt.Errorf("CW camp %d/%d has not been captured", seasonID, campID) } row.Common.MonsterList = append([]uint32(nil), row.Common.MonsterList...) row.Common.BattleAreaList = append([]uint32(nil), row.Common.BattleAreaList...) row.Common.CampName = append(json.RawMessage(nil), row.Common.CampName...) row.Common.CampDesc = append(json.RawMessage(nil), row.Common.CampDesc...) row.Season.MonsterList = append([]uint32(nil), row.Season.MonsterList...) return row, nil }