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

151 lines
5.4 KiB
Go

package gameserver
import (
"fmt"
"slices"
"gac-hkrpg-go/internal/common"
pb "gac-hkrpg-go/protocol/proto"
)
// GridFightAddItemSourceType: CraftEquip=5 in the reference enum/builder.
const cwEquipmentSourceCraft uint32 = 5
func cwCraftRecipe(recipes []common.CurrencyWarsCraft, target uint32, materialIDs []uint32) (*common.CurrencyWarsCraft, error) {
if len(materialIDs) != 2 {
return nil, fmt.Errorf("CW craft requires two material instances")
}
ids := slices.Clone(materialIDs)
slices.Sort(ids)
var found *common.CurrencyWarsCraft
for i := range recipes {
r := &recipes[i]
if target != 0 && r.CraftEquipID != target {
continue
}
cost := slices.Clone(r.CostEquipList)
slices.Sort(cost)
if slices.Equal(cost, ids) {
if found != nil && found.CraftEquipID != r.CraftEquipID {
return nil, fmt.Errorf("CW craft result is ambiguous")
}
found = r
}
}
if found == nil {
return nil, fmt.Errorf("CW material pair has no matching current recipe")
}
return found, nil
}
func cwCraftEquipment(catalog *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, uids []uint32, target uint32) (*pb.GridFightSyncUpdateResultScNotify, error) {
return cwCraftEquipmentOnRole(catalog, run, uids, target, nil)
}
func cwCraftEquipmentOnRole(catalog *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, uids []uint32, target uint32, receiver *pb.CDEAABODKCP) (*pb.GridFightSyncUpdateResultScNotify, error) {
if len(uids) != 2 || uids[0] == 0 || uids[0] == uids[1] {
return nil, fmt.Errorf("CW craft material identities differ")
}
bag := cwEquipmentBag(run)
_, team, _ := cwComponents(run)
if bag == nil || team == nil {
return nil, fmt.Errorf("CW craft inventory/team missing")
}
var materials []*pb.IJDLENJJBHJ
var ids []uint32
for _, uid := range uids {
var gear *pb.IJDLENJJBHJ
for _, item := range bag.GridFightEquipmentList {
if item.UniqueId == uid {
gear = item
break
}
}
if gear == nil {
return nil, fmt.Errorf("CW material does not belong to this run")
}
for _, role := range team.GridGameRoleList {
if slices.Contains(role.PNJIPHJJONG, uid) && (receiver == nil || receiver.UniqueId != role.UniqueId) {
return nil, fmt.Errorf("CW manual craft material is equipped")
}
}
for _, npc := range team.GridGameNpcList {
if slices.Contains(npc.PNJIPHJJONG, uid) {
return nil, fmt.Errorf("CW manual craft material is equipped by NPC")
}
}
materials = append(materials, gear)
ids = append(ids, gear.GridFightEquipmentId)
}
recipes, err := common.LoadCurrencyWarsCrafts(catalog)
if err != nil {
return nil, err
}
recipe, err := cwCraftRecipe(recipes, target, ids)
if err != nil {
return nil, err
}
equipment, err := common.LoadCurrencyWarsEquipment(catalog)
if err != nil {
return nil, err
}
if _, ok := equipment[recipe.CraftEquipID]; !ok {
return nil, fmt.Errorf("CW craft result configuration %d not captured", recipe.CraftEquipID)
}
uid, err := cwNextEntityID(run)
if err != nil {
return nil, err
}
gear := &pb.IJDLENJJBHJ{UniqueId: uid, GridFightEquipmentId: recipe.CraftEquipID, Source: cwEquipmentSourceCraft}
if receiver != nil {
receiver.PNJIPHJJONG = slices.DeleteFunc(receiver.PNJIPHJJONG, func(id uint32) bool { return slices.Contains(uids, id) })
if err = cwCanDressEquipment(catalog, equipment, bag, receiver, gear); err != nil {
return nil, err
}
receiver.PNJIPHJJONG = append(receiver.PNJIPHJJONG, gear.UniqueId)
}
bag.GridFightEquipmentList = slices.DeleteFunc(bag.GridFightEquipmentList, func(x *pb.IJDLENJJBHJ) bool { return slices.Contains(uids, x.UniqueId) })
bag.GridFightEquipmentList = append(bag.GridFightEquipmentList, gear)
// Current reference admin acquisition proves LMOLELDIKMA is Add. The
// source/Client sync family maps PENGEDECBNF to Remove, DDBKEOPKPFI to
// Update; never send the acquisition using the remove arm.
notify := cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_PENGEDECBNF{PENGEDECBNF: &pb.AOFOEOIJEHF{GridFightEquipmentList: materials}}}, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_LMOLELDIKMA{LMOLELDIKMA: &pb.AOFOEOIJEHF{GridFightEquipmentList: []*pb.IJDLENJJBHJ{gear}}}})
if receiver != nil {
notify.GCPNIHHBGGD[0].JKPJFKCBIEM = append(notify.GCPNIHHBGGD[0].JKPJFKCBIEM, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_PIDDDHKPDDM{PIDDDHKPDDM: receiver}})
}
return notify, nil
}
func init() {
cwHandle("GridFightEquipCraft", func(s *Session, req *pb.GridFightEquipCraftCsReq, rsp *pb.GridFightEquipCraftScRsp) error {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
catalog, data, err := cwPreparingData(s)
if err != nil {
return nil
}
if _, ok := pb.ACNPGGHPDJB_name[int32(req.Source)]; !ok {
return nil
}
if req.JCNLCNFHHDG == 0 {
return nil
}
// Request offset +0x24 is target equipment (current wire tag 10),
// +0x28 is optional track ID (tag 13). Isolated current-Client wire
// evidence ev_6340e59770f2b6839244f07ec5054aefd568e58d9723650e3bbe1cdf1a7c2a25.
notify, err := cwCraftEquipment(catalog, data.DHFOFLFCLAH, req.EHDAKFEHPMF, req.JCNLCNFHHDG)
if err != nil {
PacketLog("CW_CRAFT rejected: " + err.Error())
return nil
}
if err = s.saveCurrencyWarsData(data, catalog); err != nil {
return err
}
if err = cwSend(s, notify); err != nil {
return err
}
rsp.Retcode = 0
PacketLog(fmt.Sprintf("CW_CRAFT saved materials=%v target=%d track=%d revision=%d", req.EHDAKFEHPMF, req.JCNLCNFHHDG, req.NNFECNICMOD, s.currencyWarsRevision))
return nil
})
}