Update AA synchronization, CW progression and diagnostics

This commit is contained in:
CAtlas0784 committed 2026-10-30 19:19:05 +07:00
1 parent 67a819a4d3
commit 30bee81fe9
30 files changed
+1143 -55

No files matched your search

@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"math/rand/v2"
"sort"
"gac-hkrpg-go/internal/common"
pb "gac-hkrpg-go/protocol/proto"
@@ -19,18 +20,19 @@ type cwReferenceEnemies struct {
Reference string `json:"reference_executable_sha256"`
Excel string `json:"excel_dump_sha256"`
Policy struct {
FirstChapter uint32 `json:"first_chapter"`
FirstStar uint32 `json:"first_chapter_boss_star"`
LaterStar uint32 `json:"later_chapter_boss_star"`
BaseElite uint32 `json:"base_elite_group"`
AlternateThreshold uint32 `json:"alternate_route_threshold"`
AlternateElite uint32 `json:"alternate_base_elite_group"`
CampMaxTier uint32 `json:"camp_monster_max_tier"`
MonsterMaxTier uint32 `json:"monster_max_tier"`
FirstMonsterCount uint32 `json:"first_monster_count"`
LaterMonsterCount uint32 `json:"later_monster_count"`
CampWaveCounts [][]uint32 `json:"camp_wave_counts"`
ChapterStarWeights [][]uint32 `json:"chapter_star_weights"`
NeutralCampCompatibility string `json:"neutral_camp_compatibility"`
FirstChapter uint32 `json:"first_chapter"`
FirstStar uint32 `json:"first_chapter_boss_star"`
LaterStar uint32 `json:"later_chapter_boss_star"`
BaseElite uint32 `json:"base_elite_group"`
AlternateThreshold uint32 `json:"alternate_route_threshold"`
AlternateElite uint32 `json:"alternate_base_elite_group"`
CampMaxTier uint32 `json:"camp_monster_max_tier"`
MonsterMaxTier uint32 `json:"monster_max_tier"`
FirstMonsterCount uint32 `json:"first_monster_count"`
LaterMonsterCount uint32 `json:"later_monster_count"`
CampWaveCounts [][]uint32 `json:"camp_wave_counts"`
ChapterStarWeights [][]uint32 `json:"chapter_star_weights"`
} `json:"policy"`
MonsterTiers map[uint32]uint32 `json:"monster_tiers"`
EliteGroups []uint32 `json:"elite_groups"`
@@ -89,7 +91,7 @@ func cwGenerateReferenceEnemyLayer(c *common.CurrencyWarsCatalog, run *pb.MPIMAE
if e != nil {
return nil, e
}
if display == nil || (t.NodeType != "Boss" && t.NodeType != "CampMonster") || display.StageId != t.StageID {
if display == nil || (t.NodeType != "Boss" && t.NodeType != "CampMonster" && t.NodeType != "Monster") || display.StageId != t.StageID {
return nil, fmt.Errorf("CW reference enemy factory does not support node type %s", t.NodeType)
}
r, e := cwLoadReferenceEnemies(c)
@@ -99,6 +101,9 @@ func cwGenerateReferenceEnemyLayer(c *common.CurrencyWarsCatalog, run *pb.MPIMAE
if t.NodeType == "CampMonster" {
return cwGenerateReferenceCampLayer(c, run, s, t, r)
}
if t.NodeType == "Monster" {
return cwGenerateReferenceMonsterLayer(c, run, s, t, r)
}
if s.DCAHJELNJNP == 0 || s.BossInfo == nil || len(s.BossInfo.NCGHKPEMPDP) == 0 || len(s.HEKMEJLMGFF) == 0 {
return nil, fmt.Errorf("CW saved chapter boss/camp selection missing")
}
@@ -140,6 +145,84 @@ func cwGenerateReferenceEnemyLayer(c *common.CurrencyWarsCatalog, run *pb.MPIMAE
return &pb.NCKOKDHNJDC{EFECLPJFOCM: &pb.NCKOKDHNJDC_MPOBBIPMNNJ{MPOBBIPMNNJ: layer}}, nil
}
// The original server's neutral camp is a run-owned field absent from our
// captured wire. Until that identity can be restored from the Client, select
// one eligible captured season camp deterministically per run. This is the
// declared compatibility policy, not a claim about the original camp roll.
func cwGenerateReferenceMonsterLayer(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, s *pb.BEEFLEJAOCN, t common.CurrencyWarsReferenceNodeTemplate, r *cwReferenceEnemies) (*pb.NCKOKDHNJDC, error) {
if run == nil || run.UniqueId == 0 || s == nil || s.DCAHJELNJNP == 0 || r.Policy.NeutralCampCompatibility != "stable_random_enabled_season_camp_by_run_id" {
return nil, fmt.Errorf("CW neutral camp compatibility policy or run identity missing")
}
type eligible struct {
id uint32
monsters []uint32
}
var camps []eligible
for _, camp := range c.MonsterCamps {
if camp.SeasonID != run.LJNGPCKPJLF || camp.Common.IfRandomEnabled == 0 {
continue
}
var monsters []uint32
for _, id := range camp.Season.MonsterList {
if tier, ok := r.MonsterTiers[id]; ok && tier > 0 && tier <= r.Policy.MonsterMaxTier {
monsters = append(monsters, id)
}
}
if len(monsters) != 0 {
camps = append(camps, eligible{camp.ID, monsters})
}
}
if len(camps) == 0 {
return nil, fmt.Errorf("CW no captured neutral camp with eligible monsters")
}
sort.Slice(camps, func(i, j int) bool { return camps[i].id < camps[j].id })
chosen := camps[uint64(run.UniqueId)%uint64(len(camps))]
previous, err := cwReferenceEarlierCount(c, s, "Monster", false)
if err != nil {
return nil, err
}
count := r.Policy.LaterMonsterCount
if previous == 0 {
count = r.Policy.FirstMonsterCount
}
if count == 0 || len(r.Policy.ChapterStarWeights) == 0 {
return nil, fmt.Errorf("CW neutral monster policy incomplete")
}
chapter := int(s.DCAHJELNJNP) - 1
if chapter >= len(r.Policy.ChapterStarWeights) {
chapter = len(r.Policy.ChapterStarWeights) - 1
}
weights := r.Policy.ChapterStarWeights[chapter]
var total uint32
for _, weight := range weights {
if ^uint32(0)-total < weight {
return nil, fmt.Errorf("CW monster star weights overflow")
}
total += weight
}
if total == 0 {
return nil, fmt.Errorf("CW monster star weights empty")
}
wave := &pb.PNAKHANCNGK{OCPEPEMNNEK: 1}
for i := uint32(0); i < count; i++ {
roll := rand.Uint32N(total)
var star uint32
for j, weight := range weights {
if roll < weight {
star = uint32(j + 1)
break
}
roll -= weight
}
if star == 0 {
return nil, fmt.Errorf("CW monster star roll unresolved")
}
wave.PBLBBDBDLEK = append(wave.PBLBBDBDLEK, &pb.AHKDPCIIELA{MonsterId: chosen.monsters[rand.IntN(len(chosen.monsters))], RoleStar: star})
}
branch := &pb.KKBGIPCAGGH{IFIIOIIKJHO: t.PenaltyBonusRuleID, MonsterWaveList: []*pb.PNAKHANCNGK{wave}}
return &pb.NCKOKDHNJDC{EFECLPJFOCM: &pb.NCKOKDHNJDC_MPOBBIPMNNJ{MPOBBIPMNNJ: &pb.MDJBBILMHLC{LNDKCPBJDKH: chosen.id, MOADBALHMFD: 1, BPDABHGFBFO: []*pb.KKBGIPCAGGH{branch}}}}, nil
}
func cwReferenceEarlierCount(c *common.CurrencyWarsCatalog, s *pb.BEEFLEJAOCN, kind string, sameChapter bool) (uint32, error) {
rows, e := c.RouteNodes(s.EMOHLJHPOPC)
if e != nil {