224 lines
7.3 KiB
Go
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)
|
|
}
|