Files
90CNServer/server/internal/modules/dnf/darkknightcombo/layout_test.go
T
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

143 lines
4.7 KiB
Go

package darkknightcombo
import (
"context"
"reflect"
"testing"
dnfpvf "longheng.io/server/internal/modules/dnf/pvf"
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
dnfskill "longheng.io/server/internal/modules/dnf/skill"
)
func TestCleanSavedChildrenQuickSlotsPreservesRootsAndMovesChildren(t *testing.T) {
layout := dnfrepo.SkillLayout{0: 100, 1: 46, 198: 47, 54: 9}
got, moved, err := CleanSavedChildrenQuickSlots(
layout,
map[uint16]struct{}{100: {}},
map[uint16]struct{}{46: {}, 47: {}},
)
if err != nil {
t.Fatal(err)
}
want := dnfrepo.SkillLayout{0: 100, 54: 9, 150: 46, 151: 47}
if moved != 2 || !reflect.DeepEqual(got, want) {
t.Fatalf("moved=%d layout=%v want=%v", moved, got, want)
}
if !reflect.DeepEqual(layout, dnfrepo.SkillLayout{0: 100, 1: 46, 198: 47, 54: 9}) {
t.Fatalf("input layout mutated: %v", layout)
}
}
func TestCleanSavedChildrenQuickSlotsUsesStockSixPlusSixBoundary(t *testing.T) {
layout := make(dnfrepo.SkillLayout)
savedChildren := make(map[uint16]struct{})
for slot := 0; slot < 12; slot++ {
skillID := uint16(100 + slot)
layout[slot] = skillID
savedChildren[skillID] = struct{}{}
}
for slot := 198; slot < 204; slot++ {
skillID := uint16(300 + slot - 198)
layout[slot] = skillID
savedChildren[skillID] = struct{}{}
}
got, moved, err := CleanSavedChildrenQuickSlots(
layout,
map[uint16]struct{}{},
savedChildren,
)
if err != nil {
t.Fatal(err)
}
if moved != 12 {
t.Fatalf("moved=%d want=12", moved)
}
for slot := 0; slot < 6; slot++ {
if _, occupied := got[slot]; occupied {
t.Fatalf("primary quick slot %d was not cleaned: %v", slot, got)
}
}
for slot := 198; slot < 204; slot++ {
if _, occupied := got[slot]; occupied {
t.Fatalf("extension quick slot %d was not cleaned: %v", slot, got)
}
}
for slot := 6; slot < 12; slot++ {
want := uint16(100 + slot)
if got[slot] != want {
t.Fatalf("ordinary tree slot %d=%d want=%d; layout=%v", slot, got[slot], want, got)
}
}
}
func TestNormalizeFixedComboRootSlotsInstallsRootsAndMovesOrdinarySkillsToQWERTY(t *testing.T) {
catalog := fixedComboRootTestCatalog(t)
states := map[int64]dnfrepo.SkillState{
5: {Level: 1, Enabled: true}, 8: {Level: 1, Enabled: true}, 46: {Level: 1, Enabled: true},
118: {Level: 1, Enabled: true}, 119: {Level: 1, Enabled: true},
}
layout := dnfrepo.SkillLayout{0: 5, 1: 8, 2: 46, 198: 99, 150: 118, 151: 119}
got, moved, err := NormalizeFixedComboRootSlots(catalog, states, layout)
if err != nil {
t.Fatal(err)
}
want := dnfrepo.SkillLayout{0: 118, 1: 119, 198: 99, 199: 5, 200: 8, 201: 46}
if moved != 3 || !reflect.DeepEqual(got, want) {
t.Fatalf("moved=%d layout=%v want=%v", moved, got, want)
}
if !reflect.DeepEqual(layout, dnfrepo.SkillLayout{0: 5, 1: 8, 2: 46, 198: 99, 150: 118, 151: 119}) {
t.Fatalf("input layout mutated: %v", layout)
}
}
func TestNormalizeFixedComboRootSlotsOverflowsOrdinarySkillsIntoSkillTree(t *testing.T) {
catalog := fixedComboRootTestCatalog(t)
states := map[int64]dnfrepo.SkillState{
5: {Level: 1, Enabled: true}, 8: {Level: 1, Enabled: true},
118: {Level: 1, Enabled: true}, 119: {Level: 1, Enabled: true},
}
layout := dnfrepo.SkillLayout{0: 5, 1: 8}
for slot := extensionQuickSlotFrom; slot < extensionQuickSlotEnd; slot++ {
layout[slot] = uint16(100 + slot - extensionQuickSlotFrom)
}
layout[150] = 118
layout[151] = 119
got, moved, err := NormalizeFixedComboRootSlots(catalog, states, layout)
if err != nil {
t.Fatal(err)
}
if moved != 2 || got[150] != 5 || got[151] != 8 || got[0] != 118 || got[1] != 119 {
t.Fatalf("moved=%d layout=%v", moved, got)
}
for slot := 2; slot < ordinaryQuickSlotEnd; slot++ {
if _, occupied := got[slot]; occupied {
t.Fatalf("unlearned combo root slot %d remained occupied: %v", slot, got)
}
}
}
func fixedComboRootTestCatalog(t *testing.T) *dnfskill.Table {
t.Helper()
source := comboTestSource{
"skill/skilllist.lst": "9 `demonicswordman/skilllist.lst`\n",
"skill/demonicswordman/skilllist.lst": "5 `five.skl`\n8 `eight.skl`\n46 `forty_six.skl`\n118 `comboset0.skl`\n119 `comboset1.skl`\n",
"skill/demonicswordman/five.skl": "[type] `[active]`\n",
"skill/demonicswordman/eight.skl": "[type] `[active]`\n",
"skill/demonicswordman/forty_six.skl": "[type] `[active]`\n",
"skill/demonicswordman/comboset0.skl": "[combo set]\n5 8\n[/combo set]\n",
"skill/demonicswordman/comboset1.skl": "[combo set]\n46\n[/combo set]\n",
}
index, err := dnfpvf.Build(context.Background(), source, dnfpvf.BuildOptions{Lists: []string{dnfskill.DefaultList}})
if err != nil {
t.Fatal(err)
}
catalog, err := dnfskill.Load(context.Background(), index, dnfskill.Options{})
if err != nil {
t.Fatal(err)
}
return catalog
}