package gameserver import ( "fmt" "maps" "gac-hkrpg-go/internal/common" pb "gac-hkrpg-go/protocol/proto" "google.golang.org/protobuf/proto" ) // Native Supply selection capture march-cw-supply-next: equipment Source=9. // This is the protocol acquisition-source discriminator, not a game item ID. const cwEquipmentSourceSupply uint32 = 9 // Applies only to the detached pending-action snapshot; the caller saves the // complete transaction before publishing any grant or queue acknowledgement. func cwSelectSupply(catalog *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, request *pb.HMNMJKCHABE) (*pb.GridFightSyncUpdateResultScNotify, error) { pending := run.GetFMIGOKIDKJL() offer := pending.GetFIKAGBAEOAP() if offer == nil || request == nil || offer.DAEMEBCKHIK == 0 || uint64(len(request.LOAFKKAFBOK)) != uint64(offer.DAEMEBCKHIK) { return nil, fmt.Errorf("CW Supply selection count differs from offer") } // Paid additional selections have no verified cost/update sequence yet. if offer.JJPMDBCPJBA < offer.DAEMEBCKHIK || offer.PLPJNCDKMAD != 0 { return nil, fmt.Errorf("CW paid Supply selection is not captured") } player, team, _ := cwComponents(run) stage, node := cwStage(run) var bag *pb.CONONEALNOF for _, c := range run.NAGDJGMMCBE { if v := c.GetGCKLIONKMJC(); v != nil { bag = v } } if player == nil || team == nil || stage == nil || node == nil || bag == nil || len(team.GridGameRoleList) == 0 { return nil, fmt.Errorf("CW Supply components missing") } equipment, err := common.LoadCurrencyWarsEquipment(catalog) if err != nil { return nil, err } front, err := catalog.IntegerRule("GridFight_Front_AvatarMaxNum") if err != nil { return nil, err } back, err := catalog.IntegerRule("GridFight_Back_AvatarMaxNum") if err != nil { return nil, err } overflow, err := catalog.IntegerRule("GridFight_Bench_OverFlow_AvatarNum") if err != nil { return nil, err } scales := team.GridGameRoleList[0].ConvertPropertyToFixpoint for _, role := range team.GridGameRoleList { if !maps.Equal(scales, role.ConvertPropertyToFixpoint) { return nil, fmt.Errorf("CW Supply role property template differs") } } notify := &pb.GridFightSyncUpdateResultScNotify{} used := map[uint32]bool{} for _, index := range request.LOAFKKAFBOK { // The native action stores one-based option indices. if index == 0 || uint64(index) > uint64(len(offer.MFBAKLCBPCJ)) || used[index] { return nil, fmt.Errorf("CW Supply option invalid/duplicated") } used[index] = true choice := offer.MFBAKLCBPCJ[index-1] if choice == nil { return nil, fmt.Errorf("CW Supply option missing") } drop := choice.GetOPPFPECOBEF() if drop == nil || drop.FKKGLFOHGGA != pb.MJJIFGINJAJ_MJJIFGINJAJ_BDPIMPJOJBK || drop.FNLMKJLKLFH != choice.RoleId || drop.Num != 1 { return nil, fmt.Errorf("CW Supply role drop differs from display") } star := drop.GetDisplayValue().GetRoleStar() if _, err = catalog.CapturedRoleVariant(choice.RoleId, star); err != nil { return nil, err } 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+back) + 1; p <= uint64(front+back)+uint64(player.MICEOJBAKGN)+uint64(overflow); p++ { if p > uint64(^uint32(0)) { break } if !occupied[uint32(p)] { pos = uint32(p) break } } id, e := cwNextEntityID(run) if e != nil { return nil, e } role := &pb.CDEAABODKCP{Id: choice.RoleId, RoleStar: star, UniqueId: id, Pos: pos, ConvertPropertyToFixpoint: maps.Clone(scales)} team.GridGameRoleList = append(team.GridGameRoleList, role) // Reference sync orders equipment acquisition, role acquisition, then // merges as separate result groups sharing the selected option context. grants := cwNotify() grants.GCPNIHHBGGD[0].GridUpdateSrc = pb.JJDFGMMMCMI_JJDFGMMMCMI_GLKAGBGLENM grants.GCPNIHHBGGD[0].OOEOCGLLHFO = []uint32{index} if len(choice.DHLBEFLOOPM) != len(choice.GridFightItemList) { return nil, fmt.Errorf("CW Supply equipment display differs") } for i, item := range choice.DHLBEFLOOPM { if item == nil || item.FKKGLFOHGGA != pb.MJJIFGINJAJ_MJJIFGINJAJ_NLFCABFPJHD || item.Num != 1 || item.FNLMKJLKLFH != choice.GridFightItemList[i] { return nil, fmt.Errorf("CW Supply equipment drop differs") } if _, ok := equipment[item.FNLMKJLKLFH]; !ok { return nil, fmt.Errorf("CW Supply equipment %d not captured", item.FNLMKJLKLFH) } uid, e := cwNextEntityID(run) if e != nil { return nil, e } gear := &pb.IJDLENJJBHJ{UniqueId: uid, GridFightEquipmentId: item.FNLMKJLKLFH, Source: cwEquipmentSourceSupply} bag.GridFightEquipmentList = append(bag.GridFightEquipmentList, gear) grants.GCPNIHHBGGD[0].JKPJFKCBIEM = append(grants.GCPNIHHBGGD[0].JKPJFKCBIEM, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_LMOLELDIKMA{LMOLELDIKMA: &pb.AOFOEOIJEHF{GridFightEquipmentList: []*pb.IJDLENJJBHJ{gear}}}}) } notify.GCPNIHHBGGD = append(notify.GCPNIHHBGGD, grants.GCPNIHHBGGD...) roles := cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_PIDDDHKPDDM{PIDDDHKPDDM: role}}) roles.GCPNIHHBGGD[0].GridUpdateSrc = pb.JJDFGMMMCMI_JJDFGMMMCMI_GLKAGBGLENM roles.GCPNIHHBGGD[0].OOEOCGLLHFO = []uint32{index} notify.GCPNIHHBGGD = append(notify.GCPNIHHBGGD, roles.GCPNIHHBGGD...) merged, e := cwMergeRoles(catalog, run, id) 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 Supply bench is full") } } } entries, err := cwNodeTransitions(catalog) if err != nil { return nil, err } var next *cwNodeTransition for i := range entries { e := &entries[i] if e.Route == stage.EMOHLJHPOPC && e.Chapter == stage.DCAHJELNJNP && e.Section == stage.SectionId && e.Stage == node.StageId && e.Penalty == node.IFIIOIIKJHO && e.Division == run.DivisionId && e.Overclock == run.IsOverlock { if next != nil { return nil, fmt.Errorf("CW Supply transition duplicated") } next = e } } if next == nil { return nil, fmt.Errorf("CW Supply next node not captured") } level := new(pb.AINFNFIDGFF) nextPending := new(pb.CHCHAJIANIM) if err = proto.Unmarshal(next.LevelWire, level); err != nil { return nil, err } if err = proto.Unmarshal(next.PendingWire, nextPending); err != nil { return nil, err } if level.IMPNAHBCADN == nil || nextPending.GetNNDGEJFLGGF() == nil { return nil, fmt.Errorf("CW Supply next node incomplete") } found := false for _, n := range stage.OOMMIIKPHBC { found = found || (n.DCAHJELNJNP == level.DCAHJELNJNP && n.SectionId == level.SectionId) } if !found { return nil, fmt.Errorf("CW Supply next node outside route") } stage.DCAHJELNJNP = level.DCAHJELNJNP stage.SectionId = level.SectionId stage.IMPNAHBCADN = proto.Clone(level.IMPNAHBCADN).(*pb.NCKOKDHNJDC) run.FMIGOKIDKJL = nextPending notify.GCPNIHHBGGD = append(notify.GCPNIHHBGGD, cwNotify(cwFinish(pending.QueuePosition), cwUnlock(), &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_OOICHLAMNIG{OOICHLAMNIG: level}}, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_FMIGOKIDKJL{FMIGOKIDKJL: nextPending}}).GCPNIHHBGGD...) return notify, nil }