116 lines
4.0 KiB
Go
116 lines
4.0 KiB
Go
package common
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
)
|
|
|
|
// LoadCurrencyWarsGuideMissions returns only the native CW panel's declared
|
|
// FinishMainMission prerequisites for the server's existing freeplay policy.
|
|
// It does not finish all imported missions, change a save or force Client UI
|
|
// state. The current condition capture is an explicit overlay of the older CW
|
|
// catalog, not a claim that every CW table was recaptured for that assembly.
|
|
func LoadCurrencyWarsGuideMissions() ([]uint32, error) {
|
|
catalog, err := LoadCurrencyWarsCatalog()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
raw, err := ReadResourceFile("resources/CurrencyWarsActivityConditions.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var overlay struct {
|
|
Format uint32 `json:"format_version"`
|
|
Source string `json:"source_assembly_sha256"`
|
|
Target string `json:"target_assembly_sha256"`
|
|
Capture string `json:"capture_sha256"`
|
|
Policy string `json:"guide_policy"`
|
|
Types map[string]uint32 `json:"condition_types"`
|
|
Rows []struct {
|
|
PanelID uint32
|
|
GuideConditions []struct {
|
|
Type uint32
|
|
Param string
|
|
}
|
|
} `json:"rows"`
|
|
}
|
|
if err := json.Unmarshal(raw, &overlay); err != nil {
|
|
return nil, fmt.Errorf("decode CW activity conditions: %w", err)
|
|
}
|
|
if overlay.Format != 1 || overlay.Source != catalog.Provenance.GameAssemblySHA256 || overlay.Policy != "complete_declared_guide_finish_main_missions" {
|
|
return nil, fmt.Errorf("CW activity condition source/policy differs")
|
|
}
|
|
for _, digest := range []string{overlay.Target, overlay.Capture} {
|
|
value, e := hex.DecodeString(digest)
|
|
if e != nil || len(value) != 32 {
|
|
return nil, fmt.Errorf("CW activity condition evidence missing")
|
|
}
|
|
}
|
|
// The independently captured current activity catalog provides the target
|
|
// assembly identity; a configured version label is not an identity check.
|
|
current, err := ReadResourceFile("resources/PreviewGameplayCatalog.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var identity struct {
|
|
Assembly string `json:"client_assembly_sha256"`
|
|
}
|
|
if err := json.Unmarshal(current, &identity); err != nil || identity.Assembly != overlay.Target {
|
|
return nil, fmt.Errorf("CW activity condition target differs from current activity capture")
|
|
}
|
|
finishType := overlay.Types["FinishMainMission"]
|
|
if finishType == 0 || len(overlay.Rows) == 0 {
|
|
return nil, fmt.Errorf("CW guide condition enum/rows missing")
|
|
}
|
|
mainData, err := ReadResourceFile("resources/MainMission.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var main struct {
|
|
Rows []struct {
|
|
ID uint32 `json:"MainMissionID"`
|
|
} `json:"main_mission_config"`
|
|
}
|
|
if err := json.Unmarshal(mainData, &main); err != nil {
|
|
return nil, err
|
|
}
|
|
known := make(map[uint32]bool, len(main.Rows))
|
|
for _, mission := range main.Rows {
|
|
known[mission.ID] = true
|
|
}
|
|
panels := map[uint32]bool{}
|
|
for _, schedule := range catalog.ActivitySchedules() {
|
|
panels[schedule.ActivityID] = true
|
|
}
|
|
seenPanels, seenMissions := map[uint32]bool{}, map[uint32]bool{}
|
|
var result []uint32
|
|
for _, row := range overlay.Rows {
|
|
if row.PanelID == 0 || !panels[row.PanelID] || seenPanels[row.PanelID] {
|
|
return nil, fmt.Errorf("CW guide condition panel is not an active unique CW panel")
|
|
}
|
|
seenPanels[row.PanelID] = true
|
|
for _, condition := range row.GuideConditions {
|
|
// An unimplemented condition cannot silently be treated as finished.
|
|
if condition.Type != finishType {
|
|
return nil, fmt.Errorf("CW guide condition kind %d is not implemented", condition.Type)
|
|
}
|
|
id, err := strconv.ParseUint(condition.Param, 10, 32)
|
|
if err != nil || id == 0 || !known[uint32(id)] {
|
|
return nil, fmt.Errorf("CW guide mission is not present in the mission resource")
|
|
}
|
|
if !seenMissions[uint32(id)] {
|
|
seenMissions[uint32(id)] = true
|
|
result = append(result, uint32(id))
|
|
}
|
|
}
|
|
}
|
|
if len(result) == 0 {
|
|
return nil, fmt.Errorf("CW guide condition mission list is empty")
|
|
}
|
|
sort.Slice(result, func(i, j int) bool { return result[i] < result[j] })
|
|
return result, nil
|
|
}
|