188 lines
7.2 KiB
Go
188 lines
7.2 KiB
Go
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
|
|
}
|