2026-10-31 20:47:36 +07:00
|
|
|
package gameserver
|
|
|
|
|
|
|
|
|
|
import (
|
2026-11-01 20:14:36 +07:00
|
|
|
"bytes"
|
2026-10-31 20:47:36 +07:00
|
|
|
"os"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"gac-hkrpg-go/internal/common"
|
|
|
|
|
pb "gac-hkrpg-go/protocol/proto"
|
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestCurrencyWarsTalentsHandlers(t *testing.T) {
|
2026-11-01 20:14:36 +07:00
|
|
|
before, err := os.ReadFile("persistent")
|
2026-10-31 20:47:36 +07:00
|
|
|
if err != nil {
|
2026-11-01 20:14:36 +07:00
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
s := &Session{Token: 12345}
|
|
|
|
|
requests := map[string]proto.Message{
|
|
|
|
|
"GridFightPermanentTalentEnableCsReq": &pb.GridFightPermanentTalentEnableCsReq{CKHCLKOALKE: ^uint32(0)},
|
|
|
|
|
"GridFightSeasonTalentEnableCsReq": &pb.GridFightSeasonTalentEnableCsReq{CKHCLKOALKE: ^uint32(0)},
|
|
|
|
|
"GridFightPermanentTalentResetCsReq": &pb.GridFightPermanentTalentResetCsReq{},
|
|
|
|
|
"GridFightSeasonTalentResetCsReq": &pb.GridFightSeasonTalentResetCsReq{},
|
|
|
|
|
}
|
|
|
|
|
for name, req := range requests {
|
|
|
|
|
raw, _ := proto.Marshal(req)
|
|
|
|
|
if err := typedHandlers[uint16(pb.CmdIDByName[name])](s, raw); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
after, err := os.ReadFile("persistent")
|
2026-10-31 20:47:36 +07:00
|
|
|
if err != nil {
|
2026-11-01 20:14:36 +07:00
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(before, after) {
|
|
|
|
|
t.Fatalf("unsupported talent request changed persistent: %s", name)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
|
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
}
|
2026-10-31 20:47:36 +07:00
|
|
|
|
2026-11-01 20:14:36 +07:00
|
|
|
func TestCurrencyWarsArchiveHandlers(t *testing.T) {
|
|
|
|
|
c, err := common.LoadCurrencyWarsCatalog()
|
2026-10-31 20:47:36 +07:00
|
|
|
if err != nil {
|
2026-11-01 20:14:36 +07:00
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
data, revision, err := readCurrencyWarsData(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
if data == nil {
|
|
|
|
|
data, err = newCurrencyWarsData(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
|
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
session := &Session{Token: 54321, currencyWarsRevision: revision}
|
|
|
|
|
record := &pb.OEDHMAHDECC{Reason: pb.PEIJNEFOCHI_PEIJNEFOCHI_JOJPGBCECLN}
|
|
|
|
|
if err := session.saveCurrencyWarsTerminal(data, c, record); err != nil {
|
|
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
snapshot, err := cwArchiveSnapshot(c)
|
2026-10-31 20:47:36 +07:00
|
|
|
if err != nil {
|
2026-11-01 20:14:36 +07:00
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
entries, err := cwReadArchives(snapshot)
|
|
|
|
|
if err != nil || len(entries) == 0 {
|
|
|
|
|
t.Fatalf("read archive: %v", err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
entry := entries[len(entries)-1]
|
|
|
|
|
raw, _ := proto.Marshal(&pb.FavourArchiveCsReq{BAAGDILLLME: entry.BAAGDILLLME, IECKKCHJMLP: true})
|
|
|
|
|
if err := typedHandlers[uint16(pb.CmdIDByName["FavourArchiveCsReq"])](session, raw); err != nil {
|
|
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
// A new read/session has no in-memory archive state to rely on.
|
|
|
|
|
snapshot, err = cwArchiveSnapshot(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
entries, err = cwReadArchives(snapshot)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
if !entries[len(entries)-1].IECKKCHJMLP {
|
|
|
|
|
t.Fatal("favourite was not persisted")
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
stale := &Session{currencyWarsRevision: revision}
|
|
|
|
|
if err := stale.saveCurrencyWarsTerminal(data, c, record); err == nil {
|
|
|
|
|
t.Fatal("stale settlement appended archive")
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
again, err := cwArchiveSnapshot(c)
|
2026-10-31 20:47:36 +07:00
|
|
|
if err != nil {
|
2026-11-01 20:14:36 +07:00
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
if len(again.Archives) != len(snapshot.Archives) {
|
|
|
|
|
t.Fatal("failed transaction changed archives")
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
if err := session.saveCurrencyWarsData(data, c); err != nil {
|
|
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
again, err = cwArchiveSnapshot(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
2026-11-01 20:14:36 +07:00
|
|
|
if len(again.Archives) != len(snapshot.Archives) {
|
|
|
|
|
t.Fatal("ordinary save lost archives")
|
2026-10-31 20:47:36 +07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrencyWarsCalculateBoardTraits(t *testing.T) {
|
|
|
|
|
catalog, err := common.LoadCurrencyWarsCatalog()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("load catalog: %v", err)
|
|
|
|
|
}
|
|
|
|
|
// Role 100402 (Welt) in Season 2 has TraitList: [ 100502, 200302 ]
|
|
|
|
|
run := &pb.MPIMAEMCEFH{
|
|
|
|
|
NAGDJGMMCBE: []*pb.GridFightGameInfo{
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_EJIPINIKNCK{
|
|
|
|
|
EJIPINIKNCK: &pb.HKFECLCHBFJ{
|
|
|
|
|
GridGameRoleList: []*pb.CDEAABODKCP{
|
|
|
|
|
{Id: 100402, RoleStar: 1, Pos: 1, UniqueId: 101},
|
|
|
|
|
{Id: 100402, RoleStar: 1, Pos: 2, UniqueId: 102},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
traits := cwCalculateBoardTraits(catalog, run)
|
|
|
|
|
if len(traits) == 0 {
|
|
|
|
|
t.Fatal("expected computed traits for deployed roles")
|
|
|
|
|
}
|
|
|
|
|
foundTrait := false
|
|
|
|
|
for _, tr := range traits {
|
|
|
|
|
if tr.TraitId == 100502 || tr.TraitId == 200302 {
|
|
|
|
|
foundTrait = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !foundTrait {
|
|
|
|
|
t.Errorf("expected Welt traits to be computed, got %v", traits)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrencyWarsOrbDropsAndClaim(t *testing.T) {
|
|
|
|
|
origPersistent, _ := os.ReadFile("persistent")
|
|
|
|
|
if origPersistent != nil {
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
_ = os.WriteFile("persistent", origPersistent, 0644)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
catalog, err := common.LoadCurrencyWarsCatalog()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("load catalog: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1. Test cwRollNodeOrbs for various stages in Route 2801
|
|
|
|
|
// 1-1: 900 -> 4 rolls
|
|
|
|
|
orbs1_1, err := cwRollNodeOrbs(catalog, nil, 2801, 1, 1, 100)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwRollNodeOrbs 1-1: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if len(orbs1_1) != 4 {
|
|
|
|
|
t.Errorf("expected 4 orbs from 1-1, got %d", len(orbs1_1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Progress 50% on 1-1: (4*50 + 99)/100 = 2 orbs
|
|
|
|
|
orbs1_1_half, err := cwRollNodeOrbs(catalog, nil, 2801, 1, 1, 50)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwRollNodeOrbs 1-1 half: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if len(orbs1_1_half) != 2 {
|
|
|
|
|
t.Errorf("expected 2 orbs from 1-1 at 50%% progress, got %d", len(orbs1_1_half))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1-5 (monster 902 in route 2801): 8 rolls
|
|
|
|
|
orbs1_5, err := cwRollNodeOrbs(catalog, nil, 2801, 1, 5, 100)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwRollNodeOrbs 1-5: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if len(orbs1_5) != 8 {
|
|
|
|
|
t.Errorf("expected 8 orbs from 1-5, got %d", len(orbs1_5))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2-4 (monster 903): 10 rolls
|
|
|
|
|
orbs2_4, err := cwRollNodeOrbs(catalog, nil, 2801, 2, 4, 100)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwRollNodeOrbs 2-4: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if len(orbs2_4) != 10 {
|
|
|
|
|
t.Errorf("expected 10 orbs from 2-4, got %d", len(orbs2_4))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3-4 (monster 904): 6 rolls
|
|
|
|
|
orbs3_4, err := cwRollNodeOrbs(catalog, nil, 2801, 3, 4, 100)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwRollNodeOrbs 3-4: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if len(orbs3_4) != 6 {
|
|
|
|
|
t.Errorf("expected 6 orbs from 3-4, got %d", len(orbs3_4))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Non-combat / Supply node (1-3): 0 orbs
|
|
|
|
|
orbs1_3, err := cwRollNodeOrbs(catalog, nil, 2801, 1, 3, 100)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwRollNodeOrbs 1-3: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if len(orbs1_3) != 0 {
|
|
|
|
|
t.Errorf("expected 0 orbs from supply node 1-3, got %d", len(orbs1_3))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Test cwUseOrbs with Orb 305 and multiple orbs
|
|
|
|
|
session := &Session{Token: 99887}
|
|
|
|
|
orbBag := &pb.DBONCGFHHDN{
|
|
|
|
|
MDKGCIKOMLI: []*pb.IBPDPCOGDEP{
|
|
|
|
|
{UniqueId: 5001, LDBNOMPCOKK: 102},
|
|
|
|
|
{UniqueId: 5002, LDBNOMPCOKK: 103},
|
|
|
|
|
{UniqueId: 5003, LDBNOMPCOKK: 305},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
data, err := newCurrencyWarsData(catalog)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("new data: %v", err)
|
|
|
|
|
}
|
|
|
|
|
testRun := &pb.MPIMAEMCEFH{
|
|
|
|
|
UniqueId: 99887,
|
|
|
|
|
LJNGPCKPJLF: catalog.ActiveSeasonID,
|
|
|
|
|
DivisionId: data.LFLCDNGALME.HFPEJIDDDJG.DivisionId,
|
|
|
|
|
NAGDJGMMCBE: []*pb.GridFightGameInfo{
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_KLCPAAHDPJF{
|
|
|
|
|
KLCPAAHDPJF: &pb.BEEFLEJAOCN{
|
|
|
|
|
DCAHJELNJNP: 1,
|
|
|
|
|
SectionId: 1,
|
|
|
|
|
EMOHLJHPOPC: 2801,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_FAOCFBOFCJP{
|
|
|
|
|
FAOCFBOFCJP: &pb.NIHNGPKHEPC{
|
|
|
|
|
Gold: 100,
|
|
|
|
|
MICEOJBAKGN: 4,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_EJIPINIKNCK{
|
|
|
|
|
EJIPINIKNCK: &pb.HKFECLCHBFJ{},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_GCKLIONKMJC{
|
|
|
|
|
GCKLIONKMJC: &pb.CONONEALNOF{},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_EBHDIOGALIE{
|
|
|
|
|
EBHDIOGALIE: &pb.POCJDMMANBM{},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_DBAGKJNPDOL{
|
|
|
|
|
DBAGKJNPDOL: orbBag,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
data.DHFOFLFCLAH = testRun
|
|
|
|
|
_, rev, _ := readCurrencyWarsData(catalog)
|
|
|
|
|
session.currencyWarsRevision = rev
|
|
|
|
|
if err := session.saveCurrencyWarsData(data, catalog); err != nil {
|
|
|
|
|
t.Fatalf("save initial data with orbs: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Claim all orbs
|
|
|
|
|
useReq := &pb.IKMOCJBMGOJ{IsGetAll: true}
|
|
|
|
|
if err := cwUseOrbs(session, useReq); err != nil {
|
|
|
|
|
t.Fatalf("cwUseOrbs with IsGetAll: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify bag in saved data is now empty
|
|
|
|
|
saved, _, err := readCurrencyWarsData(catalog)
|
|
|
|
|
if err != nil || saved == nil || saved.DHFOFLFCLAH == nil {
|
|
|
|
|
t.Fatalf("read saved data: %v", err)
|
|
|
|
|
}
|
|
|
|
|
var savedBag *pb.DBONCGFHHDN
|
|
|
|
|
for _, c := range saved.DHFOFLFCLAH.NAGDJGMMCBE {
|
|
|
|
|
if b := c.GetDBAGKJNPDOL(); b != nil {
|
|
|
|
|
savedBag = b
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if savedBag != nil && len(savedBag.MDKGCIKOMLI) != 0 {
|
|
|
|
|
t.Errorf("expected 0 orbs left in bag, got %d", len(savedBag.MDKGCIKOMLI))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Test idempotency: clicking already consumed or ghost orbs must NOT return error (avoids 6900 error toast)
|
|
|
|
|
ghostReq := &pb.IKMOCJBMGOJ{CBMGDBJHLHO: []uint32{5001, 5002, 5003}}
|
|
|
|
|
if err := cwUseOrbs(session, ghostReq); err != nil {
|
|
|
|
|
t.Fatalf("cwUseOrbs on already consumed orbs returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calling IsGetAll when bag is already empty must also succeed cleanly
|
|
|
|
|
if err := cwUseOrbs(session, &pb.IKMOCJBMGOJ{IsGetAll: true}); err != nil {
|
|
|
|
|
t.Fatalf("cwUseOrbs with IsGetAll on empty bag returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrencyWarsTerminalSettlementLogic(t *testing.T) {
|
|
|
|
|
catalog, err := common.LoadCurrencyWarsCatalog()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("load catalog: %v", err)
|
|
|
|
|
}
|
|
|
|
|
run := &pb.MPIMAEMCEFH{
|
|
|
|
|
UniqueId: 999,
|
|
|
|
|
LJNGPCKPJLF: catalog.ActiveSeasonID,
|
|
|
|
|
DivisionId: 1,
|
|
|
|
|
NAGDJGMMCBE: []*pb.GridFightGameInfo{
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_KLCPAAHDPJF{
|
|
|
|
|
KLCPAAHDPJF: &pb.BEEFLEJAOCN{
|
|
|
|
|
DCAHJELNJNP: 3,
|
|
|
|
|
SectionId: 5,
|
|
|
|
|
EMOHLJHPOPC: 2801,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_FAOCFBOFCJP{
|
|
|
|
|
FAOCFBOFCJP: &pb.NIHNGPKHEPC{
|
|
|
|
|
Gold: 500,
|
|
|
|
|
GridFightLineupHp: 100,
|
|
|
|
|
PPKKHHNEOHB: 100,
|
|
|
|
|
GDAKCOICIDA: 7,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_EJIPINIKNCK{
|
|
|
|
|
EJIPINIKNCK: &pb.HKFECLCHBFJ{
|
|
|
|
|
GridGameRoleList: []*pb.CDEAABODKCP{
|
|
|
|
|
{Id: 1001, UniqueId: 1, Pos: 1, RoleStar: 3},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
keepWin, settle, err := currencyWarsTerminalSettlement(catalog, nil, run, pb.PEIJNEFOCHI_PEIJNEFOCHI_JOJPGBCECLN, true, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("currencyWarsTerminalSettlement failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if keepWin == nil || settle == nil {
|
|
|
|
|
t.Fatal("expected non-nil keepWin and settle notifications")
|
|
|
|
|
}
|
|
|
|
|
if settle.BFLLJJBHLDF == nil || settle.BFLLJJBHLDF.Reason != pb.PEIJNEFOCHI_PEIJNEFOCHI_JOJPGBCECLN {
|
|
|
|
|
t.Errorf("expected victory reason in settlement, got %v", settle.BFLLJJBHLDF)
|
|
|
|
|
}
|
|
|
|
|
if settle.BFLLJJBHLDF.NDLIKNEKDLL.DCAHJELNJNP != 3 || settle.BFLLJJBHLDF.NDLIKNEKDLL.SectionId != 5 {
|
|
|
|
|
t.Errorf("expected 3-5 stage in settlement, got %d-%d", settle.BFLLJJBHLDF.NDLIKNEKDLL.DCAHJELNJNP, settle.BFLLJJBHLDF.NDLIKNEKDLL.SectionId)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrencyWarsBonfireSelectHeal(t *testing.T) {
|
|
|
|
|
origPersistent, _ := os.ReadFile("persistent")
|
|
|
|
|
if origPersistent != nil {
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
_ = os.WriteFile("persistent", origPersistent, 0644)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
catalog, err := common.LoadCurrencyWarsCatalog()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("load catalog: %v", err)
|
|
|
|
|
}
|
|
|
|
|
offer := &pb.NCEPOLNLNKH{
|
|
|
|
|
Phase: cwBonfireChoosing,
|
|
|
|
|
EIFICNFGGHI: 10,
|
|
|
|
|
AHJJJNBOMPG: 2,
|
|
|
|
|
DANCNIMJOLE: 3,
|
|
|
|
|
KFNMIEMBJGD: []*pb.MHCOIBONODA{
|
|
|
|
|
{EventId: 10200601, HAFOKMHCGFM: 1},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
run := &pb.MPIMAEMCEFH{
|
|
|
|
|
UniqueId: 101,
|
|
|
|
|
LJNGPCKPJLF: catalog.ActiveSeasonID,
|
|
|
|
|
DivisionId: 1,
|
|
|
|
|
FMIGOKIDKJL: &pb.CHCHAJIANIM{
|
|
|
|
|
QueuePosition: 795,
|
|
|
|
|
FGLIODAJFML: &pb.CHCHAJIANIM_IIDBLCFAAPC{
|
|
|
|
|
IIDBLCFAAPC: offer,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
NAGDJGMMCBE: []*pb.GridFightGameInfo{
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_KLCPAAHDPJF{
|
|
|
|
|
KLCPAAHDPJF: &pb.BEEFLEJAOCN{
|
|
|
|
|
DCAHJELNJNP: 1,
|
|
|
|
|
SectionId: 5,
|
|
|
|
|
EMOHLJHPOPC: 2801,
|
|
|
|
|
BossInfo: &pb.KGMCLFHAJNJ{NCGHKPEMPDP: []*pb.AHKDPCIIELA{{MonsterId: 1004016, RoleStar: 1, LOGHLGKIFEN: 1}}},
|
|
|
|
|
HEKMEJLMGFF: []*pb.LHNCMINFONJ{{LOGHLGKIFEN: 1}},
|
|
|
|
|
OOMMIIKPHBC: []*pb.BCCIJHPLJOJ{
|
|
|
|
|
{DCAHJELNJNP: 1, SectionId: 6, DisplayValue: &pb.OHMDHKGAEMM{StageId: 70000009}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_FAOCFBOFCJP{
|
|
|
|
|
FAOCFBOFCJP: &pb.NIHNGPKHEPC{
|
|
|
|
|
Gold: 50,
|
|
|
|
|
GridFightLineupHp: 60,
|
|
|
|
|
PPKKHHNEOHB: 100,
|
|
|
|
|
EHNJDIDABHD: 1,
|
|
|
|
|
NDAINFLOLPJ: 10,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_EJIPINIKNCK{
|
|
|
|
|
EJIPINIKNCK: &pb.HKFECLCHBFJ{},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_EBHDIOGALIE{
|
|
|
|
|
EBHDIOGALIE: &pb.POCJDMMANBM{},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
// 1. Test Select Heal HP
|
|
|
|
|
healReq := &pb.CAMIICJDOBO{
|
|
|
|
|
FFBBOFAGGDI: &pb.CAMIICJDOBO_NCGOOEMOBIC{
|
|
|
|
|
NCGOOEMOBIC: &pb.MLAENDOFBCK{
|
|
|
|
|
FFBBOFAGGDI: &pb.MLAENDOFBCK_LGAJNMNOAEM{
|
|
|
|
|
LGAJNMNOAEM: true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
notify, _, err := cwSelectBonfire(catalog, run, healReq)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwSelectBonfire heal failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if notify == nil {
|
|
|
|
|
t.Fatal("expected notify from heal")
|
|
|
|
|
}
|
|
|
|
|
if offer.Phase != cwBonfireSelected {
|
|
|
|
|
t.Errorf("expected phase %v, got %v", cwBonfireSelected, offer.Phase)
|
|
|
|
|
}
|
|
|
|
|
player, _, _ := cwComponents(run)
|
|
|
|
|
if player.GridFightLineupHp != 80 {
|
|
|
|
|
t.Errorf("expected 80 HP after heal, got %d", player.GridFightLineupHp)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Test Take Bonfire rewards and advance node
|
|
|
|
|
takeReq := &pb.CAMIICJDOBO{
|
|
|
|
|
FFBBOFAGGDI: &pb.CAMIICJDOBO_ECDACDNDHGO{
|
|
|
|
|
ECDACDNDHGO: &pb.EFIEAIJBHIB{},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
takeNotify, _, err := cwSelectBonfire(catalog, run, takeReq)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwSelectBonfire take failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if takeNotify == nil {
|
|
|
|
|
t.Fatal("expected notify from take")
|
|
|
|
|
}
|
|
|
|
|
stage, _ := cwStage(run)
|
|
|
|
|
if stage.SectionId != 6 {
|
|
|
|
|
t.Errorf("expected node advance to section 6, got %d", stage.SectionId)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPrintSaveRoles(t *testing.T) {
|
|
|
|
|
catalog, err := common.LoadCurrencyWarsCatalog()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("catalog: %v", err)
|
|
|
|
|
}
|
|
|
|
|
data, _, err := readCurrencyWarsData(catalog)
|
|
|
|
|
if err != nil || data == nil || data.DHFOFLFCLAH == nil {
|
|
|
|
|
t.Skip("no active run in persistent snapshot")
|
|
|
|
|
}
|
|
|
|
|
run := data.DHFOFLFCLAH
|
|
|
|
|
_, team, _ := cwComponents(run)
|
|
|
|
|
if team == nil {
|
|
|
|
|
t.Fatal("team nil")
|
|
|
|
|
}
|
|
|
|
|
for _, r := range team.GridGameRoleList {
|
|
|
|
|
v, _ := catalog.CapturedRoleVariant(r.Id, r.RoleStar)
|
|
|
|
|
t.Logf("Role ID=%d Star=%d Pos=%d AvatarID=%d BABNHPDGNGB=%v Props=%v ValInit=%v", r.Id, r.RoleStar, r.Pos, v.Config.AvatarID, r.BABNHPDGNGB, r.ConvertPropertyToFixpoint, r.GridFightValueInitComponent)
|
|
|
|
|
}
|
|
|
|
|
runtime, _ := cwBonfireRuntime(catalog, run)
|
|
|
|
|
if runtime != nil {
|
|
|
|
|
t.Logf("Runtime rules: %v", runtime.Rules)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCurrencyWarsGrantRandomOrb(t *testing.T) {
|
|
|
|
|
catalog, err := common.LoadCurrencyWarsCatalog()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("load catalog: %v", err)
|
|
|
|
|
}
|
|
|
|
|
run := &pb.MPIMAEMCEFH{
|
|
|
|
|
UniqueId: 999,
|
|
|
|
|
NAGDJGMMCBE: []*pb.GridFightGameInfo{
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_DBAGKJNPDOL{
|
|
|
|
|
DBAGKJNPDOL: &pb.DBONCGFHHDN{},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
LEOIAOFBBFA: &pb.GridFightGameInfo_FAOCFBOFCJP{
|
|
|
|
|
FAOCFBOFCJP: &pb.NIHNGPKHEPC{
|
|
|
|
|
Gold: 100,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
cmd := common.CurrencyWarsCommand{
|
|
|
|
|
Field: "orb",
|
|
|
|
|
Value: 0,
|
|
|
|
|
Quantity: 3,
|
|
|
|
|
}
|
|
|
|
|
notify, err := cwApplyCommand(catalog, run, cmd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("cwApplyCommand random orb: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if notify == nil || len(notify.GCPNIHHBGGD) == 0 {
|
|
|
|
|
t.Fatal("expected notify with update bundle")
|
|
|
|
|
}
|
|
|
|
|
if len(notify.GCPNIHHBGGD[0].JKPJFKCBIEM) != 3 {
|
|
|
|
|
t.Fatalf("expected 3 notify items, got %d", len(notify.GCPNIHHBGGD[0].JKPJFKCBIEM))
|
|
|
|
|
}
|
|
|
|
|
var bag *pb.DBONCGFHHDN
|
|
|
|
|
for _, c := range run.NAGDJGMMCBE {
|
|
|
|
|
if b := c.GetDBAGKJNPDOL(); b != nil {
|
|
|
|
|
bag = b
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if bag == nil || len(bag.MDKGCIKOMLI) != 3 {
|
|
|
|
|
t.Fatalf("expected 3 orbs in bag, got %v", bag)
|
|
|
|
|
}
|
|
|
|
|
// Verify that all granted orbs have valid, implemented IDs
|
|
|
|
|
recipes, err := common.LoadCurrencyWarsOrbRecipes()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("load recipes: %v", err)
|
|
|
|
|
}
|
|
|
|
|
implemented := make(map[uint32]bool)
|
|
|
|
|
for _, r := range recipes.Recipes {
|
|
|
|
|
if r.Status == "Implemented" && r.Engine == "Orb" {
|
|
|
|
|
implemented[r.ID] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i, orb := range bag.MDKGCIKOMLI {
|
|
|
|
|
if !implemented[orb.LDBNOMPCOKK] {
|
|
|
|
|
t.Errorf("orb[%d] id %d is not an implemented orb", i, orb.LDBNOMPCOKK)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|