服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
146 lines
4.1 KiB
Go
146 lines
4.1 KiB
Go
// avatarcompoundcheck 用来核对时装合成器的 PVF 数据。
|
|
//
|
|
// go run ./cmd/scratch/avatarcompoundcheck scan 列出每个职业分表的稀有池/普通池及各候选自己的 grade
|
|
// go run ./cmd/scratch/avatarcompoundcheck 112510093 ... 打印指定装备的 name/grade/rarity/equipment type
|
|
//
|
|
// scan 的结论:16 张分表 586 个稀有候选的 PVF grade 全是 3,普通候选全是 2,
|
|
// 也就是分表里的 [grade] 1 与 [upper grade] 2 说的是**投入的来源装扮**,
|
|
// 不是产出物品的档次。
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
|
|
dnfpvf "longheng.io/server/internal/modules/dnf/pvf"
|
|
platformpvf "longheng.io/server/internal/platform/pvf"
|
|
)
|
|
|
|
func resolvePath(archive *platformpvf.Archive, listPath, reference string) string {
|
|
reference = strings.TrimSpace(strings.Trim(reference, "`\"'"))
|
|
for _, candidate := range []string{reference, path.Join(path.Dir(listPath), reference)} {
|
|
if file, ok := archive.FindFile(candidate); ok {
|
|
return file.ArchivePath
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func main() {
|
|
pvfPath := flag.String("pvf", `../runtime/data/dnf/Script.pvf`, "Script.pvf path")
|
|
flag.Parse()
|
|
archive, err := platformpvf.Open(*pvfPath)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
text, err := archive.ReadText("equipment/equipment.lst")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
doc, err := dnfpvf.Parse("equipment/equipment.lst", text)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
byID := map[int64]string{}
|
|
for _, entry := range dnfpvf.ParseList(doc) {
|
|
byID[entry.ID] = resolvePath(archive, "equipment/equipment.lst", entry.Path)
|
|
}
|
|
|
|
grade := func(id int64) int64 {
|
|
p := byID[id]
|
|
if p == "" {
|
|
return -1
|
|
}
|
|
txt, e := archive.ReadText(p)
|
|
if e != nil {
|
|
return -1
|
|
}
|
|
d, e := dnfpvf.Parse(p, txt)
|
|
if e != nil {
|
|
return -1
|
|
}
|
|
g, _ := d.Int("grade")
|
|
return g
|
|
}
|
|
|
|
if len(flag.Args()) == 1 && flag.Args()[0] == "scan" {
|
|
idxText, _ := archive.ReadText("etc/chn_compound/compoundavatar.etc")
|
|
idxDoc, _ := dnfpvf.Parse("etc/chn_compound/compoundavatar.etc", idxText)
|
|
seen := map[int64]int{}
|
|
for _, tok := range idxDoc.Tokens {
|
|
name := strings.TrimSpace(strings.Trim(tok.Value, "`"))
|
|
if !strings.HasSuffix(strings.ToLower(name), ".etc") {
|
|
continue
|
|
}
|
|
tp := "etc/chn_compound/" + strings.ToLower(name)
|
|
tt, readErr := archive.ReadText(tp)
|
|
if readErr != nil {
|
|
fmt.Println(tp, "ERR", readErr)
|
|
continue
|
|
}
|
|
td, parseErr := dnfpvf.Parse(tp, tt)
|
|
if parseErr != nil {
|
|
continue
|
|
}
|
|
g, _ := td.Int("grade")
|
|
ug, _ := td.Int("upper grade")
|
|
fmt.Printf("== %s grade=%d upper=%d\n", name, g, ug)
|
|
for _, part := range []string{"hat", "hair", "face", "neck", "coat", "pants", "belt", "shoes"} {
|
|
vals := td.Ints(part + " avatar")
|
|
if len(vals) < 3 {
|
|
continue
|
|
}
|
|
n := int(vals[0])
|
|
pairs := vals[1:]
|
|
specials := ""
|
|
for i := 0; i*2+1 < len(pairs) && i < n; i++ {
|
|
gr := grade(pairs[i*2])
|
|
specials += fmt.Sprintf(" %d(w=%d,g=%d)", pairs[i*2], pairs[i*2+1], gr)
|
|
seen[gr]++
|
|
}
|
|
normals := ""
|
|
for i := n; i*2+1 < len(pairs); i++ {
|
|
normals += fmt.Sprintf(" %d(w=%d,g=%d)", pairs[i*2], pairs[i*2+1], grade(pairs[i*2]))
|
|
}
|
|
fmt.Printf(" %-6s specials:%s | normals:%s\n", part, specials, normals)
|
|
}
|
|
}
|
|
fmt.Println("special grade histogram:", seen)
|
|
return
|
|
}
|
|
|
|
for _, id := range flag.Args() {
|
|
var itemID int64
|
|
fmt.Sscan(id, &itemID)
|
|
p := byID[itemID]
|
|
if p == "" {
|
|
fmt.Printf("%d: NOT IN equipment.lst\n", itemID)
|
|
continue
|
|
}
|
|
t, readErr := archive.ReadText(p)
|
|
if readErr != nil {
|
|
fmt.Printf("%d %s: read error %v\n", itemID, p, readErr)
|
|
continue
|
|
}
|
|
d, parseErr := dnfpvf.Parse(p, t)
|
|
if parseErr != nil {
|
|
fmt.Printf("%d %s: parse error %v\n", itemID, p, parseErr)
|
|
continue
|
|
}
|
|
g, hasGrade := d.Int("grade")
|
|
rarity, hasRarity := d.Int("rarity")
|
|
name := ""
|
|
if tokens, ok := d.Section("name"); ok && len(tokens) > 0 {
|
|
name = tokens[0].Value
|
|
}
|
|
eqType := ""
|
|
if tokens, ok := d.Section("equipment type"); ok && len(tokens) > 0 {
|
|
eqType = tokens[0].Value
|
|
}
|
|
fmt.Printf("%d %s name=%q grade=%d(%v) rarity=%d(%v) type=%s\n",
|
|
itemID, p, name, g, hasGrade, rarity, hasRarity, eqType)
|
|
}
|
|
}
|