147 lines
4.9 KiB
Go
147 lines
4.9 KiB
Go
package gameserver
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"gac-hkrpg-go/internal/common"
|
|
pb "gac-hkrpg-go/protocol/proto"
|
|
"math/big"
|
|
"slices"
|
|
"sort"
|
|
)
|
|
|
|
// Current Client GridFightForgeFuncType metadata: Equip = 1.
|
|
const cwForgeEquipment uint32 = 1
|
|
|
|
func cwFreeBenchPosition(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH) (uint32, error) {
|
|
player, team, _ := cwComponents(run)
|
|
if player == nil || team == nil {
|
|
return 0, fmt.Errorf("CW team missing")
|
|
}
|
|
front, err := c.IntegerRule("GridFight_Front_AvatarMaxNum")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
back, err := c.IntegerRule("GridFight_Back_AvatarMaxNum")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
used := map[uint32]bool{}
|
|
for _, r := range team.GridGameRoleList {
|
|
used[r.Pos] = true
|
|
}
|
|
for _, r := range team.KCOMPKMJCMC {
|
|
used[r.Pos] = true
|
|
}
|
|
for _, r := range team.PMEOPPDAMLM {
|
|
used[r.Pos] = true
|
|
}
|
|
for p := uint64(front) + uint64(back) + 1; p <= uint64(front)+uint64(back)+uint64(player.MICEOJBAKGN) && p <= uint64(^uint32(0)); p++ {
|
|
if !used[uint32(p)] {
|
|
return uint32(p), nil
|
|
}
|
|
}
|
|
return 0, fmt.Errorf("CW bench full")
|
|
}
|
|
|
|
// GM offers use a uniform draw without replacement from captured equipment of
|
|
// the configured category, matching the reference RollForgeEquipment policy.
|
|
// This does not claim original server weights or implement Role/Expert actions.
|
|
func cwGrantForge(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, id uint32) (*pb.KFNCAIHIHCM, error) {
|
|
rewards, err := common.LoadCurrencyWarsRewards(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
row, ok := rewards.Forges[id]
|
|
if !ok {
|
|
return nil, fmt.Errorf("forge %d not captured", id)
|
|
}
|
|
if row.FuncType != cwForgeEquipment || len(row.ParamList) == 0 {
|
|
return nil, fmt.Errorf("forge function %d needs its Role/Expert pending action", row.FuncType)
|
|
}
|
|
gear, err := common.LoadCurrencyWarsEquipment(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var pool []uint32
|
|
for id, e := range gear {
|
|
if e.EquipCategory == row.ParamList[0] {
|
|
pool = append(pool, id)
|
|
}
|
|
}
|
|
sort.Slice(pool, func(i, j int) bool { return pool[i] < pool[j] })
|
|
if uint64(len(pool)) < uint64(row.EquipNum) {
|
|
return nil, fmt.Errorf("forge equipment category capture incomplete")
|
|
}
|
|
pos, err := cwFreeBenchPosition(c, run)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
uid, err := cwNextEntityID(run)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
forge := &pb.DDAMEHEPOBD{UniqueId: uid, Pos: pos, LCLPEJHEAID: id}
|
|
for i := uint32(0); i < row.EquipNum; i++ {
|
|
n, e := rand.Int(rand.Reader, big.NewInt(int64(len(pool))-int64(i)))
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
j := int(i) + int(n.Int64())
|
|
pool[i], pool[j] = pool[j], pool[i]
|
|
forge.CKJAKKNOHEK = append(forge.CKJAKKNOHEK, &pb.MBPDIPLJMKN{OFJBCEDFLKL: &pb.MBPDIPLJMKN_GGMPEGMEHPD{GGMPEGMEHPD: &pb.ELGLKEAPLGM{GridFightEquipmentId: pool[i]}}})
|
|
}
|
|
_, team, _ := cwComponents(run)
|
|
team.PMEOPPDAMLM = append(team.PMEOPPDAMLM, forge)
|
|
// Current _ProcessUpdateInfo: tag 1821 -> Team.AddForge; 1257 -> UpdateForge.
|
|
return &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_OLCKALFILKK{OLCKALFILKK: forge}}, nil
|
|
}
|
|
|
|
func init() {
|
|
cwHandle("GridFightUseForge", func(s *Session, req *pb.GridFightUseForgeCsReq, rsp *pb.GridFightUseForgeScRsp) error {
|
|
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
|
|
c, data, err := cwPreparingData(s)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
run := data.DHFOFLFCLAH
|
|
_, team, _ := cwComponents(run)
|
|
if team == nil {
|
|
return nil
|
|
}
|
|
index := slices.IndexFunc(team.PMEOPPDAMLM, func(x *pb.DDAMEHEPOBD) bool { return x.UniqueId == req.UniqueId })
|
|
if index < 0 {
|
|
return nil
|
|
}
|
|
forge := team.PMEOPPDAMLM[index]
|
|
rewards, err := common.LoadCurrencyWarsRewards(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
row, ok := rewards.Forges[forge.LCLPEJHEAID]
|
|
if !ok || row.FuncType != cwForgeEquipment || uint64(req.PFIKCNFBGHE) >= uint64(len(forge.CKJAKKNOHEK)) {
|
|
return nil
|
|
}
|
|
// Zero-based choice index follows the reference UseForge contract. Current
|
|
// request names remain obfuscated; record the index in the gameplay log.
|
|
id := forge.CKJAKKNOHEK[req.PFIKCNFBGHE].GetGGMPEGMEHPD().GetGridFightEquipmentId()
|
|
notify, err := cwApplyCommand(c, run, common.CurrencyWarsCommand{Field: "equip", Value: id, Quantity: 1})
|
|
if err != nil {
|
|
PacketLog("CW_FORGE_USE rejected: " + err.Error())
|
|
return nil
|
|
}
|
|
team.PMEOPPDAMLM = slices.Delete(team.PMEOPPDAMLM, index, index+1)
|
|
// tag 703 -> Team.RemoveForge(UInt32), verified through the current switch.
|
|
notify.GCPNIHHBGGD[0].JKPJFKCBIEM = append([]*pb.KFNCAIHIHCM{{MNLCHEJKDHO: &pb.KFNCAIHIHCM_OACCGHIHLBA{OACCGHIHLBA: req.UniqueId}}}, notify.GCPNIHHBGGD[0].JKPJFKCBIEM...)
|
|
if err = s.saveCurrencyWarsData(data, c); err != nil {
|
|
return err
|
|
}
|
|
if err = cwSend(s, notify); err != nil {
|
|
return err
|
|
}
|
|
rsp.Retcode = 0
|
|
PacketLog(fmt.Sprintf("CW_FORGE_USE saved uid=%d index=%d equipment=%d revision=%d", req.UniqueId, req.PFIKCNFBGHE, id, s.currencyWarsRevision))
|
|
return nil
|
|
})
|
|
}
|