Files
GaCy-GO/internal/gameserver/currency_wars_grants.go
2026-10-28 18:54:43 +07:00

224 lines
7.3 KiB
Go

package gameserver
import (
"fmt"
"gac-hkrpg-go/internal/common"
pb "gac-hkrpg-go/protocol/proto"
"maps"
)
// Grant transactions operate on the detached Preparation snapshot, then the
// caller saves before sending the same acquisition updates used by Supply/shop.
func cwApplyCommand(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, cmd common.CurrencyWarsCommand) (*pb.GridFightSyncUpdateResultScNotify, error) {
player, team, shop := cwComponents(run)
if player == nil {
return nil, fmt.Errorf("CW player missing")
}
switch cmd.Field {
case "gold":
player.Gold = cmd.Value
return cwNotify(cwGoldSync(player.Gold)), nil
case "hp":
if cmd.Value == 0 || cmd.Value > player.PPKKHHNEOHB {
return nil, fmt.Errorf("HP must be 1..%d", player.PPKKHHNEOHB)
}
player.GridFightLineupHp = cmd.Value
return cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_GridFightLineupHp{GridFightLineupHp: &pb.FMJDDIHBELC{GridFightLineupHp: cmd.Value, PPKKHHNEOHB: player.PPKKHHNEOHB}}}), nil
case "exp":
if shop == nil {
return nil, fmt.Errorf("CW shop missing")
}
if err := cwApplyBaseExperience(c, player, shop, cmd.Value); err != nil {
return nil, err
}
return cwNotify(cwCapacitySync(player), cwShopSync(shop), &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_PLPHEIBLPDL{PLPHEIBLPDL: &pb.PIHKLPJGEPM{Level: player.EHNJDIDABHD, Exp: player.GridFightCurLevelExp, HFMKLAPEKFA: player.NDAINFLOLPJ}}}), nil
case "equip":
items, err := common.LoadCurrencyWarsEquipment(c)
if err != nil {
return nil, err
}
if _, ok := items[cmd.Value]; !ok {
return nil, fmt.Errorf("equipment %d not captured", cmd.Value)
}
bag := cwEquipmentBag(run)
if bag == nil {
return nil, fmt.Errorf("CW equipment bag missing")
}
quantity := cmd.Quantity
if quantity == 0 {
quantity = 1
} // Existing scalar queue format.
if uint64(quantity) > uint64(^uint32(0))-uint64(len(bag.GridFightEquipmentList)) {
return nil, fmt.Errorf("equipment count overflow")
}
added := &pb.AOFOEOIJEHF{}
for i := uint32(0); i < quantity; i++ {
uid, e := cwNextEntityID(run)
if e != nil {
return nil, e
}
// Source zero is protocol Default. Do not impersonate a Supply reward.
gear := &pb.IJDLENJJBHJ{UniqueId: uid, GridFightEquipmentId: cmd.Value}
bag.GridFightEquipmentList = append(bag.GridFightEquipmentList, gear)
added.GridFightEquipmentList = append(added.GridFightEquipmentList, gear)
}
return cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_LMOLELDIKMA{LMOLELDIKMA: added}}), nil
case "orb":
rewards, err := common.LoadCurrencyWarsRewards(c)
if err != nil {
return nil, err
}
if _, ok := rewards.Orbs[cmd.Value]; !ok {
return nil, fmt.Errorf("Orb %d not captured", cmd.Value)
}
var orbs *pb.DBONCGFHHDN
for _, component := range run.NAGDJGMMCBE {
if x := component.GetDBAGKJNPDOL(); x != nil {
orbs = x
}
}
if orbs == nil {
return nil, fmt.Errorf("CW Orb component missing")
}
quantity := cmd.Quantity
if quantity == 0 {
quantity = 1
}
notify := cwNotify()
for i := uint32(0); i < quantity; i++ {
uid, e := cwNextEntityID(run)
if e != nil {
return nil, e
}
orbs.MDKGCIKOMLI = append(orbs.MDKGCIKOMLI, &pb.IBPDPCOGDEP{UniqueId: uid, LDBNOMPCOKK: cmd.Value})
notify.GCPNIHHBGGD[0].JKPJFKCBIEM = append(notify.GCPNIHHBGGD[0].JKPJFKCBIEM, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_AFILIPBHMOO{AFILIPBHMOO: &pb.MMEDLCAENAL{UniqueId: uid, LDBNOMPCOKK: cmd.Value}}})
}
return notify, nil
case "consumable":
rewards, err := common.LoadCurrencyWarsRewards(c)
if err != nil {
return nil, err
}
if _, ok := rewards.Consumables[cmd.Value]; !ok {
return nil, fmt.Errorf("consumable %d not captured", cmd.Value)
}
bag := cwEquipmentBag(run)
if bag == nil {
return nil, fmt.Errorf("CW bag missing")
}
quantity := cmd.Quantity
if quantity == 0 {
quantity = 1
}
if quantity > uint32(^uint32(0)>>1) {
return nil, fmt.Errorf("consumable delta exceeds protocol int32")
}
var row *pb.LALKBPJANIH
var group uint32
for _, x := range bag.IPMKOMEHBMP {
if x.GroupId > group {
group = x.GroupId
}
if x.ItemId == cmd.Value {
row = x
}
}
fresh := row == nil
if fresh {
if group == ^uint32(0) {
return nil, fmt.Errorf("consumable groups exhausted")
}
row = &pb.LALKBPJANIH{GroupId: group + 1, ItemId: cmd.Value}
bag.IPMKOMEHBMP = append(bag.IPMKOMEHBMP, row)
}
if uint64(row.Num)+uint64(quantity) > uint64(^uint32(0)) {
return nil, fmt.Errorf("consumable stack overflow")
}
row.Num += quantity
info := &pb.AOFOEOIJEHF{PFMDKHIDDLB: []*pb.FLGMGNEOFBH{{GroupId: row.GroupId, ItemId: row.ItemId, Num: row.Num, RoundCnt: row.RoundCnt, HFNNNGHBHGG: int32(quantity)}}}
update := &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_DDBKEOPKPFI{DDBKEOPKPFI: info}}
if fresh {
update.MNLCHEJKDHO = &pb.KFNCAIHIHCM_LMOLELDIKMA{LMOLELDIKMA: info}
}
return cwNotify(update), nil
case "forge":
quantity := cmd.Quantity
if quantity == 0 {
quantity = 1
}
notify := cwNotify()
for i := uint32(0); i < quantity; i++ {
update, err := cwGrantForge(c, run, cmd.Value)
if err != nil {
return nil, err
}
notify.GCPNIHHBGGD[0].JKPJFKCBIEM = append(notify.GCPNIHHBGGD[0].JKPJFKCBIEM, update)
}
return notify, nil
case "role":
if team == nil || len(team.GridGameRoleList) == 0 {
return nil, fmt.Errorf("CW role property template missing")
}
if _, err := c.CapturedRoleVariant(cmd.Value, cmd.Star); err != nil {
return nil, err
}
scales := team.GridGameRoleList[0].ConvertPropertyToFixpoint
for _, r := range team.GridGameRoleList {
if !maps.Equal(scales, r.ConvertPropertyToFixpoint) {
return nil, fmt.Errorf("role property templates differ")
}
}
front, err := c.IntegerRule("GridFight_Front_AvatarMaxNum")
if err != nil {
return nil, err
}
back, err := c.IntegerRule("GridFight_Back_AvatarMaxNum")
if err != nil {
return nil, err
}
quantity := cmd.Quantity
if quantity == 0 {
quantity = 1
}
notify := &pb.GridFightSyncUpdateResultScNotify{}
for i := uint32(0); i < quantity; i++ {
occupied := map[uint32]bool{}
for _, r := range team.GridGameRoleList {
occupied[r.Pos] = true
}
for _, r := range team.KCOMPKMJCMC {
occupied[r.Pos] = true
}
for _, f := range team.PMEOPPDAMLM {
occupied[f.Pos] = true
}
var pos uint32
for p := uint64(front) + uint64(back) + 1; p <= uint64(front)+uint64(back)+uint64(player.MICEOJBAKGN) && p <= uint64(^uint32(0)); p++ {
if !occupied[uint32(p)] {
pos = uint32(p)
break
}
}
uid, e := cwNextEntityID(run)
if e != nil {
return nil, e
}
role := &pb.CDEAABODKCP{Id: cmd.Value, RoleStar: cmd.Star, UniqueId: uid, Pos: pos, ConvertPropertyToFixpoint: maps.Clone(scales)}
team.GridGameRoleList = append(team.GridGameRoleList, role)
notify.GCPNIHHBGGD = append(notify.GCPNIHHBGGD, cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_PIDDDHKPDDM{PIDDDHKPDDM: role}}).GCPNIHHBGGD...)
merged, e := cwMergeRoles(c, run, uid)
if e != nil {
return nil, e
}
notify.GCPNIHHBGGD = append(notify.GCPNIHHBGGD, merged...)
for _, r := range team.GridGameRoleList {
if r.Pos == 0 {
return nil, fmt.Errorf("CW bench full; entire grant cancelled")
}
}
}
return notify, nil
}
return nil, fmt.Errorf("unknown CW command %q", cmd.Field)
}