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

121 lines
3.9 KiB
Go

package gameserver
import (
"fmt"
"gac-hkrpg-go/internal/common"
pb "gac-hkrpg-go/protocol/proto"
"google.golang.org/protobuf/proto"
"maps"
"sort"
)
// Basic identical-role merge, following the captured remove/add sync. Existing
// equipment stays in the bag; only the retained role's attachment list moves.
// Switch-role groups and Spotlight star locks need their separately mapped rules.
func cwMergeRoles(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, preferred uint32) ([]*pb.ABPLALCHNFA, error) {
_, team, _ := cwComponents(run)
if team == nil {
return nil, fmt.Errorf("CW team missing")
}
rules, e := c.MergeRules()
if e != nil {
return nil, e
}
front, e := c.IntegerRule("GridFight_Front_AvatarMaxNum")
if e != nil {
return nil, e
}
back, e := c.IntegerRule("GridFight_Back_AvatarMaxNum")
if e != nil {
return nil, e
}
active := func(r *pb.CDEAABODKCP) bool { return r.Pos > 0 && uint64(r.Pos) <= uint64(front+back) }
better := func(a, b *pb.CDEAABODKCP) bool {
if active(a) != active(b) {
return active(a)
}
if len(a.PNJIPHJJONG) != len(b.PNJIPHJJONG) {
return len(a.PNJIPHJJONG) > len(b.PNJIPHJJONG)
}
return a.UniqueId < b.UniqueId
}
var groups []*pb.ABPLALCHNFA
for {
candidates := map[[2]uint32][]*pb.CDEAABODKCP{}
for _, r := range team.GridGameRoleList {
if r.RoleStar < rules.OrdinaryMaxStar {
key := [2]uint32{r.Id, r.RoleStar}
candidates[key] = append(candidates[key], r)
}
}
keys := make([][2]uint32, 0, len(candidates))
for k, v := range candidates {
if len(v) >= int(rules.Copies) {
keys = append(keys, k)
}
}
sort.Slice(keys, func(i, j int) bool {
if keys[i][1] != keys[j][1] {
return keys[i][1] < keys[j][1]
}
return keys[i][0] < keys[j][0]
})
var selected []*pb.CDEAABODKCP
for _, key := range keys {
if _, err := c.CapturedRoleVariant(key[0], key[1]+1); err != nil {
continue
}
selected = candidates[key]
sort.Slice(selected, func(i, j int) bool {
if (selected[i].UniqueId == preferred) != (selected[j].UniqueId == preferred) {
return selected[i].UniqueId == preferred
}
return better(selected[i], selected[j])
})
selected = selected[:int(rules.Copies)]
break
}
if len(selected) == 0 {
return groups, nil
}
sort.Slice(selected, func(i, j int) bool { return better(selected[i], selected[j]) })
anchor := selected[0]
for _, r := range selected {
if !maps.Equal(anchor.ConvertPropertyToFixpoint, r.ConvertPropertyToFixpoint) || !maps.Equal(anchor.BABNHPDGNGB, r.BABNHPDGNGB) || !maps.Equal(anchor.GridFightValueInitComponent, r.GridFightValueInitComponent) {
return nil, fmt.Errorf("CW merge role modifiers need an explicit transfer rule")
}
}
id, err := cwNextEntityID(run)
if err != nil {
return nil, err
}
merged := proto.Clone(anchor).(*pb.CDEAABODKCP)
merged.UniqueId = id
merged.RoleStar++
removed := map[uint32]bool{}
group := &pb.ABPLALCHNFA{GridUpdateSrc: pb.JJDFGMMMCMI_JJDFGMMMCMI_NJBMBFFLNGM}
for _, r := range selected {
removed[r.UniqueId] = true
group.JKPJFKCBIEM = append(group.JKPJFKCBIEM, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_HFCBOIOAECK{HFCBOIOAECK: r.UniqueId}})
}
kept := make([]*pb.CDEAABODKCP, 0, len(team.GridGameRoleList)-len(selected)+1)
for _, r := range team.GridGameRoleList {
if !removed[r.UniqueId] {
kept = append(kept, r)
}
}
team.GridGameRoleList = append(kept, merged)
group.JKPJFKCBIEM = append(group.JKPJFKCBIEM, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_PIDDDHKPDDM{PIDDDHKPDDM: proto.Clone(merged).(*pb.CDEAABODKCP)}})
if state := cwSpotlights(run); state != nil {
for _, binding := range state.FNNEPMAPOOF {
if removed[binding.MDNAGANGNKK] {
binding.MDNAGANGNKK = merged.UniqueId
group.JKPJFKCBIEM = append(group.JKPJFKCBIEM, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_OHHBBOGFDON{OHHBBOGFDON: proto.Clone(binding).(*pb.FMBACDOFCAL)}})
}
}
}
groups = append(groups, group)
preferred = id
}
}