120 lines
4.6 KiB
Go
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
|
|
}
|