package gameserver import ( "sort" "testing" "gac-hkrpg-go/internal/common" "gac-hkrpg-go/internal/testutil" pb "gac-hkrpg-go/protocol/proto" ) func TestAhaBuffTargetsSceneAttacker(t *testing.T) { temporary := t.TempDir() if err := testutil.CopyRuntime(testWorkspaceRoot, temporary); err != nil { t.Fatal(err) } t.Chdir(temporary) t.Setenv("GAC_CONFIG", "server.json") rules, err := loadBattleRules() if err != nil { t.Fatal(err) } resources, err := common.GameRes() if err != nil { t.Fatal(err) } world, err := loadWorldBattleResources() if err != nil { t.Fatal(err) } levels, err := common.LoadAhaHelpLevels() if err != nil { t.Fatal(err) } if err := common.SaveAhaHelpLevel(&levels[0].BuffLevel); err != nil { t.Fatal(err) } // Pick valid characters and a battle source from the checked-in resources; // neither the user's live roster nor their persistent settings are changed. var ids []uint32 for id, config := range resources.AvatarConfigs { if id != 8001 && id != 1001 && rules.EntryAttackBuffs[config.DamageType] != 0 { ids = append(ids, id) } } sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) if len(ids) < 4 || len(world.Cocoons) == 0 || len(world.NPCMonsters) == 0 { t.Fatal("battle fixture data incomplete") } player := &common.FreesrData{Avatars: map[uint32]*common.AvatarJson{}, Lineups: map[uint32]uint32{}} for i, id := range ids[:4] { player.Avatars[id] = &common.AvatarJson{AvatarID: id, Level: 1} player.Lineups[uint32(i)] = id } enabled := true player.EnableAhaGlobal, player.EnableSWGlobal, player.EnableCastoriceGlobal = &enabled, &enabled, &enabled var monster, corruption, ahaID uint32 for id := range world.NPCMonsters { monster = id break } for _, id := range rules.InvasionBuffs { corruption = id break } for _, rule := range rules.GlobalPassives { if rule.Setting == "enable_aha_global" { ahaID = rule.BuffID } } if monster == 0 || corruption == 0 || ahaID == 0 { t.Fatal("battle fixture rule missing") } source := &common.BattleConfig{StageID: world.Cocoons[0].StageID, Monsters: [][]common.Monster{{{MonsterID: monster, Level: 1}}}, Blessings: []common.BattleBuffJson{{ID: corruption, Level: 1}}} session := &Session{MonsterEntities: map[uint32]uint32{777777: monster}} session.SetJSON(player) cases := []struct { name string caster, want uint32 }{ {"first slot", 1, 0}, {"second slot", 2, 1}, {"third slot", 3, 2}, {"fourth slot", 4, 3}, {"avatar entity namespace", techniqueAvatarEntityOffset + ids[2], 2}, {"direct entry has no caster", 0, 0}, {"unknown actor", 12345, 0}, {"monster ambush", 777777, 0}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { battle, err := createBattleInfo(session, tc.caster, 0, source) if err != nil { t.Fatal(err) } var aha, help *pb.BattleBuff for _, buff := range battle.BuffList { switch buff.Id { case ahaID: aha = buff case levels[0].BuffID: help = buff default: for _, rule := range rules.GlobalPassives { if rule.Setting != "enable_aha_global" && buff.Id == rule.BuffID && (len(buff.TargetIndexList) != 1 || buff.TargetIndexList[0] != 0) { t.Fatal("unrelated passive target changed") } } } } for name, buff := range map[string]*pb.BattleBuff{"Aha Global": aha, "Aha Help": help} { if buff == nil || len(buff.TargetIndexList) != 1 || buff.TargetIndexList[0] != tc.want { t.Fatalf("%s target: got %v, want slot %d", name, buff, tc.want) } } // Source ownership and unrelated corruption are unchanged. if help.OwnerId != battle.BattleAvatarList[0].Index { t.Fatal("Aha Help owner changed") } }) } off := uint32(0) if err := common.SaveAhaHelpLevel(&off); err != nil { t.Fatal(err) } battle, err := createBattleInfo(session, 2, 0, source) if err != nil { t.Fatal(err) } for _, buff := range battle.BuffList { if buff.Id == levels[0].BuffID { t.Fatal("disabled Aha Help was added") } } }