Files
2026-10-28 18:54:43 +07:00

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
})
}