package sdkserver import ( "fmt" "gac-hkrpg-go/internal/common" pb "gac-hkrpg-go/protocol/proto" "google.golang.org/protobuf/proto" "net/http" "sort" "strconv" "strings" ) func apiCurrencyWarsCommand(w http.ResponseWriter, command string) { fail := func(err error) { writeJSON(w, map[string]any{"success": false, "output": err.Error()}) } parts := strings.Fields(command) usage := "Usage: /cw status | /cw list role|equip|orb|consumable|forge | /cw gold | /cw hp | /cw exp | /cw equip|orb|consumable|forge [count] | /cw role [count]. Changes apply in CW Preparation." if len(parts) < 2 { fail(fmt.Errorf("%s", usage)) return } catalog, err := common.LoadCurrencyWarsCatalog() if err != nil { fail(err) return } if strings.EqualFold(parts[1], "help") { writeJSON(w, map[string]any{"success": true, "output": usage}) return } if strings.EqualFold(parts[1], "list") { if len(parts) != 3 { fail(fmt.Errorf("%s", usage)) return } var lines []string switch strings.ToLower(parts[2]) { case "role": for _, v := range catalog.CapturedVariants { lines = append(lines, fmt.Sprintf("role=%d star=%d avatar=%d rarity=%d", v.Config.ID, v.Star, v.Config.AvatarID, v.Config.Rarity)) } case "equip": rows, e := common.LoadCurrencyWarsEquipment(catalog) if e != nil { fail(e) return } ids := make([]uint32, 0, len(rows)) for id := range rows { ids = append(ids, id) } sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) for _, id := range ids { v := rows[id] lines = append(lines, fmt.Sprintf("equip=%d type=%d category=%d ability=%s", id, v.EquipType, v.EquipCategory, v.AbilityName)) } case "orb", "consumable", "forge": rows, e := common.LoadCurrencyWarsRewards(catalog) if e != nil { fail(e) return } ids := []uint32{} kind := strings.ToLower(parts[2]) switch kind { case "orb": for id := range rows.Orbs { ids = append(ids, id) } case "consumable": for id := range rows.Consumables { ids = append(ids, id) } case "forge": for id := range rows.Forges { ids = append(ids, id) } } sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) for _, id := range ids { switch kind { case "orb": v := rows.Orbs[id] lines = append(lines, fmt.Sprintf("orb=%d type=%d bonus=%d", id, v.Type, v.Bonus)) case "consumable": v := rows.Consumables[id] lines = append(lines, fmt.Sprintf("consumable=%d rule=%d stack=%t", id, v.ConsumableRule, v.IfStack)) case "forge": v := rows.Forges[id] lines = append(lines, fmt.Sprintf("forge=%d function=%d choices=%d parameters=%v", id, v.FuncType, v.EquipNum, v.ParamList)) } } default: fail(fmt.Errorf("available catalogs: role, equip, orb, consumable, forge")) return } writeJSON(w, map[string]any{"success": true, "output": strings.Join(lines, "\n")}) return } cfg, err := common.LoadRuntimeConfig() if err != nil { fail(err) return } snapshot, err := common.ReadCurrencyWarsSnapshot(cfg.PlayerUID, catalog.ClientVersion, catalog.Provenance.GameAssemblySHA256) if err != nil { fail(err) return } if snapshot == nil { fail(fmt.Errorf("no saved CW run")) return } var data pb.GridFightGetDataScRsp if err = proto.Unmarshal(snapshot.WireData, &data); err != nil { fail(err) return } run := data.DHFOFLFCLAH if run == nil { fail(fmt.Errorf("no active CW run")) return } var player *pb.NIHNGPKHEPC for _, c := range run.NAGDJGMMCBE { if v := c.GetFAOCFBOFCJP(); v != nil { player = v } } if player == nil { fail(fmt.Errorf("CW player state missing")) return } field := strings.ToLower(parts[1]) if field == "status" && len(parts) == 2 { writeJSON(w, map[string]any{"success": true, "output": fmt.Sprintf("CW run=%d revision=%d gold=%d hp=%d/%d level=%d", run.UniqueId, snapshot.Revision, player.Gold, player.GridFightLineupHp, player.PPKKHHNEOHB, player.EHNJDIDABHD)}) return } if len(parts) < 3 || len(parts) > 5 || (field != "gold" && field != "hp" && field != "exp" && field != "equip" && field != "role" && field != "orb" && field != "consumable" && field != "forge") { fail(fmt.Errorf("%s", usage)) return } value, err := strconv.ParseUint(parts[2], 10, 32) if err != nil { fail(fmt.Errorf("value must be a nonnegative uint32")) return } if field == "hp" && (value == 0 || value > uint64(player.PPKKHHNEOHB)) { fail(fmt.Errorf("HP must be 1..%d", player.PPKKHHNEOHB)) return } quantity, star := uint32(1), uint32(0) parse := func(text string) (uint32, error) { v, e := strconv.ParseUint(text, 10, 32) if e != nil || v == 0 { return 0, fmt.Errorf("star/count must be a positive uint32") } return uint32(v), nil } switch field { case "role": if len(parts) < 4 { fail(fmt.Errorf("%s", usage)) return } star, err = parse(parts[3]) if err == nil && len(parts) == 5 { quantity, err = parse(parts[4]) } if err == nil { _, err = catalog.CapturedRoleVariant(uint32(value), star) } case "orb", "consumable", "forge": if len(parts) > 4 { fail(fmt.Errorf("%s", usage)) return } if len(parts) == 4 { quantity, err = parse(parts[3]) } if err == nil { var rows *common.CurrencyWarsRewards rows, err = common.LoadCurrencyWarsRewards(catalog) if err == nil { if field == "forge" { if _, ok := rows.Forges[uint32(value)]; !ok { err = fmt.Errorf("unknown forge %d", value) } } else if field == "orb" { if _, ok := rows.Orbs[uint32(value)]; !ok { err = fmt.Errorf("unknown Orb %d", value) } } else { if _, ok := rows.Consumables[uint32(value)]; !ok { err = fmt.Errorf("unknown consumable %d", value) } } } } case "equip": if len(parts) > 4 { fail(fmt.Errorf("%s", usage)) return } if len(parts) == 4 { quantity, err = parse(parts[3]) } if err == nil { var rows map[uint32]common.CurrencyWarsEquipment rows, err = common.LoadCurrencyWarsEquipment(catalog) if err == nil { if _, ok := rows[uint32(value)]; !ok { err = fmt.Errorf("equipment %d not captured; use /cw list equip", value) } } } default: if len(parts) != 3 { fail(fmt.Errorf("%s", usage)) return } } if err != nil { fail(err) return } id, err := common.QueueCurrencyWarsGrant(cfg.PlayerUID, run.UniqueId, field, uint32(value), quantity, star) if err != nil { fail(err) return } writeJSON(w, map[string]any{"success": true, "state": "queued", "request_id": id, "output": fmt.Sprintf("Queued CW %s=%d star=%d count=%d for run %d. Applies at the next heartbeat in Preparation; check CW_GM log for the applied result.", field, value, star, quantity, run.UniqueId)}) }