feat(currency_wars): complete systems, settlement Rank SS, damage report, camera and recovery fixes

This commit is contained in:
CAtlas0784 committed 2026-10-31 20:47:36 +07:00
1 parent c008243f11
commit 0995055296
78 files changed
+33840 -9525

No files matched your search

+150 -30
View File
@@ -19,16 +19,19 @@ func currencyWarsAbandonRun(data *pb.GridFightGetDataScRsp) (uint32, bool) {
return runID, true
}
// currencyWarsQuitSettlement builds the ordered server notifications expected
// before GridFightQuitSettleScRsp. All packet IDs and wire tags come from the
// current generated protocol; values are copied from the saved run/catalog.
func currencyWarsQuitSettlement(catalog *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH) (*pb.GridFightSyncKeepWinCntNotify, *pb.GridFightSettleNotify, error) {
func currencyWarsQuitSettlement(catalog *common.CurrencyWarsCatalog, data *pb.GridFightGetDataScRsp, run *pb.MPIMAEMCEFH) (*pb.GridFightSyncKeepWinCntNotify, *pb.GridFightSettleNotify, error) {
return currencyWarsTerminalSettlement(catalog, data, run, pb.PEIJNEFOCHI_PEIJNEFOCHI_NOCIJDINKPJ, false, nil)
}
// currencyWarsTerminalSettlement builds the ordered server notifications expected
// when a run ends (quit, defeat, or victory).
func currencyWarsTerminalSettlement(catalog *common.CurrencyWarsCatalog, data *pb.GridFightGetDataScRsp, run *pb.MPIMAEMCEFH, reason pb.PEIJNEFOCHI, isWon bool, damageList []*pb.PLFKMENCDMM) (*pb.GridFightSyncKeepWinCntNotify, *pb.GridFightSettleNotify, error) {
if catalog == nil || run == nil || run.UniqueId == 0 {
return nil, nil, fmt.Errorf("CW quit settlement is missing catalog or active run")
return nil, nil, fmt.Errorf("CW settlement is missing catalog or active run")
}
player, team, _ := cwComponents(run)
if player == nil || team == nil {
return nil, nil, fmt.Errorf("CW quit settlement is missing player/team components")
return nil, nil, fmt.Errorf("CW settlement is missing player/team components")
}
var stage *pb.BEEFLEJAOCN
for _, component := range run.NAGDJGMMCBE {
@@ -37,13 +40,13 @@ func currencyWarsQuitSettlement(catalog *common.CurrencyWarsCatalog, run *pb.MPI
}
if current := component.GetKLCPAAHDPJF(); current != nil {
if stage != nil {
return nil, nil, fmt.Errorf("CW quit settlement has duplicate stage components")
return nil, nil, fmt.Errorf("CW settlement has duplicate stage components")
}
stage = current
}
}
if stage == nil {
return nil, nil, fmt.Errorf("CW quit settlement is missing the current stage component")
return nil, nil, fmt.Errorf("CW settlement is missing the current stage component")
}
avatarIDs := make([]uint32, 0, len(team.GridGameRoleList))
@@ -72,22 +75,88 @@ func currencyWarsQuitSettlement(catalog *common.CurrencyWarsCatalog, run *pb.MPI
return roles[i].UniqueId < roles[j].UniqueId
})
var highestDivision common.CurrencyWarsDivision
for _, division := range catalog.SeasonDivisions(catalog.ActiveSeasonID) {
if division.DivisionLevel > highestDivision.DivisionLevel ||
(division.DivisionLevel == highestDivision.DivisionLevel && division.Progress > highestDivision.Progress) {
highestDivision = division
// Rank calculation based on remaining HP percentage (GridFight_SettleRankHpConfig: [1, 40, 70])
// Rank C = 0, Rank B = 1, Rank A = 2, Rank S = 3, Rank SS = 4
var rankType uint32
var divisionUp uint32
if isWon && player.PPKKHHNEOHB > 0 && player.GridFightLineupHp > 0 {
hpPercent := float64(player.GridFightLineupHp) / float64(player.PPKKHHNEOHB) * 100.0
if hpPercent >= 100.0 || player.GridFightLineupHp >= player.PPKKHHNEOHB {
rankType = 4 // Rank SS (flawless / 100% HP)
divisionUp = 3
} else if hpPercent >= 70.0 {
rankType = 3 // Rank S
divisionUp = 3
} else if hpPercent >= 40.0 {
rankType = 2 // Rank A
divisionUp = 2
} else {
rankType = 1 // Rank B
divisionUp = 1
}
} else {
rankType = 0
divisionUp = 0
}
seasonDivisions := catalog.SeasonDivisions(catalog.ActiveSeasonID)
curDivisionID := run.DivisionId
newDivisionID := curDivisionID
isPromotion := false
if isWon && divisionUp > 0 && len(seasonDivisions) > 0 {
curIdx := -1
for i, d := range seasonDivisions {
if d.ID == curDivisionID {
curIdx = i
break
}
}
if curIdx >= 0 {
targetIdx := curIdx + int(divisionUp)
if targetIdx >= len(seasonDivisions) {
targetIdx = len(seasonDivisions) - 1
}
newDivisionID = seasonDivisions[targetIdx].ID
if seasonDivisions[targetIdx].DivisionLevel > seasonDivisions[curIdx].DivisionLevel {
isPromotion = true
}
}
}
if highestDivision.ID == 0 {
return nil, nil, fmt.Errorf("CW quit settlement has no active-season division data")
var equipmentList []*pb.IJDLENJJBHJ
if bag := cwEquipmentBag(run); bag != nil {
equipmentList = bag.GridFightEquipmentList
}
var augments []*pb.PPGEBEBPMJC
for _, component := range run.NAGDJGMMCBE {
if component == nil {
continue
}
if owned := component.GetLLDBOIANGDN(); owned != nil {
augments = owned.SyncAugmentInfo
break
}
}
var teamValue uint32
for _, r := range team.GridGameRoleList {
if r != nil {
teamValue += uint32(r.RoleStar) * 2
}
}
finish := &pb.OEDHMAHDECC{
// GridFightSettleReason value 1 is Quit in the Client enum.
Reason: pb.PEIJNEFOCHI_PEIJNEFOCHI_NOCIJDINKPJ,
IsOverlock: run.IsOverlock,
JLDDDLDNFEF: run.JLDDDLDNFEF,
Reason: reason,
IsOverlock: run.IsOverlock,
JLDDDLDNFEF: run.JLDDDLDNFEF,
LLJJCOOKANH: rankType,
MBMJNEDIFFC: divisionUp,
DBODGPBOIHC: isPromotion,
EEGPILJDLBG: damageList,
GridFightEquipmentList: equipmentList,
SyncAugmentInfo: augments,
AKEGLJLCFPH: teamValue,
NDLIKNEKDLL: &pb.GOCIGPCDKDJ{
LKCJAPIGEND: player.PPKKHHNEOHB,
PAGPKOKMDBK: player.GridFightLineupHp,
@@ -95,21 +164,72 @@ func currencyWarsQuitSettlement(catalog *common.CurrencyWarsCatalog, run *pb.MPI
EMOHLJHPOPC: stage.EMOHLJHPOPC,
AACGBEBGDPE: run.DivisionId,
SectionId: stage.SectionId,
FDGKBOIEHBD: player.NDAINFLOLPJ,
FMEOHKHGMJM: []uint32{catalog.ActiveSeasonModule().ActivityModuleID},
},
PPJFPNMIFHL: &pb.GLPCEKMKLGG{GridFightAvatarList: avatarIDs},
GridGameRoleList: roles,
GridGameNpcList: team.GridGameNpcList,
BossInfo: stage.BossInfo,
CFFCNEDGCJJ: stage.CFFCNEDGCJJ,
OAIGAGABCMK: stage.GridFightPortalBuffList,
PPJFPNMIFHL: &pb.GLPCEKMKLGG{GridFightAvatarList: avatarIDs},
GridGameRoleList: roles,
GridGameNpcList: team.GridGameNpcList,
BossInfo: stage.BossInfo,
CFFCNEDGCJJ: stage.CFFCNEDGCJJ,
OAIGAGABCMK: stage.GridFightPortalBuffList,
GridFightTraitInfo: cwCalculateBoardTraits(catalog, run),
}
return &pb.GridFightSyncKeepWinCntNotify{EHBLJGIENPI: player.GDAKCOICIDA}, &pb.GridFightSettleNotify{
var weeklyScore, expBase, expExtra, totalExp uint32
if scoreCatalog, err := common.LoadCurrencyWarsScore(catalog); err == nil {
weeklyScore, expBase, expExtra = scoreCatalog.CalculateRunScore(run.DivisionId, run.IsOverlock, stage.DCAHJELNJNP, stage.SectionId, isWon)
totalExp = expBase + expExtra
}
var curSeasonExp, curTalentCoin uint32
if data != nil && data.LFLCDNGALME != nil && data.LFLCDNGALME.HFPEJIDDDJG != nil {
if data.LFLCDNGALME.HFPEJIDDDJG.Exp != nil {
curSeasonExp = data.LFLCDNGALME.HFPEJIDDDJG.Exp.KEJGCDOGEDD
}
curTalentCoin = data.LFLCDNGALME.HFPEJIDDDJG.GEBGGMAGDJB
}
newSeasonExp := curSeasonExp + totalExp
seasonLevel := (newSeasonExp / 800) + 1
maxExp := catalog.ActiveSeasonModule().MaxRewardExp
maxLevel := maxExp / 800
if maxLevel == 0 {
maxLevel = 60
}
if seasonLevel > maxLevel {
seasonLevel = maxLevel
}
cwRecordArchive(finish, catalog.ActiveSeasonID, catalog.ActiveSubSeasonID)
if data != nil && data.LFLCDNGALME != nil && data.LFLCDNGALME.HFPEJIDDDJG != nil {
if data.LFLCDNGALME.HFPEJIDDDJG.Exp == nil {
data.LFLCDNGALME.HFPEJIDDDJG.Exp = &pb.GLIFOLCEMIN{}
}
data.LFLCDNGALME.HFPEJIDDDJG.Exp.KEJGCDOGEDD = newSeasonExp
if newDivisionID > data.LFLCDNGALME.HFPEJIDDDJG.DivisionId {
data.LFLCDNGALME.HFPEJIDDDJG.DivisionId = newDivisionID
}
data.LFLCDNGALME.HFPEJIDDDJG.GDAKCOICIDA = player.GDAKCOICIDA
}
settleNotify := &pb.GridFightSettleNotify{
FLHOPMMEGAI: run.DivisionId,
CDANKDNACJP: highestDivision.ID,
NMCKJPEMACM: catalog.ActiveSeasonModule().MaxRewardExp,
CDANKDNACJP: newDivisionID,
NMCKJPEMACM: seasonLevel,
EDHKHJAPMKA: weeklyScore,
CGKNDIJMOCJ: expBase,
MNNKKEPCPPL: expExtra,
KEJGCDOGEDD: newSeasonExp,
GEBGGMAGDJB: curTalentCoin,
BFLLJJBHLDF: finish,
}, nil
}
if data == nil {
settleNotify.NMCKJPEMACM = catalog.ActiveSeasonModule().MaxRewardExp
settleNotify.KEJGCDOGEDD = totalExp
}
return &pb.GridFightSyncKeepWinCntNotify{EHBLJGIENPI: player.GDAKCOICIDA}, settleNotify, nil
}
func init() {
@@ -143,7 +263,7 @@ func init() {
return nil
}
runID := run.UniqueId
keepWinCount, settlement, err := currencyWarsQuitSettlement(catalog, run)
keepWinCount, settlement, err := currencyWarsQuitSettlement(catalog, data, run)
if err != nil {
PacketLog(fmt.Sprintf("CW_QUIT_SETTLE rejected run=%d reason=settlement_build_error error=%v", runID, err))
return nil