2026-10-21 20:40:13 +07:00
package sdkserver
import (
"fmt"
"gac-hkrpg-go/internal/common"
pb "gac-hkrpg-go/protocol/proto"
"google.golang.org/protobuf/proto"
"net/http"
2026-10-28 18:54:43 +07:00
"sort"
2026-10-21 20:40:13 +07:00
"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 )
2026-10-28 18:54:43 +07:00
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."
2026-10-21 20:40:13 +07:00
if len ( parts ) < 2 {
fail ( fmt . Errorf ( "%s" , usage ))
return
}
catalog , err := common . LoadCurrencyWarsCatalog ()
if err != nil {
fail ( err )
return
}
2026-10-28 18:54:43 +07:00
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
}
2026-10-21 20:40:13 +07:00
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
}
2026-10-28 18:54:43 +07:00
if len ( parts ) < 3 || len ( parts ) > 5 || ( field != "gold" && field != "hp" && field != "exp" && field != "equip" && field != "role" && field != "orb" && field != "consumable" && field != "forge" ) {
2026-10-21 20:40:13 +07:00
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
}
2026-10-28 18:54:43 +07:00
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 )
2026-10-21 20:40:13 +07:00
if err != nil {
fail ( err )
return
}
2026-10-28 18:54:43 +07:00
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 )})
2026-10-21 20:40:13 +07:00
}