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

216 lines
6.8 KiB
Go

package gameserver
import (
"crypto/rand"
"fmt"
"gac-hkrpg-go/internal/common"
pb "gac-hkrpg-go/protocol/proto"
"google.golang.org/protobuf/proto"
"math/big"
"sort"
)
func cwSpotlights(run *pb.MPIMAEMCEFH) *pb.GNOBLNOCEHA {
for _, c := range run.NAGDJGMMCBE {
if v := c.GetLEGMAJLHBLA(); v != nil {
return v
}
}
return nil
}
func cwEnsureSpotlights(run *pb.MPIMAEMCEFH) bool {
if cwSpotlights(run) != nil {
return false
}
run.NAGDJGMMCBE = append(run.NAGDJGMMCBE, &pb.GridFightGameInfo{LEOIAOFBBFA: &pb.GridFightGameInfo_LEGMAJLHBLA{LEGMAJLHBLA: &pb.GNOBLNOCEHA{}}})
return true
}
func cwSpotlightChoices(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH) (*pb.BDLBHJGNCFM, error) {
rows, policy, e := c.SpotlightData()
if e != nil {
return nil, e
}
_, team, _ := cwComponents(run)
if team == nil {
return nil, fmt.Errorf("CW Spotlight team missing")
}
owned := map[uint32]bool{}
for _, r := range team.GridGameRoleList {
owned[r.Id] = true
}
used := map[uint32]bool{}
if v := cwSpotlights(run); v != nil {
for _, r := range v.FNNEPMAPOOF {
used[r.RoleId] = true
}
}
var ids, safeIDs []uint32
for id, row := range rows {
if run.IsOverlock && !row.IsOCEffective {
continue
}
compatible, hasUsedRole := false, false
for _, role := range row.SuitableRoles {
if owned[role] && !used[role] {
compatible = true
}
if owned[role] && used[role] {
hasUsedRole = true
}
}
if compatible {
ids = append(ids, id)
if !hasUsedRole {
safeIDs = append(safeIDs, id)
}
}
}
// The Client can offer every compatible owned role, including one with an
// existing binding. Prefer options without such roles when available so a
// valid-looking selection cannot be rejected as a duplicate. This is a
// server offer policy, not a claim about the original selection algorithm.
if len(safeIDs) > 0 {
ids = safeIDs
}
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
if len(ids) == 0 {
return nil, fmt.Errorf("CW no compatible Spotlight choices for owned roles")
}
// Explicit resource policy, not an invented claim about native weights.
count := int(policy.OfferCount)
if count > len(ids) {
count = len(ids)
}
out := &pb.BDLBHJGNCFM{BKOGIFHEKHH: true, ACKOKJBBPKH: true, DOAHJPPPMGM: policy.FreeRefreshCount}
for i := 0; i < count; i++ {
n, e := rand.Int(rand.Reader, big.NewInt(int64(len(ids)-i)))
if e != nil {
return nil, e
}
j := i + int(n.Int64())
ids[i], ids[j] = ids[j], ids[i]
out.HPKFKBKOADP = append(out.HPKFKBKOADP, &pb.FGEOPPMIHJM{IHHNLLAJAGB: ids[i]})
}
return out, nil
}
func cwSelectSpotlight(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, selection *pb.NIJPHAAONFB) (*pb.GridFightSyncUpdateResultScNotify, error) {
if selection == nil || run.FMIGOKIDKJL == nil || run.FMIGOKIDKJL.GetEEKMIAMGHFL() == nil {
return nil, fmt.Errorf("CW Spotlight is not pending")
}
pending := run.FMIGOKIDKJL
offer := pending.GetEEKMIAMGHFL()
if !offer.BKOGIFHEKHH || offer.GFGJOKJGGCO != 0 {
return nil, fmt.Errorf("CW Spotlight replacement is not a grant")
}
offered := false
for _, v := range offer.HPKFKBKOADP {
offered = offered || v.IHHNLLAJAGB == selection.IHHNLLAJAGB
}
if !offered {
return nil, fmt.Errorf("CW Spotlight choice not offered")
}
rows, _, e := c.SpotlightData()
if e != nil {
return nil, e
}
row, ok := rows[selection.IHHNLLAJAGB]
if !ok {
return nil, fmt.Errorf("CW Spotlight configuration missing")
}
compatible := false
for _, id := range row.SuitableRoles {
compatible = compatible || id == selection.RoleId
}
if !compatible {
return nil, fmt.Errorf("CW Spotlight role is incompatible")
}
player, team, _ := cwComponents(run)
if player == nil || team == nil {
return nil, fmt.Errorf("CW Spotlight player or team missing")
}
var owned *pb.CDEAABODKCP
for _, r := range team.GridGameRoleList {
if r.Id == selection.RoleId && (owned == nil || r.RoleStar > owned.RoleStar || r.RoleStar == owned.RoleStar && r.UniqueId < owned.UniqueId) {
owned = r
}
}
if owned == nil {
return nil, fmt.Errorf("CW Spotlight role not owned")
}
cwEnsureSpotlights(run)
state := cwSpotlights(run)
for _, v := range state.FNNEPMAPOOF {
if v.RoleId == selection.RoleId {
return nil, fmt.Errorf("CW role already has Spotlight")
}
}
uid, e := cwNextEntityID(run)
if e != nil {
return nil, e
}
// AddOrUpdate shuffles proto fields into the binding record; MDNAGANGNKK
// becomes RoleUniqueID, RoleId remains the configuration ID. Native
// GetActiveSpotLightBindingByRoleId + binding constructor corroborate it.
item := &pb.FMBACDOFCAL{UniqueId: uid, IHHNLLAJAGB: row.ID, RoleId: selection.RoleId, MDNAGANGNKK: owned.UniqueId}
state.FNNEPMAPOOF = append(state.FNNEPMAPOOF, item)
stage, node := cwStage(run)
if stage == nil || node == nil || !node.HPNNLMBHOPF {
return nil, fmt.Errorf("CW current node has no unclaimed Spotlight")
}
node.HPNNLMBHOPF = false
var routeNode *pb.BCCIJHPLJOJ
for _, v := range stage.OOMMIIKPHBC {
if v.DCAHJELNJNP == stage.DCAHJELNJNP && v.SectionId == stage.SectionId {
routeNode = v
break
}
}
if routeNode == nil {
return nil, fmt.Errorf("CW current route node missing")
}
run.FMIGOKIDKJL = nil
player.GAJGBBFELEL = &pb.GMBMNFLMKPI{}
return cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_OHHBBOGFDON{OHHBBOGFDON: proto.Clone(item).(*pb.FMBACDOFCAL)}},
&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_KBHOBGHKIBA{KBHOBGHKIBA: &pb.BAAGMMIDHDB{DBIIPEEDBHI: proto.Clone(routeNode).(*pb.BCCIJHPLJOJ)}}}, cwFinish(pending.QueuePosition), cwUnlock()), nil
}
func init() {
cwHandle("GridFightSpotLightEnter", func(s *Session, _ *pb.GridFightSpotLightEnterCsReq, rsp *pb.GridFightSpotLightEnterScRsp) error {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_CONF_MISS)
c, data, e := cwPreparingData(s)
if e != nil {
PacketLog("CW_SPOTLIGHT_ENTER rejected: " + e.Error())
return nil
}
run := data.DHFOFLFCLAH
_, node := cwStage(run)
if node == nil || !node.HPNNLMBHOPF {
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
return nil
}
if s.currencyWarsRevision >= uint64(^uint32(0)) {
return nil
}
offer, e := cwSpotlightChoices(c, run)
if e != nil {
PacketLog("CW_SPOTLIGHT_ENTER rejected: " + e.Error())
return nil
}
cwEnsureSpotlights(run)
pending := &pb.CHCHAJIANIM{QueuePosition: uint32(s.currencyWarsRevision + 1), FGLIODAJFML: &pb.CHCHAJIANIM_EEKMIAMGHFL{EEKMIAMGHFL: offer}}
run.FMIGOKIDKJL = pending
if e = s.saveCurrencyWarsData(data, c); e != nil {
return nil
}
if e = cwSend(s, cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_FMIGOKIDKJL{FMIGOKIDKJL: pending}})); e != nil {
return e
}
rsp.Retcode = 0
PacketLog(fmt.Sprintf("CW_SPOTLIGHT_ENTER offered=%d queue=%d revision=%d", len(offer.HPKFKBKOADP), pending.QueuePosition, s.currencyWarsRevision))
return nil
})
}