234 lines
6.7 KiB
Go
234 lines
6.7 KiB
Go
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 <total> | /cw hp <total> | /cw exp <gain> | /cw equip|orb|consumable|forge <itemID> [count] | /cw role <roleID> <star> [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)})
|
|
}
|