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

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
}