GemData:修订间差异
来自蒙纳多研究本部
创建页面,内容为“-- 模块:GemData —— 宝珠数据层(唯一 xbdb 访问入口,仿 ArtsData/source 决策11 架构) -- 数据来源(全部运行时在线取,零硬编码名单): -- bdat_common.BTL_skilllist 宝珠技能定义(attach=0/1/2 归属、I~X 数值、money、rvs_caption) -- bdat_common.ITM_dropcrystallist 掉落结晶(skill1/skill2 引用 → 「真实宝珠」过滤锚点) -- 注意:row_id 越界会整页 500 且 pcall 无效(ArtsD…” |
无编辑摘要 |
||
| 第44行: | 第44行: | ||
-- 未被引用的(力量弱化/杂质等占位、内部削弱条目)自然出局。 | -- 未被引用的(力量弱化/杂质等占位、内部削弱条目)自然出局。 | ||
-- 首次调用时全表扫描 820 行 dropcrystallist 并构建引用集合(同一解析内记忆化)。 | -- 首次调用时全表扫描 820 行 dropcrystallist 并构建引用集合(同一解析内记忆化)。 | ||
-- ⚠️ 线上 xbdb 返回的 skill1/skill2 是「内嵌完整技能对象」(含 row_id),不是裸数字 id。 | |||
-- 所以这里做两形态兼容:对象 → 取 .row_id;数字 → 直接用。 | |||
local refSet = nil | local refSet = nil | ||
function p.referencedSkills() | function p.referencedSkills() | ||
| 第52行: | 第54行: | ||
if dc then | if dc then | ||
for _, k in ipairs({ "skill1", "skill2" }) do | for _, k in ipairs({ "skill1", "skill2" }) do | ||
local v = | local v = dc[k] | ||
if type(v) == "table" then | |||
v = tonumber(v.row_id) -- 线上形态:内嵌对象 | |||
else | |||
v = tonumber(v) -- 防御形态:裸 id | |||
end | |||
if v and v >= 1 and v <= SKILLLIST_MAX_ID then | if v and v >= 1 and v <= SKILLLIST_MAX_ID then | ||
refSet[v] = true | refSet[v] = true | ||
2026年10月13日 (二) 21:26的版本
此模块的文档可以在模块:GemData/doc创建
-- 模块:GemData —— 宝珠数据层(唯一 xbdb 访问入口,仿 ArtsData/source 决策11 架构)
-- 数据来源(全部运行时在线取,零硬编码名单):
-- bdat_common.BTL_skilllist 宝珠技能定义(attach=0/1/2 归属、I~X 数值、money、rvs_caption)
-- bdat_common.ITM_dropcrystallist 掉落结晶(skill1/skill2 引用 → 「真实宝珠」过滤锚点)
-- 注意:row_id 越界会整页 500 且 pcall 无效(ArtsData/source 已知坑),所以本模块
-- 只对"表全量行数范围内的 id"发调用;行数上限写死为安全护栏(非数据名单)。
local p = {}
local post = require("Module:RequestHelper").post
-- ====== 安全护栏(实现细节,非游戏数据) ======
local SKILLLIST_MAX_ID = 98 -- BTL_skilllist 行数上限(超出即 500,必须前置拦截)
local DROPCRYSTAL_MAX_ID = 820 -- ITM_dropcrystallist 行数上限
local cache = {}
-- 基础取数(带护栏 + 记忆化)
local function fetch(table, id, maxId)
local n = tonumber(id)
if not n or n < 1 or n > maxId then return nil end
local key = table .. ":" .. n
if cache[key] == nil then
local ok, data = pcall(post, table, n)
if not ok or type(data) ~= "table" or (data.row_id == nil and data.__id == nil) then
cache[key] = false
return nil
end
cache[key] = data
end
return cache[key] or nil
end
function p.fetchSkill(id)
return fetch("bdat_common.BTL_skilllist", id, SKILLLIST_MAX_ID)
end
function p.fetchDropCrystal(id)
return fetch("bdat_common.ITM_dropcrystallist", id, DROPCRYSTAL_MAX_ID)
end
-- 「真实宝珠」判定(数据驱动,无名单):
-- 掉落结晶表 skill1/skill2 引用到的技能 = 游戏内真实存在的宝珠。
-- 未被引用的(力量弱化/杂质等占位、内部削弱条目)自然出局。
-- 首次调用时全表扫描 820 行 dropcrystallist 并构建引用集合(同一解析内记忆化)。
-- ⚠️ 线上 xbdb 返回的 skill1/skill2 是「内嵌完整技能对象」(含 row_id),不是裸数字 id。
-- 所以这里做两形态兼容:对象 → 取 .row_id;数字 → 直接用。
local refSet = nil
function p.referencedSkills()
if refSet ~= nil then return refSet end
refSet = {}
for id = 1, DROPCRYSTAL_MAX_ID do
local dc = p.fetchDropCrystal(id)
if dc then
for _, k in ipairs({ "skill1", "skill2" }) do
local v = dc[k]
if type(v) == "table" then
v = tonumber(v.row_id) -- 线上形态:内嵌对象
else
v = tonumber(v) -- 防御形态:裸 id
end
if v and v >= 1 and v <= SKILLLIST_MAX_ID then
refSet[v] = true
end
end
end
end
return refSet
end
-- 共用宝珠列表(attach=0 且 被结晶引用 → 线上实测 13 条)
-- ⚠️ 过滤口径修正(线上快照复算结论):只用「被 ITM_dropcrystallist 引用」这一条过滤,
-- 不要叠加 money>0 —— money=0 的条目里有两个真实宝珠(武器·命中上升 #69、防具·填充时间缩短 #45)
-- 会被误杀;而 attach=0 中被过滤掉的恰好全是 负面占位(16/32/48/64/80/96)+杂质(98)+共用版
-- 无法战斗抗性(97,防具版 #27 才是可装备的),一条不多一条不少。
-- 返回 { {id=..., data={...}}, ... } 按 id 升序
function p.sharedGems()
return p.gemsByAttach(0)
end
-- 通用分类列表:attachValue = 0(共用) / 1(武器) / 2(防具)
function p.gemsByAttach(attachValue)
local refs = p.referencedSkills()
local out = {}
for id = 1, SKILLLIST_MAX_ID do
if refs[id] then
local s = p.fetchSkill(id)
if s and tonumber(s.attach) == attachValue then
out[#out + 1] = { id = id, data = s }
end
end
end
table.sort(out, function(a, b) return a.id < b.id end)
return out
end
-- 单宝珠取数(含归属校验)
function p.getGem(id)
local s = p.fetchSkill(id)
if not s then return nil end
local refs = p.referencedSkills()
if not refs[tonumber(id)] then return nil end
return s
end
-- 数值插值:等级 r(1..10 → E..S),在 lower_X..upper_X 区间内
-- 游戏内宝珠等级 I~X 对应字段 E,D,C,B,A,S 只有 6 档锚点,
-- 10 级向 6 锚点的映射采用天幻逐级值验证过的线性插值(锚点处取精确值)。
-- 锚点映射:I=E, III=D, V=C, VII=B, IX=A, X=S(偶数级取相邻锚点中点,四舍五入)
local RANK_KEYS = { "E", "D", "C", "B", "A", "S" }
function p.valueAtRank(data, rank)
local anchors = { [1] = "E", [3] = "D", [5] = "C", [7] = "B", [9] = "A", [10] = "S" }
local key = anchors[rank]
if not key then
-- 偶数级:取左右锚点均值
local lo = anchors[rank - 1] or "E"
local hi = anchors[rank + 1] or "S"
local lv = tonumber(data["lower_" .. lo] or 0)
local hv = tonumber(data["lower_" .. hi] or 0)
return math.floor((lv + hv) / 2 + 0.5)
end
return tonumber(data["lower_" .. key] or 0)
end
p.RANK_KEYS = RANK_KEYS
p.SKILLLIST_MAX_ID = SKILLLIST_MAX_ID
return p