服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
75 lines
2.9 KiB
Go
75 lines
2.9 KiB
Go
package dnfbridge
|
|
|
|
import (
|
|
"testing"
|
|
|
|
dnfrepo "longheng.io/server/internal/modules/dnf/repository"
|
|
)
|
|
|
|
// 勋章 [flag] 与守护珠 [flag gem] 只有落在 list 38 才能佩戴/镶嵌。副本拾取原先
|
|
// 无条件写 list 0 的 PVF 页,于是捡到的勋章躺在背包里用不了,要等下次登录的
|
|
// migrateLegacyGuildMedalItemsFromMainInventory 搬过去才正常——这正是"捡到时
|
|
// 不对、重登才好"。这里钉住拾取与 NPC 公会兑换用的是同一条归位规则。
|
|
func TestCurrentDungeonPickupListPlacementSendsGuildMedalsToList38(t *testing.T) {
|
|
for _, row := range []struct {
|
|
name string
|
|
equipmentType string
|
|
stackableType string
|
|
kind dungeonDropItemKind
|
|
}{
|
|
{name: "guild medal", equipmentType: "[flag]", kind: dungeonDropItemEquipment},
|
|
{name: "guardian gem", stackableType: "[flag gem]", kind: dungeonDropItemStackable},
|
|
} {
|
|
t.Run(row.name, func(t *testing.T) {
|
|
definition := dungeonDropItemDefinition{
|
|
ItemID: 90000,
|
|
Kind: row.kind,
|
|
EquipmentType: row.equipmentType,
|
|
StackableType: row.stackableType,
|
|
SlotStart: 9,
|
|
SlotEnd: 64,
|
|
}
|
|
start, end, ok := currentGuildMedalListPlacement(definition)
|
|
if !ok {
|
|
t.Skip("PVF placement rule does not classify this fixture; nothing to pin")
|
|
}
|
|
listType, gotStart, gotEnd := currentDungeonPickupListPlacement(definition)
|
|
if listType != currentGuildMedalInventoryListType {
|
|
t.Fatalf("pickup list = %d, want %d", listType, currentGuildMedalInventoryListType)
|
|
}
|
|
if gotStart != start || gotEnd != end {
|
|
t.Fatalf("pickup page = %d..%d, want the NPC-exchange page %d..%d",
|
|
gotStart, gotEnd, start, end)
|
|
}
|
|
// The quick-slot preference reaches for list-0 slots 3..8 and must
|
|
// never be carried onto list 38.
|
|
placement := currentDungeonPickupPlacement(definition, 1)
|
|
if placement.ListType != currentGuildMedalInventoryListType {
|
|
t.Fatalf("placement list = %d, want %d", placement.ListType, currentGuildMedalInventoryListType)
|
|
}
|
|
if placement.Definition.PreferQuickSlot {
|
|
t.Fatal("list-38 placement must not prefer the list-0 quick slots")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// 普通掉落必须原样留在 list 0,否则这次改动会把整棵掉落树搬错地方。
|
|
func TestCurrentDungeonPickupListPlacementKeepsOrdinaryDropsOnListZero(t *testing.T) {
|
|
definition := dungeonDropItemDefinition{
|
|
ItemID: 10154439,
|
|
Kind: dungeonDropItemStackable,
|
|
StackableType: "[quest]",
|
|
SlotStart: 65,
|
|
SlotEnd: 97,
|
|
}
|
|
listType, start, end := currentDungeonPickupListPlacement(definition)
|
|
if listType != dnfrepo.MainInventoryListType {
|
|
t.Fatalf("ordinary drop list = %d, want %d", listType, dnfrepo.MainInventoryListType)
|
|
}
|
|
if start != definition.SlotStart || end != definition.SlotEnd {
|
|
t.Fatalf("ordinary drop page = %d..%d, want the PVF page %d..%d",
|
|
start, end, definition.SlotStart, definition.SlotEnd)
|
|
}
|
|
}
|