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

120 lines
4.6 KiB
Go

package gameserver
import (
"fmt"
"gac-hkrpg-go/internal/common"
pb "gac-hkrpg-go/protocol/proto"
"google.golang.org/protobuf/proto"
"math"
)
// cwAdvanceRouteNode follows gfPendingAdvanceNode -> gfFxHEnterNode in the
// reference workflow. The caller owns the detached snapshot and its single CAS
// commit; this helper grants no rewards and never reads/writes a player save.
// Saved route displays retain run-specific Tea Break/spotlight substitutions.
func cwAdvanceRouteNode(catalog *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH) (*pb.GridFightSyncUpdateResultScNotify, error) {
if run == nil || run.FMIGOKIDKJL == nil {
return nil, fmt.Errorf("CW advance requires current pending queue")
}
queue := run.FMIGOKIDKJL.QueuePosition
if queue == math.MaxUint32 {
return nil, fmt.Errorf("CW pending queue exhausted")
}
stage, _ := cwStage(run)
if stage == nil {
return nil, fmt.Errorf("CW advance stage missing")
}
route, err := catalog.RouteNodes(stage.EMOHLJHPOPC)
if err != nil {
return nil, err
}
index := -1
for i, row := range route {
if row.ChapterID == stage.DCAHJELNJNP && row.SectionID == stage.SectionId {
index = i
break
}
}
if index < 0 || index+1 >= len(route) {
return nil, fmt.Errorf("CW route has no next node")
}
next := route[index+1]
var display *pb.OHMDHKGAEMM
for _, node := range stage.OOMMIIKPHBC {
if node != nil && node.DCAHJELNJNP == next.ChapterID && node.SectionId == next.SectionID {
if display != nil {
return nil, fmt.Errorf("CW duplicate next route display")
}
display = node.DisplayValue
}
}
if display == nil {
return nil, fmt.Errorf("CW next route display missing")
}
refs, err := common.LoadCurrencyWarsReferenceNodes(catalog)
if err != nil {
return nil, err
}
template, err := refs.Template(next.NodeTemplateID)
if err != nil {
return nil, err
}
if template.StageID != display.StageId {
return nil, fmt.Errorf("CW next template/display stage differs")
}
// Build against a temporary run at the destination: the enemy constructor
// selects that chapter's saved boss/camp, leaving the source untouched if a
// recipe fails validation. Dynamic non-enemy nodes need their own factory.
shadow := proto.Clone(run).(*pb.MPIMAEMCEFH)
shadowStage, _ := cwStage(shadow)
shadowStage.DCAHJELNJNP = next.ChapterID
shadowStage.SectionId = next.SectionID
shadowStage.IMPNAHBCADN = nil
layer, err := cwGenerateReferenceEnemyLayer(catalog, shadow, display)
if err != nil {
return nil, err
}
if layer == nil {
return nil, fmt.Errorf("CW generated next node layer missing")
}
pending := &pb.CHCHAJIANIM{QueuePosition: queue + 1, FGLIODAJFML: &pb.CHCHAJIANIM_NNDGEJFLGGF{NNDGEJFLGGF: &pb.DPHPAKFLNOD{}}}
stage.DCAHJELNJNP = next.ChapterID
stage.SectionId = next.SectionID
stage.IMPNAHBCADN = layer
run.FMIGOKIDKJL = pending
return cwNotify(cwFinish(queue), &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_OOICHLAMNIG{OOICHLAMNIG: &pb.AINFNFIDGFF{DCAHJELNJNP: stage.DCAHJELNJNP, SectionId: stage.SectionId, IMPNAHBCADN: layer}}}, &pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_FMIGOKIDKJL{FMIGOKIDKJL: pending}}), nil
}
// cwGeneratedRoundPreparation handles the same RoundBegin -> shop refresh ->
// ReturnPreparation transition as the captured initial rounds, without replaying
// an unrelated node's opaque wire or its fixed queue. Restrict this fallback to
// constructed enemy layers; other node families keep their typed handlers.
func cwGeneratedRoundPreparation(catalog *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH) (*pb.CHCHAJIANIM, error) {
if run == nil || run.GetFMIGOKIDKJL().GetNNDGEJFLGGF() == nil {
return nil, fmt.Errorf("CW generated round is not RoundBegin")
}
stage, display := cwStage(run)
if stage == nil || display == nil || stage.GetIMPNAHBCADN().GetMPOBBIPMNNJ() == nil {
return nil, fmt.Errorf("CW generated RoundBegin enemy layer missing")
}
queue := run.FMIGOKIDKJL.QueuePosition
if queue == math.MaxUint32 {
return nil, fmt.Errorf("CW pending queue exhausted")
}
if display.HPNNLMBHOPF {
offer, err := cwSpotlightChoices(catalog, run)
if err != nil {
return nil, err
}
return &pb.CHCHAJIANIM{QueuePosition: queue + 1, FGLIODAJFML: &pb.CHCHAJIANIM_EEKMIAMGHFL{EEKMIAMGHFL: offer}}, nil
}
name, err := catalog.NodeTypeName(display.CJCCMAACJDB)
if err != nil {
return nil, err
}
if name == "EliteBranch" && stage.IMPNAHBCADN.GetMPOBBIPMNNJ().MOADBALHMFD == 0 {
return &pb.CHCHAJIANIM{QueuePosition: queue + 1, FGLIODAJFML: &pb.CHCHAJIANIM_BAKAKODKECM{BAKAKODKECM: &pb.HNPHLAHJPOB{}}}, nil
}
return &pb.CHCHAJIANIM{QueuePosition: queue + 1, FGLIODAJFML: &pb.CHCHAJIANIM_LGNPHHBGKFH{LGNPHHBGKFH: &pb.HJPDALHDJHC{}}}, nil
}