DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
This commit is contained in:
commit
645b64fe21
4388 files changed
+1405457
No files matched your search
@@ -0,0 +1,362 @@
|
||||
package dnfbridge
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"longheng.io/server/internal/modules/dnf/raid"
|
||||
)
|
||||
|
||||
// lukeCardSetMapInfo is luke.etc's [CARD SET MAP INFO], verbatim.
|
||||
var lukeCardSetMapInfo = []int{0, 0, 0, 3, 3, 3, 6, 6, 6, 9, 9, 9, 12, 12, 12}
|
||||
|
||||
func lukeSanctumMazeTestState() *runtimeRaidState {
|
||||
state := lukePhaseOneGeneratorState()
|
||||
state.LukeConfig.CardSetMapInfo = append([]int(nil), lukeCardSetMapInfo...)
|
||||
state.Lifecycle.Objectives = map[int]runtimeRaidDungeonObjective{}
|
||||
for _, mapID := range []int{lukeBirthSanctumMapID, lukeExtinctionSanctumMapID, lukeSpreadSanctumMapID} {
|
||||
state.Lifecycle.Objectives[mapID] = runtimeRaidDungeonObjective{
|
||||
Phase: runtimeRaidDungeonPhaseOne, MapID: mapID, State: raid.AntonDungeonOpen, MaxHP: 3, RemainingHP: 3,
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// 这是**回退**候选表:正常路径已改成「当天共用一条记录、各读分量」,只有记录的分量
|
||||
// 放不下该圣所的迷宫数时才回到这里。它对 "旧行为" 的钉法照旧有效。
|
||||
func TestLukeSanctumMazeGroupsComeFromCardSetMapInfo(t *testing.T) {
|
||||
if got := runtimeRaidLukeSanctumMazeGroups(lukeCardSetMapInfo, 15); !reflect.DeepEqual(got, []int{0, 3, 6, 9, 12}) {
|
||||
t.Fatalf("groups=%v want [0 3 6 9 12]", got)
|
||||
}
|
||||
// A group whose third maze does not exist is not a group.
|
||||
if got := runtimeRaidLukeSanctumMazeGroups(lukeCardSetMapInfo, 13); !reflect.DeepEqual(got, []int{0, 3, 6, 9}) {
|
||||
t.Fatalf("groups for 13 mazes=%v want [0 3 6 9]", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Live 2026-09-24 production: three uniform picks of 湮灭之圣所 drew mazes 8, 11
|
||||
// and 5, all third-of-group, and the squad fought 哈布 three times. The sanctum
|
||||
// must instead walk one drawn group's first, second and third maze.
|
||||
//
|
||||
// ⚠ 语义变更(2026-09-29):这个测试里的 choose 以前是「在候选组列表里抽一个下标、
|
||||
// 各圣所各抽各的」,现在是「**当天抽哪一条 [CARD SET MAP INFO] 记录**」——官方
|
||||
// 「所有频道的塔罗卡牌统一相同」⇒ 三圣所共用同一条记录。所以下面那条
|
||||
// "Every sanctum draws its own group" 已按新语义换成"三所共用当天那条记录"。
|
||||
// 其余部分(一次抽完不再抽、给放弃不重复抽、新一阶段重新抽)逐字保留。
|
||||
func TestLukeSanctumWalksItsDrawnGroupOneMazePerClear(t *testing.T) {
|
||||
state := lukeSanctumMazeTestState()
|
||||
rolls := 0
|
||||
choose := func(limit int) (int, error) {
|
||||
rolls++
|
||||
if limit != 5 {
|
||||
t.Fatalf("card set record roll limit=%d want 5 records", limit)
|
||||
}
|
||||
return 3, nil // 记录 3 = `9 9 9`
|
||||
}
|
||||
for clear, wantMaze := range []int{9, 10, 11} {
|
||||
objective := state.Lifecycle.Objectives[lukeExtinctionSanctumMapID]
|
||||
objective.ClearCount = clear
|
||||
state.Lifecycle.Objectives[lukeExtinctionSanctumMapID] = objective
|
||||
maze, ok, err := runtimeRaidLukeSanctumMazeLocked(state, lukeExtinctionSanctumMapID, 15, choose)
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("clear %d: ok=%t err=%v", clear, ok, err)
|
||||
}
|
||||
if maze.Maze != wantMaze || maze.GroupStart != 9 || maze.Chosen != (clear == 0) {
|
||||
t.Fatalf("clear %d: maze=%+v want maze %d of group 9", clear, maze, wantMaze)
|
||||
}
|
||||
}
|
||||
// A give-up does not clear: the retry re-enters the same maze.
|
||||
if maze, _, _ := runtimeRaidLukeSanctumMazeLocked(state, lukeExtinctionSanctumMapID, 15, choose); maze.Maze != 11 {
|
||||
t.Fatalf("retry after the third clear's give-up=%d want 11", maze.Maze)
|
||||
}
|
||||
if rolls != 1 {
|
||||
t.Fatalf("group rolled %d times want once per sanctum", rolls)
|
||||
}
|
||||
|
||||
// 当天那条记录由三个圣所共用(旧断言是"每圣所各抽自己的组",已随 2026-09-29
|
||||
// 的口径改动替换):同一个记录下标下,另外两个圣所读到的正是同一条记录的分量。
|
||||
// 出货数据每条记录三个分量恒等,所以组起点相同。
|
||||
for _, sanctum := range []int{lukeBirthSanctumMapID, lukeSpreadSanctumMapID} {
|
||||
maze, ok, err := runtimeRaidLukeSanctumMazeLocked(state, sanctum, 15, choose)
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("sanctum %d: ok=%t err=%v", sanctum, ok, err)
|
||||
}
|
||||
if maze.Record != 3 || maze.GroupStart != 9 || maze.Maze != 9 {
|
||||
t.Fatalf("sanctum %d maze=%+v want the day's record 3 (= group 9)", sanctum, maze)
|
||||
}
|
||||
}
|
||||
if got := state.Lifecycle.LukeSanctumMazeGroups[lukeExtinctionSanctumMapID]; got != 9 {
|
||||
t.Fatalf("another sanctum's draw moved the extinction group to %d", got)
|
||||
}
|
||||
|
||||
// 换一天(换一条记录)就换一组:记录 0 = `0 0 0`。用一个还没抽过的团验证。
|
||||
fresh := lukeSanctumMazeTestState()
|
||||
other := func(int) (int, error) { return 0, nil }
|
||||
if maze, ok, _ := runtimeRaidLukeSanctumMazeLocked(fresh, lukeBirthSanctumMapID, 15, other); !ok ||
|
||||
maze.Record != 0 || maze.GroupStart != 0 || maze.Maze != 0 {
|
||||
t.Fatalf("birth sanctum maze=%+v ok=%t want record 0 / group 0", maze, ok)
|
||||
}
|
||||
|
||||
// A new phase one draws again.
|
||||
initRuntimeRaidLukePhaseOneLocked(state)
|
||||
if len(state.Lifecycle.LukeSanctumMazeGroups) != 0 {
|
||||
t.Fatalf("phase-one init kept groups %v", state.Lifecycle.LukeSanctumMazeGroups)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLukeSanctumMazeLeavesEveryOtherRunToTheOrdinaryChoice(t *testing.T) {
|
||||
failing := func(int) (int, error) { return 0, errors.New("must not roll") }
|
||||
state := lukeSanctumMazeTestState()
|
||||
if _, ok, err := runtimeRaidLukeSanctumMazeLocked(state, lukeLightAltarMapID, 15, failing); ok || err != nil {
|
||||
t.Fatalf("a phase-two map took the sanctum path ok=%t err=%v", ok, err)
|
||||
}
|
||||
state.Lifecycle.Phase = runtimeRaidDungeonPhaseTwo
|
||||
if _, ok, err := runtimeRaidLukeSanctumMazeLocked(state, lukeBirthSanctumMapID, 15, failing); ok || err != nil {
|
||||
t.Fatalf("phase two took the sanctum path ok=%t err=%v", ok, err)
|
||||
}
|
||||
state.Lifecycle.Phase = runtimeRaidDungeonPhaseOne
|
||||
state.TailFlag = 1
|
||||
if _, ok, err := runtimeRaidLukeSanctumMazeLocked(state, lukeBirthSanctumMapID, 15, failing); ok || err != nil {
|
||||
t.Fatalf("a single-mode raid took the sanctum path ok=%t err=%v", ok, err)
|
||||
}
|
||||
if _, ok, err := runtimeRaidLukeSanctumMazeLocked(nil, lukeBirthSanctumMapID, 15, failing); ok || err != nil {
|
||||
t.Fatalf("no raid took the sanctum path ok=%t err=%v", ok, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLukeSanctumMazeSharesTheDaysCardSetRecordAcrossSanctums is the state-level
|
||||
// pin for the 2026-09-29 rule change. Official system text (2017-06-22 国服开放
|
||||
// 卢克团队模式): 「塔罗卡牌以卢克团队模式进行的当日6点为基准进行变更,所有频道
|
||||
// 的塔罗卡牌统一相同。」⇒ 一天抽**一条** [CARD SET MAP INFO] 记录,三个圣所共用
|
||||
// 它、各读自己的分量;抽取器只按 tarot day,不带圣所。
|
||||
//
|
||||
// 用生产侧那一个抽取器跑一整个月:任一天里三个圣所必须报出同一个 Record,且
|
||||
// 组起点就是该记录的分量;跨天至少出现 3 条不同记录(否则"每天变"是假的)。
|
||||
func TestLukeSanctumMazeSharesTheDaysCardSetRecordAcrossSanctums(t *testing.T) {
|
||||
seen := map[int]bool{}
|
||||
for day := 1; day <= 30; day++ {
|
||||
raidDay := 20260900 + day
|
||||
// 生产侧 selectRuntimeRaidLukeSanctumMaze 给的就是这一个:只按 tarot day。
|
||||
draw := func(limit int) (int, error) {
|
||||
return runtimeRaidLukeTarotCardSetRecord(raidDay, limit), nil
|
||||
}
|
||||
record := runtimeRaidLukeTarotCardSetRecord(raidDay, 5)
|
||||
if record < 0 || record >= 5 {
|
||||
t.Fatalf("day %d record=%d outside [0,5)", raidDay, record)
|
||||
}
|
||||
seen[record] = true
|
||||
state := lukeSanctumMazeTestState()
|
||||
for _, sanctum := range []int{lukeBirthSanctumMapID, lukeExtinctionSanctumMapID, lukeSpreadSanctumMapID} {
|
||||
maze, ok, err := runtimeRaidLukeSanctumMazeLocked(state, sanctum, 15, draw)
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("day %d sanctum %d ok=%t err=%v", raidDay, sanctum, ok, err)
|
||||
}
|
||||
if maze.Record != record {
|
||||
t.Fatalf("day %d sanctum %d read record %d, the day drew %d", raidDay, sanctum, maze.Record, record)
|
||||
}
|
||||
// 出货数据每条记录三个分量恒等 ⇒ 三所组起点都等于该记录的分量。
|
||||
want := lukeCardSetMapInfo[record*lukeSanctumClearsPerGroup]
|
||||
if maze.GroupStart != want || maze.Maze != want {
|
||||
t.Fatalf("day %d sanctum %d maze=%+v want group start %d on the first clear",
|
||||
raidDay, sanctum, maze, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(seen) < 3 {
|
||||
t.Fatalf("a month of tarot days drew only card set records %v", seen)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLukeSanctumCardSetComponentsMapEachSanctumToItsOwnCell uses a table whose
|
||||
// records carry three DIFFERENT components, so "each sanctum reads its own cell"
|
||||
// is distinguishable from "all three read the first cell". The mapping itself
|
||||
// (BIRTH 0 / EXTINCTION 1 / SPREAD 2) is an unproven convention taken from
|
||||
// luke.etc [DEFAULT OBJECT COUNT]'s written order -- see the file header.
|
||||
func TestLukeSanctumCardSetComponentsMapEachSanctumToItsOwnCell(t *testing.T) {
|
||||
distinct := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
|
||||
draw := func(int) (int, error) { return 2, nil } // 记录 2 = `6 7 8`
|
||||
want := map[int]int{
|
||||
lukeBirthSanctumMapID: 6,
|
||||
lukeExtinctionSanctumMapID: 7,
|
||||
lukeSpreadSanctumMapID: 8,
|
||||
}
|
||||
state := lukeSanctumMazeTestState()
|
||||
state.LukeConfig.CardSetMapInfo = distinct
|
||||
for sanctum, wantGroup := range want {
|
||||
// mazeCount 取 60:这 15 个分量都是合法组起点,所以走的是共用记录这条路径。
|
||||
maze, ok, err := runtimeRaidLukeSanctumMazeLocked(state, sanctum, 60, draw)
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("sanctum %d ok=%t err=%v", sanctum, ok, err)
|
||||
}
|
||||
if maze.Record != 2 || maze.GroupStart != wantGroup || maze.Maze != wantGroup {
|
||||
t.Fatalf("sanctum %d maze=%+v want record 2 / group %d", sanctum, maze, wantGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestLukeSanctumMazeFallsBackWhenTheDaysRecordDoesNotFit keeps the guard the
|
||||
// change must not lose: the shared record's component still has to address a
|
||||
// whole group of THIS sanctum's maze count (each .dgn declares its own), and a
|
||||
// group that does not fit falls back to the candidate list instead of emitting
|
||||
// an out-of-range maze. An unusable table takes over nothing at all.
|
||||
func TestLukeSanctumMazeFallsBackWhenTheDaysRecordDoesNotFit(t *testing.T) {
|
||||
// 记录 1 = `14 14 14`;迷宫数 15 容不下 14(14+3 > 15)。候选表只剩 {12}。
|
||||
tight := []int{12, 12, 12, 14, 14, 14}
|
||||
// 抽取器对两次调用都要给出合法值:记录数 2 → 1;候选表大小 1 → 0。
|
||||
draw := func(limit int) (int, error) { return limit - 1, nil }
|
||||
state := lukeSanctumMazeTestState()
|
||||
state.LukeConfig.CardSetMapInfo = tight
|
||||
maze, ok, err := runtimeRaidLukeSanctumMazeLocked(state, lukeBirthSanctumMapID, 15, draw)
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("ok=%t err=%v", ok, err)
|
||||
}
|
||||
if maze.Record != -1 {
|
||||
t.Fatalf("record=%d want -1 (the candidate fallback drew this one)", maze.Record)
|
||||
}
|
||||
if maze.GroupStart != 12 || maze.Maze != 12 {
|
||||
t.Fatalf("maze=%+v want the only candidate group 12", maze)
|
||||
}
|
||||
if maze.Maze+lukeSanctumClearsPerGroup > 15 {
|
||||
t.Fatalf("maze=%d runs past the sanctum's 15 mazes", maze.Maze)
|
||||
}
|
||||
|
||||
// 候选表为空时宁可不接管这一次选图,也不发越界 maze。
|
||||
none := lukeSanctumMazeTestState()
|
||||
none.LukeConfig.CardSetMapInfo = []int{14, 14, 14}
|
||||
if maze, ok, err := runtimeRaidLukeSanctumMazeLocked(none, lukeBirthSanctumMapID, 15, draw); ok || err != nil {
|
||||
t.Fatalf("an unusable table took the sanctum path maze=%+v ok=%t err=%v", maze, ok, err)
|
||||
}
|
||||
|
||||
// 越界的记录下标是错误,不是静默当成第 0 条。
|
||||
outOfRange := func(int) (int, error) { return 9, nil }
|
||||
if _, _, ok, err := runtimeRaidLukeSanctumMazeDrawnGroup(lukeCardSetMapInfo, lukeBirthSanctumMapID, 15, outOfRange); ok || err == nil {
|
||||
t.Fatalf("record roll 9 ok=%t err=%v want an error", ok, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLukeSanctumMazeRecordGroupReadsTheSharedRecordAndIsDefensive pins the
|
||||
// record→group reader on its own: the shipped table, a distinct-component table,
|
||||
// an unknown dungeon id, and the defensive paths (out-of-range record, truncated
|
||||
// table) that must fall back to the record's first component instead of
|
||||
// panicking.
|
||||
func TestLukeSanctumMazeRecordGroupReadsTheSharedRecordAndIsDefensive(t *testing.T) {
|
||||
for record := 0; record < 5; record++ {
|
||||
for _, sanctum := range []int{lukeBirthSanctumMapID, lukeExtinctionSanctumMapID, lukeSpreadSanctumMapID} {
|
||||
start, ok := runtimeRaidLukeSanctumMazeRecordGroup(lukeCardSetMapInfo, record, sanctum, 15)
|
||||
if !ok || start != record*lukeSanctumClearsPerGroup {
|
||||
t.Fatalf("record %d sanctum %d start=%d ok=%t want %d",
|
||||
record, sanctum, start, ok, record*lukeSanctumClearsPerGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
distinct := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
|
||||
for component, sanctum := range map[int]int{
|
||||
0: lukeBirthSanctumMapID,
|
||||
1: lukeExtinctionSanctumMapID,
|
||||
2: lukeSpreadSanctumMapID,
|
||||
} {
|
||||
start, ok := runtimeRaidLukeSanctumMazeRecordGroup(distinct, 2, sanctum, 60)
|
||||
if !ok || start != 6+component {
|
||||
t.Fatalf("sanctum %d component %d start=%d ok=%t want %d", sanctum, component, start, ok, 6+component)
|
||||
}
|
||||
}
|
||||
// 未列入映射的地牢 ID 按该记录的第一个分量处理(防御性;调用方只会问三个圣所)。
|
||||
if start, ok := runtimeRaidLukeSanctumMazeRecordGroup(distinct, 2, 12345, 60); !ok || start != 6 {
|
||||
t.Fatalf("unknown sanctum start=%d ok=%t want 6", start, ok)
|
||||
}
|
||||
// 越界的记录下标不 panic,只是不成立。
|
||||
if start, ok := runtimeRaidLukeSanctumMazeRecordGroup(lukeCardSetMapInfo, 99, lukeBirthSanctumMapID, 15); ok {
|
||||
t.Fatalf("record 99 start=%d ok=%t want not ok", start, ok)
|
||||
}
|
||||
if _, ok := runtimeRaidLukeSanctumMazeRecordGroup(lukeCardSetMapInfo, -1, lukeBirthSanctumMapID, 15); ok {
|
||||
t.Fatal("a negative record was accepted")
|
||||
}
|
||||
// 表被截断(16 个值 = 5 条记录 + 1):分量 2 越出列表 ⇒ 回退到该记录的第一个
|
||||
// 分量,不 panic。
|
||||
truncated := append(append([]int(nil), lukeCardSetMapInfo...), 15)
|
||||
if start, ok := runtimeRaidLukeSanctumMazeRecordGroup(truncated, 5, lukeSpreadSanctumMapID, 18); !ok || start != 15 {
|
||||
t.Fatalf("truncated table start=%d ok=%t want the record's first component 15", start, ok)
|
||||
}
|
||||
// 组起点放不下这个圣所的迷宫数 ⇒ 不成立,交给回退。
|
||||
if start, ok := runtimeRaidLukeSanctumMazeRecordGroup(lukeCardSetMapInfo, 4, lukeBirthSanctumMapID, 13); ok {
|
||||
t.Fatalf("group start %d was accepted for a 13-maze sanctum", start)
|
||||
}
|
||||
|
||||
// 出货数据下「共用记录、各读分量」与旧「每圣所各取同一分量」在**记录→组起点**
|
||||
// 这一层逐值等价:记录 i 的三个分量恒等,且正是旧候选表里的第 i 项。行为差异因此
|
||||
// 只剩"三所抽的是不是同一条记录"这一点,也就是本次要改的那一点。
|
||||
groups := runtimeRaidLukeSanctumMazeGroups(lukeCardSetMapInfo, 15)
|
||||
if len(groups) != 5 {
|
||||
t.Fatalf("candidate groups=%v want 5 records", groups)
|
||||
}
|
||||
for record := range groups {
|
||||
for _, sanctum := range []int{lukeBirthSanctumMapID, lukeExtinctionSanctumMapID, lukeSpreadSanctumMapID} {
|
||||
start, ok := runtimeRaidLukeSanctumMazeRecordGroup(lukeCardSetMapInfo, record, sanctum, 15)
|
||||
if !ok || start != groups[record] {
|
||||
t.Fatalf("record %d sanctum %d start=%d ok=%t want the old candidate %d",
|
||||
record, sanctum, start, ok, groups[record])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The tarot turns over at 06:00 server time and is the same for every raid that
|
||||
// day ("每个卢克RAID攻坚日早上6点会刷新塔罗牌").
|
||||
//
|
||||
// 这段钉的是**每圣所的图案序号 B**(1181 里"该圣所今天启动哪套防御体系"),
|
||||
// 2026-09-29 的改动没有动它的选法;"当天走哪组迷宫"改成了三所共用一条
|
||||
// [CARD SET MAP INFO] 记录,由上面 TestLukeSanctumMazeSharesTheDaysCardSetRecord
|
||||
// AcrossSanctums 钉住。
|
||||
func TestLukeTarotDayTurnsOverAtSixAndIsSharedByTheServer(t *testing.T) {
|
||||
local := time.Local
|
||||
before := time.Date(2026, 9, 26, 5, 59, 0, 0, local)
|
||||
after := time.Date(2026, 9, 26, 6, 0, 0, 0, local)
|
||||
if got := runtimeRaidLukeTarotDay(before); got != 20260925 {
|
||||
t.Fatalf("05:59 tarot day=%d want the previous day 20260925", got)
|
||||
}
|
||||
if got := runtimeRaidLukeTarotDay(after); got != 20260926 {
|
||||
t.Fatalf("06:00 tarot day=%d want 20260926", got)
|
||||
}
|
||||
// Same day, same sanctum: every raid draws the same pattern index for it.
|
||||
for _, sanctum := range []int{lukeBirthSanctumMapID, lukeExtinctionSanctumMapID, lukeSpreadSanctumMapID} {
|
||||
first := runtimeRaidLukeTarotGroup(20260926, sanctum, 5)
|
||||
if first < 0 || first >= 5 {
|
||||
t.Fatalf("sanctum %d pattern index=%d outside [0,5)", sanctum, first)
|
||||
}
|
||||
if again := runtimeRaidLukeTarotGroup(20260926, sanctum, 5); again != first {
|
||||
t.Fatalf("sanctum %d drew %d then %d on one day", sanctum, first, again)
|
||||
}
|
||||
}
|
||||
// Across a month the draw actually changes.
|
||||
seen := map[int]bool{}
|
||||
for day := 1; day <= 30; day++ {
|
||||
seen[runtimeRaidLukeTarotGroup(20260900+day, lukeExtinctionSanctumMapID, 5)] = true
|
||||
}
|
||||
if len(seen) < 3 {
|
||||
t.Fatalf("a month of tarot days drew only pattern indices %v", seen)
|
||||
}
|
||||
|
||||
// 当天的记录抽取器(走哪组迷宫)同样按天统一、换日即变,且不随圣所变。
|
||||
shared := map[int]int{}
|
||||
for day := 1; day <= 30; day++ {
|
||||
record := runtimeRaidLukeTarotCardSetRecord(20260900+day, 5)
|
||||
if record < 0 || record >= 5 {
|
||||
t.Fatalf("day %d card set record=%d outside [0,5)", day, record)
|
||||
}
|
||||
shared[record] = day
|
||||
}
|
||||
if len(shared) < 3 {
|
||||
t.Fatalf("a month of tarot days drew only card set records %v", shared)
|
||||
}
|
||||
if runtimeRaidLukeTarotCardSetRecord(20260926, 5) != runtimeRaidLukeTarotCardSetRecord(20260926, 5) {
|
||||
t.Fatal("the card set record draw is not reproducible on one day")
|
||||
}
|
||||
// 只有一条记录(或没有)时定格在 0,不 panic。
|
||||
for _, records := range []int{0, 1, -3} {
|
||||
if got := runtimeRaidLukeTarotCardSetRecord(20260926, records); got != 0 {
|
||||
t.Fatalf("records=%d record=%d want 0", records, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user