服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
208 lines
7.3 KiB
Go
208 lines
7.3 KiB
Go
package annotations
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"pvfine/internal/pvf"
|
|
)
|
|
|
|
func TestExtractPreviewFieldsUsesSharedTargets(t *testing.T) {
|
|
index := 1
|
|
context := 0
|
|
engine, err := Compile(Document{
|
|
Version: 1,
|
|
Fields: []FieldDefinition{
|
|
{
|
|
ID: "equ.name",
|
|
Match: MatchSpec{Extensions: []string{".equ"}},
|
|
Target: TargetSpec{Kind: "token", Section: "name", Index: intPtr(0)},
|
|
Annotation: AnnotationSpec{Title: "名称", Type: "text"},
|
|
Preview: &PreviewSpec{Provider: "equ", Role: "name", Group: "header", Format: "text"},
|
|
},
|
|
{
|
|
ID: "equ.skill",
|
|
Match: MatchSpec{Extensions: []string{".equ"}},
|
|
Target: TargetSpec{Kind: "token", Section: "skill levelup", Index: &index, RecordTokens: 3, ContextIndex: &context},
|
|
Annotation: AnnotationSpec{Title: "技能", Type: "text"},
|
|
Preview: &PreviewSpec{Provider: "equ", Role: "skill-levelup", Group: "skills", Format: "skill-levelup"},
|
|
},
|
|
},
|
|
Rules: []Rule{},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
values := engine.ExtractPreviewFields("equipment/a.equ", pvf.ParseScriptView("[name]\n`测试`\n[skill levelup]\n`[swordman]` 38 2\n`[knight]` 14 1\n[/skill levelup]"), "equ")
|
|
if len(values) != 3 || values[0].Values[0] != "测试" {
|
|
t.Fatalf("values = %#v", values)
|
|
}
|
|
if values[1].Context != "[swordman]" || len(values[1].Values) != 3 || values[2].Values[1] != "14" {
|
|
t.Fatalf("repeated values = %#v", values)
|
|
}
|
|
}
|
|
|
|
func TestPreviewFieldsUseStandaloneGroupsAndGroupOffset(t *testing.T) {
|
|
engine := testEngine(t)
|
|
engine, err := Compile(Document{
|
|
Version: 1,
|
|
Fields: []FieldDefinition{{
|
|
ID: "world.drop", Match: MatchSpec{Extensions: []string{".etc"}},
|
|
Target: TargetSpec{
|
|
Kind: "token", Section: "world drop", Index: intPtr(0), RecordTokens: 2,
|
|
Offset: 1, GroupOffset: 1, StandaloneValues: []int32{-1},
|
|
},
|
|
Annotation: AnnotationSpec{Title: "掉落", Type: "text"},
|
|
Preview: &PreviewSpec{Provider: "etc", Role: "drop", Group: "drops", Format: "text"},
|
|
}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
view := pvf.ParseScriptView("[world drop]\n999 10 100 200 -1 20 300 400 -1 30 500 600")
|
|
values := engine.ExtractPreviewFields("item/a.etc", view, "etc")
|
|
if len(values) != 3 {
|
|
t.Fatalf("values = %#v", values)
|
|
}
|
|
for i, want := range []string{"100", "300", "500"} {
|
|
if values[i].Values[0] != want || len(values[i].Values) != 2 {
|
|
t.Fatalf("values[%d] = %#v, want %s", i, values[i], want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFieldRuleReferencesSharedDefinition(t *testing.T) {
|
|
engine, err := Compile(Document{
|
|
Version: 1,
|
|
Fields: []FieldDefinition{{
|
|
ID: "equ.rarity",
|
|
Match: MatchSpec{Extensions: []string{".equ"}},
|
|
Target: TargetSpec{Kind: "token", Section: "rarity", Index: intPtr(0)},
|
|
Annotation: AnnotationSpec{Title: "稀有度", Type: "enum", Values: map[string]string{"4": "史诗"}},
|
|
}},
|
|
Rules: []Rule{{ID: "rarity.rule", Field: "equ.rarity"}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
results := engine.Annotate("equipment/a.equ", pvf.ParseScriptView("[rarity]\n4"), nil)
|
|
if len(results) != 1 || results[0].Title != "史诗" {
|
|
t.Fatalf("results = %#v", results)
|
|
}
|
|
}
|
|
|
|
func TestSharedFieldLoadsAsAnnotationAndSupportsMultipleProviders(t *testing.T) {
|
|
engine, err := Compile(Document{
|
|
Version: 1,
|
|
Fields: []FieldDefinition{{
|
|
ID: "global.cool-time",
|
|
Match: MatchSpec{Extensions: []string{".equ", ".stk"}},
|
|
Target: TargetSpec{Kind: "token", Section: "cool time", Index: intPtr(0)},
|
|
Annotation: AnnotationSpec{Title: "冷却时间", Type: "text"},
|
|
Preview: &PreviewSpec{Providers: []string{"equ", "stk"}, Role: "cool-time", Group: "summary", Format: "text"},
|
|
}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, extension := range []string{".equ", ".stk"} {
|
|
results := engine.Annotate("item/test"+extension, pvf.ParseScriptView("[cool time]\n100"), nil)
|
|
if len(results) != 1 || results[0].Title != "冷却时间" {
|
|
t.Fatalf("extension %s results = %#v", extension, results)
|
|
}
|
|
}
|
|
if len(engine.PreviewFields("item/test.equ", "equ")) != 1 || len(engine.PreviewFields("item/test.stk", "stk")) != 1 {
|
|
t.Fatal("multiple preview providers were not matched")
|
|
}
|
|
}
|
|
|
|
func TestLoadDefaultIncludesEquipmentPreviewFields(t *testing.T) {
|
|
engine, err := LoadDefault()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fields := engine.PreviewFields("equipment/a.equ", "equ")
|
|
if len(fields) < 20 {
|
|
t.Fatalf("equipment fields = %d", len(fields))
|
|
}
|
|
}
|
|
|
|
func TestDefaultNewRarityAnnotations(t *testing.T) {
|
|
engine, err := LoadDefault()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
engine = engine.ForVersion("110US")
|
|
for _, path := range []string{"equipment/test.equ", "stackable/test.stk"} {
|
|
for value, label := range map[string]string{"7": "神话", "8": "太初"} {
|
|
results := engine.Annotate(path, pvf.ParseScriptView("[rarity]\n"+value), nil)
|
|
if len(results) != 1 {
|
|
t.Fatalf("%s rarity %s annotations = %#v", path, value, results)
|
|
}
|
|
for _, result := range results {
|
|
if len(result.RuleIDs) != 1 || result.RuleIDs[0] != "field:*.rarity" || strings.Count(result.Content, "所有枚举值:") != 1 {
|
|
t.Fatalf("%s rarity %s duplicated tooltip = %#v", path, value, result)
|
|
}
|
|
if result.Title != label {
|
|
t.Fatalf("%s rarity %s = %q, want %q", path, value, result.Title, label)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDefaultEquipmentRarityPreviewUsesGlobalField(t *testing.T) {
|
|
engine, err := LoadDefault()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
values := engine.ForVersion("110US").ExtractPreviewFields("equipment/test.equ", pvf.ParseScriptView("[rarity]\n8"), "equ")
|
|
if len(values) != 1 || values[0].Field.ID != "*.rarity" || values[0].Field.Preview.Role != "rarity" || values[0].Values[0] != "8" {
|
|
t.Fatalf("rarity preview fields = %#v", values)
|
|
}
|
|
}
|
|
|
|
func TestDefaultFameSections(t *testing.T) {
|
|
engine, err := LoadDefault()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
engine = engine.ForVersion("110US")
|
|
for _, section := range []string{"unique option fame value", "fame value"} {
|
|
t.Run(section, func(t *testing.T) {
|
|
view := pvf.ParseScriptView("[" + section + "]\n748")
|
|
results := engine.Annotate("equipment/test.equ", view, nil)
|
|
if len(results) != 1 || results[0].Title != "冒险家名望" {
|
|
t.Fatalf("annotations = %#v", results)
|
|
}
|
|
values := engine.ExtractPreviewFields("equipment/test.equ", view, "equ")
|
|
if len(values) != 1 || len(values[0].Values) != 1 || values[0].Values[0] != "748" {
|
|
t.Fatalf("preview fields = %#v", values)
|
|
}
|
|
if results := engine.Annotate("item/test.stk", view, nil); len(results) != 0 {
|
|
t.Fatalf("non-equ annotations = %#v", results)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDefaultBuffBasicExplainSection(t *testing.T) {
|
|
engine, err := LoadDefault()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
engine = engine.ForVersion("110US")
|
|
view := pvf.ParseScriptView("[buff basic explain]\n{10=`<1::buff>`}")
|
|
results := engine.Annotate("equipment/test.equ", view, nil)
|
|
if len(results) != 1 || results[0].Title != "辅助职业效果" {
|
|
t.Fatalf("annotations = %#v", results)
|
|
}
|
|
values := engine.ExtractPreviewFields("equipment/test.equ", view, "equ")
|
|
if len(values) != 1 || values[0].Field.ID != "equ.buff-basic-explain" || values[0].Field.Preview.Role != "buff-basic-explain" {
|
|
t.Fatalf("preview fields = %#v", values)
|
|
}
|
|
if results := engine.Annotate("item/test.stk", view, nil); len(results) != 0 {
|
|
t.Fatalf("non-equ annotations = %#v", results)
|
|
}
|
|
}
|