服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
132 lines
5.2 KiB
Go
132 lines
5.2 KiB
Go
package dnfbridge
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func newEnchantBeadTestCatalog(t *testing.T) (*pvfDungeonDropCatalog, dungeonDropCatalogTestSource) {
|
|
t.Helper()
|
|
source := dungeonDropCatalogTestSource{
|
|
"monster/monster.lst": "",
|
|
"stackable/stackable.lst": "8801 `bead/fire_bead.stk`\n8802 `bead/plain_bead.stk`\n9001 `card/fire_card.stk`\n",
|
|
"equipment/equipment.lst": "700 `weapon/test_coat.equ`\n701 `weapon/test_sword.equ`\n",
|
|
"stackable/bead/fire_bead.stk": "[name] `Fire Bead`\n[stackable type] `[material]`\n[monster card id] 9001\n[bead limited usable item] 700 701\n",
|
|
"stackable/bead/plain_bead.stk": "[name] `Plain Bead`\n[stackable type] `[material]`\n",
|
|
"stackable/card/fire_card.stk": "[name] `Fire Card`\n[string data] `icon.img` `[coat]` `[weapon]`\n[enchant table]\n[enchant index]\n0\n[value] 10\n[/enchant index]\n[enchant index]\n1\n[value] 11\n[/enchant index]\n[enchant index]\n2\n[value] 12\n[/enchant index]\n[/enchant table]\n",
|
|
"equipment/weapon/test_coat.equ": "[name] `Test Coat`\n[equipment type] `[coat]`\n[durability] 57\n",
|
|
"equipment/weapon/test_sword.equ": "[name] `Test Sword`\n[equipment type] `[weapon]`\n[durability] 57\n",
|
|
}
|
|
catalog, err := newPVFDungeonDropCatalog(source)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return catalog, source
|
|
}
|
|
|
|
func TestResolveCurrentEnchantBeadMetadata(t *testing.T) {
|
|
catalog, source := newEnchantBeadTestCatalog(t)
|
|
resolution, err := resolveCurrentEnchantBeadMetadata(catalog, source, 8801, 700)
|
|
if err != nil {
|
|
t.Fatalf("resolve error = %v", err)
|
|
}
|
|
if resolution.CardItemID != 9001 {
|
|
t.Fatalf("card id = %d, want 9001", resolution.CardItemID)
|
|
}
|
|
if len(resolution.TargetWhitelist) != 2 || resolution.TargetWhitelist[0] != 700 || resolution.TargetWhitelist[1] != 701 {
|
|
t.Fatalf("whitelist = %v", resolution.TargetWhitelist)
|
|
}
|
|
if len(resolution.AllowedEquipmentTypes) != 2 || resolution.AllowedEquipmentTypes[0] != "[coat]" || resolution.AllowedEquipmentTypes[1] != "[weapon]" {
|
|
t.Fatalf("allowed types = %v, want icon entry skipped", resolution.AllowedEquipmentTypes)
|
|
}
|
|
if len(resolution.UpgradeCounts) != 3 || resolution.UpgradeCounts[0] != 0 || resolution.UpgradeCounts[1] != 1 || resolution.UpgradeCounts[2] != 2 {
|
|
t.Fatalf("upgrade counts = %v", resolution.UpgradeCounts)
|
|
}
|
|
if resolution.TargetKind != "equipment" || resolution.TargetEquipmentType != "[coat]" {
|
|
t.Fatalf("target = %+v", resolution)
|
|
}
|
|
if resolution.CardPVFPath != "stackable/card/fire_card.stk" {
|
|
t.Fatalf("card path = %q", resolution.CardPVFPath)
|
|
}
|
|
}
|
|
|
|
func TestChooseCurrentEnchantCardIDPreservesRepeatedPVFWeights(t *testing.T) {
|
|
values := []int64{9001, 9001, 9002}
|
|
seen := make(map[int64]bool)
|
|
for index := 0; index < 128; index++ {
|
|
cardID, found, err := chooseCurrentEnchantCardID(values)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !found || (cardID != 9001 && cardID != 9002) {
|
|
t.Fatalf("card id = %d found=%t", cardID, found)
|
|
}
|
|
seen[cardID] = true
|
|
}
|
|
if len(seen) != 2 {
|
|
t.Fatalf("random candidates = %v, want both weighted cards", seen)
|
|
}
|
|
}
|
|
|
|
func TestResolveCurrentEnchantBeadMetadataInvalidBeads(t *testing.T) {
|
|
catalog, source := newEnchantBeadTestCatalog(t)
|
|
tests := []struct {
|
|
name string
|
|
bead int64
|
|
}{
|
|
{name: "bead without card", bead: 8802},
|
|
{name: "unknown bead", bead: 9999},
|
|
{name: "equipment as bead", bead: 700},
|
|
{name: "zero bead", bead: 0},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
resolution, err := resolveCurrentEnchantBeadMetadata(catalog, source, tc.bead, 700)
|
|
if err != nil {
|
|
t.Fatalf("resolve error = %v", err)
|
|
}
|
|
if resolution.CardItemID != 0 {
|
|
t.Fatalf("card id = %d, want 0 for invalid bead", resolution.CardItemID)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolveCurrentEnchantBeadMetadataUnresolvedTarget(t *testing.T) {
|
|
catalog, source := newEnchantBeadTestCatalog(t)
|
|
resolution, err := resolveCurrentEnchantBeadMetadata(catalog, source, 8801, 9999)
|
|
if err != nil {
|
|
t.Fatalf("resolve error = %v", err)
|
|
}
|
|
if resolution.CardItemID != 9001 {
|
|
t.Fatalf("card id = %d, want 9001", resolution.CardItemID)
|
|
}
|
|
if resolution.TargetKind != "" {
|
|
t.Fatalf("target kind = %q, want empty for unresolved target", resolution.TargetKind)
|
|
}
|
|
}
|
|
|
|
func TestResolveCurrentEnchantBeadMetadataRandomTitleEnchant(t *testing.T) {
|
|
source := dungeonDropCatalogTestSource{
|
|
"monster/monster.lst": "",
|
|
"stackable/stackable.lst": "8803 `bead/random.stk`\n",
|
|
"equipment/equipment.lst": "702 `title/test.equ`\n",
|
|
"stackable/bead/random.stk": "[stackable type] `[auto enchant]`\n[random title enchant] 10001\n[string data] `[title name]`\n[bead limited usable item] 702\n",
|
|
"equipment/title/test.equ": "[equipment type] `[title name]`\n",
|
|
"etc/randomtitleenchant.etc": "[index]\n10001 1 1 100 `[swordman]` 4 1\n[/index]",
|
|
}
|
|
catalog, err := newPVFDungeonDropCatalog(source)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
resolution, err := resolveCurrentEnchantBeadMetadata(catalog, source, 8803, 702)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resolution.CardItemID != 0 || resolution.RandomTitleEnchantConfigID != 10001 || resolution.RandomTitleEnchantRowID != 1 {
|
|
t.Fatalf("resolution = %+v", resolution)
|
|
}
|
|
if resolution.TargetEquipmentType != "[title name]" || resolution.TargetKind != "equipment" {
|
|
t.Fatalf("target = %+v", resolution)
|
|
}
|
|
}
|