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