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,138 @@
|
||||
package actionobject
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
dnfpvf "longheng.io/server/internal/modules/dnf/pvf"
|
||||
)
|
||||
|
||||
func parseTestScript(t *testing.T, path, text string) Script {
|
||||
t.Helper()
|
||||
document, err := dnfpvf.Parse(path, text)
|
||||
if err != nil {
|
||||
t.Fatalf("parse %s: %v", path, err)
|
||||
}
|
||||
script, err := Parse(document)
|
||||
if err != nil {
|
||||
t.Fatalf("decode %s: %v", path, err)
|
||||
}
|
||||
return script
|
||||
}
|
||||
|
||||
// The gate object of 能源储藏室: it closes its own boss door while
|
||||
// `storage gate open` is short of 2 and reopens it once both cylinders have
|
||||
// counted in. Verbatim from
|
||||
// passiveobject/actionobject/map/luke_raid/phase2/enegy_save/action/gate_control_gate.act.
|
||||
const lukeEnergySaveGateScript = "" +
|
||||
"[MOTION]\n[BASE ANI]\n`../`\n[/MOTION]\n" +
|
||||
"[TRIGGER]\n[LIMIT]\n1\n[ENABLE]\n[ON]\n" +
|
||||
"[DUNGEON SYMBOL COMPARE]\n`storage gate open`\n[<]\n2\n" +
|
||||
"[DO BEHAVIOR]\n[ME]\n0\n[/TRIGGER]\n" +
|
||||
"[BEHAVIOR]\n" +
|
||||
"[NOTICE]\n`gate closed` 255 255 0 `None` 4000 1000\n[/NOTICE]\n" +
|
||||
"[GATE ENABLE]\n-1 -1 0\n[DOWN]\n[/GATE ENABLE]\n" +
|
||||
"[TRIGGER]\n1\n[ON]\n[TRIGGER]\n0\n[OFF]\n[/BEHAVIOR]\n" +
|
||||
"[TRIGGER]\n[LIMIT]\n1\n[ENABLE]\n[OFF]\n" +
|
||||
"[DUNGEON SYMBOL COMPARE]\n`storage gate open`\n[>=]\n2\n" +
|
||||
"[DO BEHAVIOR]\n[ME]\n1\n[/TRIGGER]\n" +
|
||||
"[BEHAVIOR]\n" +
|
||||
"[NOTICE]\n`gate open` 255 255 0 `None` 4000 1000\n[/NOTICE]\n" +
|
||||
"[GATE ENABLE]\n-1 -1 1\n[DOWN]\n[/GATE ENABLE]\n[/BEHAVIOR]\n"
|
||||
|
||||
// The left cylinder: it adds to the symbol once its room's monster is gone.
|
||||
const lukeEnergySaveCylinderScript = "" +
|
||||
"[MOTION]\n[BASE ANI]\n`../`\n[/MOTION]\n" +
|
||||
"[TRIGGER]\n[LIMIT]\n1\n[WHICH]\n[MONSTER]\n[CHECKUP]\n" +
|
||||
"[IS INDEX]\n57156\n[/IS INDEX]\n[CHECKED NO]\n[<]\n1\n[/CHECKUP]\n" +
|
||||
"[DO BEHAVIOR]\n[ME]\n0\n[/TRIGGER]\n" +
|
||||
"[BEHAVIOR]\n[DUNGEON SYMBOL CALC]\n`storage gate open`\n[+]\n1\n[/BEHAVIOR]\n"
|
||||
|
||||
func TestParseGateScriptDecodesBothBranches(t *testing.T) {
|
||||
script := parseTestScript(t, "gate_control_gate.act", lukeEnergySaveGateScript)
|
||||
if len(script.Triggers) != 2 || len(script.Behaviors) != 2 {
|
||||
t.Fatalf("triggers=%d behaviors=%d want 2/2 (%+v)", len(script.Triggers), len(script.Behaviors), script)
|
||||
}
|
||||
if !script.Drives() {
|
||||
t.Fatal("gate script does not drive anything")
|
||||
}
|
||||
|
||||
closing := script.Triggers[0]
|
||||
if closing.Limit != 1 || !closing.Enabled || closing.Behavior != 0 {
|
||||
t.Fatalf("closing trigger=%+v", closing)
|
||||
}
|
||||
if len(closing.Conditions) != 1 ||
|
||||
closing.Conditions[0] != (Condition{
|
||||
Kind: ConditionDungeonSymbol, Symbol: "storage gate open", Op: CompareLess, Value: 2,
|
||||
}) {
|
||||
t.Fatalf("closing conditions=%+v", closing.Conditions)
|
||||
}
|
||||
if len(closing.Unsupported) != 0 {
|
||||
t.Fatalf("closing trigger dropped %v", closing.Unsupported)
|
||||
}
|
||||
|
||||
// The reopening trigger ships disabled and is switched on by behavior 0.
|
||||
opening := script.Triggers[1]
|
||||
if opening.Enabled || opening.Behavior != 1 {
|
||||
t.Fatalf("opening trigger=%+v", opening)
|
||||
}
|
||||
if len(opening.Conditions) != 1 || opening.Conditions[0].Op != CompareGreaterEqual {
|
||||
t.Fatalf("opening conditions=%+v", opening.Conditions)
|
||||
}
|
||||
|
||||
closeBehavior := script.Behaviors[0]
|
||||
if len(closeBehavior.Operations) != 4 {
|
||||
t.Fatalf("close behavior operations=%+v", closeBehavior.Operations)
|
||||
}
|
||||
if got := closeBehavior.Operations[0]; got.Kind != OperationNotice ||
|
||||
got.Text != "gate closed" || got.Color != 0xFFFFFF00 || got.DurationMS != 4000 {
|
||||
t.Fatalf("notice=%+v", got)
|
||||
}
|
||||
if got := closeBehavior.Operations[1]; got.Kind != OperationGateEnable || got.GateEnabled ||
|
||||
len(got.GateDirections) != 1 || got.GateDirections[0] != "down" {
|
||||
t.Fatalf("gate close=%+v", got)
|
||||
}
|
||||
if got := closeBehavior.Operations[2]; got.Kind != OperationTriggerSwitch ||
|
||||
got.TriggerIndex != 1 || !got.TriggerOn {
|
||||
t.Fatalf("trigger switch on=%+v", got)
|
||||
}
|
||||
if got := closeBehavior.Operations[3]; got.Kind != OperationTriggerSwitch ||
|
||||
got.TriggerIndex != 0 || got.TriggerOn {
|
||||
t.Fatalf("trigger switch off=%+v", got)
|
||||
}
|
||||
|
||||
openBehavior := script.Behaviors[1]
|
||||
if len(openBehavior.Operations) != 2 {
|
||||
t.Fatalf("open behavior operations=%+v", openBehavior.Operations)
|
||||
}
|
||||
if got := openBehavior.Operations[1]; got.Kind != OperationGateEnable || !got.GateEnabled {
|
||||
t.Fatalf("gate open=%+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCylinderScriptCountsTheRoomMonster(t *testing.T) {
|
||||
script := parseTestScript(t, "gate_control_left.act", lukeEnergySaveCylinderScript)
|
||||
if len(script.Triggers) != 1 || len(script.Behaviors) != 1 {
|
||||
t.Fatalf("triggers=%d behaviors=%d want 1/1", len(script.Triggers), len(script.Behaviors))
|
||||
}
|
||||
trigger := script.Triggers[0]
|
||||
if len(trigger.Conditions) != 1 ||
|
||||
trigger.Conditions[0] != (Condition{
|
||||
Kind: ConditionMonsterCount, MonsterID: 57156, Op: CompareLess, Value: 1,
|
||||
}) {
|
||||
t.Fatalf("conditions=%+v unsupported=%v", trigger.Conditions, trigger.Unsupported)
|
||||
}
|
||||
operations := script.Behaviors[0].Operations
|
||||
if len(operations) != 1 ||
|
||||
operations[0].Kind != OperationSymbolCalc ||
|
||||
operations[0].Symbol != "storage gate open" ||
|
||||
operations[0].Calc != "+" || operations[0].Value != 1 {
|
||||
t.Fatalf("operations=%+v", operations)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAnimationOnlyScriptDrivesNothing(t *testing.T) {
|
||||
script := parseTestScript(t, "idle.act", "[MOTION]\n[BASE ANI]\n`../a.ani`\n[/MOTION]\n")
|
||||
if script.Drives() {
|
||||
t.Fatalf("animation-only script reported as driving: %+v", script)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user