You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
13 KiB
341 lines
13 KiB
local w3xparser = require 'w3xparser'
|
|
local progress = require 'progress'
|
|
|
|
local table_concat = table.concat
|
|
local ipairs = ipairs
|
|
local string_char = string.char
|
|
local pairs = pairs
|
|
local type = type
|
|
local table_sort = table.sort
|
|
local table_insert = table.insert
|
|
local math_floor = math.floor
|
|
local wtonumber = w3xparser.tonumber
|
|
local math_type = math.type
|
|
local os_clock = os.clock
|
|
local tonumber = tonumber
|
|
|
|
local report
|
|
local slk
|
|
local w2l
|
|
local metadata
|
|
local keys
|
|
local lines
|
|
local cx
|
|
local cy
|
|
local remove_unuse_object
|
|
local slk_type
|
|
local object
|
|
local default
|
|
|
|
local displaytype = {
|
|
unit = '单位',
|
|
ability = '技能',
|
|
item = '物品',
|
|
buff = '魔法效果',
|
|
upgrade = '科技',
|
|
doodad = '装饰物',
|
|
destructable = '可破坏物',
|
|
}
|
|
|
|
local function get_displayname(o)
|
|
local name
|
|
if o._type == 'buff' then
|
|
name = o.bufftip or o.editorname or ''
|
|
elseif o._type == 'upgrade' then
|
|
name = o.name[1] or ''
|
|
else
|
|
name = o.name or ''
|
|
end
|
|
return displaytype[o._type], o._id, (name:sub(1, 100):gsub('\r\n', ' '))
|
|
end
|
|
|
|
local function report_failed(obj, key, tip, info)
|
|
report.n = report.n + 1
|
|
if not report[tip] then
|
|
report[tip] = {}
|
|
end
|
|
if report[tip][obj._id] then
|
|
return
|
|
end
|
|
local type, id, name = get_displayname(obj)
|
|
report[tip][obj._id] = {
|
|
("%s %s %s"):format(type, id, name),
|
|
("%s %s"):format(key, info),
|
|
}
|
|
end
|
|
|
|
local slk_keys = {
|
|
['units\\abilitydata.slk'] = {
|
|
'alias','code','Area1','Area2','Area3','Area4','BuffID1','BuffID2','BuffID3','BuffID4','Cast1','Cast2','Cast3','Cast4','checkDep','Cool1','Cool2','Cool3','Cool4','Cost1','Cost2','Cost3','Cost4','DataA1','DataA2','DataA3','DataA4','DataB1','DataB2','DataB3','DataB4','DataC1','DataC2','DataC3','DataC4','DataD1','DataD2','DataD3','DataD4','DataE1','DataE2','DataE3','DataE4','DataF1','DataF2','DataF3','DataF4','DataG1','DataG2','DataG3','DataG4','DataH1','DataH2','DataH3','DataH4','DataI1','DataI2','DataI3','DataI4','Dur1','Dur2','Dur3','Dur4','EfctID1','EfctID2','EfctID3','EfctID4','HeroDur1','HeroDur2','HeroDur3','HeroDur4','levels','levelSkip','priority','reqLevel','Rng1','Rng2','Rng3','Rng4','targs1','targs2','targs3','targs4','UnitID1','UnitID2','UnitID3','UnitID4',
|
|
},
|
|
['units\\abilitybuffdata.slk'] = {
|
|
'alias',
|
|
},
|
|
['units\\destructabledata.slk'] = {
|
|
'DestructableID','armor','cliffHeight','colorB','colorG','colorR','deathSnd','fatLOS','file','fixedRot','flyH','fogRadius','fogVis','HP','lightweight','maxPitch','maxRoll','maxScale','minScale','MMBlue','MMGreen','MMRed','Name','numVar','occH','pathTex','pathTexDeath','portraitmodel','radius','selcircsize','selectable','shadow','showInMM','targType','texFile','texID','tilesetSpecific','useMMColor','walkable',
|
|
},
|
|
['units\\itemdata.slk'] = {
|
|
'itemID','abilList','armor','class','colorB','colorG','colorR','cooldownID','drop','droppable','file','goldcost','HP','ignoreCD','Level','lumbercost','morph','oldLevel','pawnable','perishable','pickRandom','powerup','prio','scale','sellable','stockMax','stockRegen','stockStart','usable','uses',
|
|
},
|
|
['units\\upgradedata.slk'] = {
|
|
'upgradeid','base1','base2','base3','base4','class','code1','code2','code3','code4','effect1','effect2','effect3','effect4','global','goldbase','goldmod','inherit','lumberbase','lumbermod','maxlevel','mod1','mod2','mod3','mod4','timebase','timemod', 'used',
|
|
},
|
|
['units\\unitabilities.slk'] = {
|
|
'unitAbilID','abilList','auto','heroAbilList',
|
|
},
|
|
['units\\unitbalance.slk'] = {
|
|
'unitBalanceID','AGI','AGIplus','bldtm','bountydice','bountyplus','bountysides','collision','def','defType','defUp','fmade','fused','goldcost','goldRep','HP','INT','INTplus','isbldg','level','lumberbountydice','lumberbountyplus','lumberbountysides','lumbercost','lumberRep','mana0','manaN','maxSpd','minSpd','nbrandom','nsight','preventPlace','Primary','regenHP','regenMana','regenType','reptm','repulse','repulseGroup','repulseParam','repulsePrio','requirePlace','sight','spd','stockMax','stockRegen','stockStart','STR','STRplus','tilesets','type','upgrades',
|
|
},
|
|
['units\\unitdata.slk'] = {
|
|
'unitID','buffRadius','buffType','canBuildOn','canFlee','canSleep','cargoSize','death','deathType','fatLOS','formation','isBuildOn','moveFloor','moveHeight','movetp','nameCount','orientInterp','pathTex','points','prio','propWin','race','requireWaterRadius','targType','turnRate',
|
|
},
|
|
['units\\unitui.slk'] = {
|
|
'unitUIID','name','armor','blend','blue','buildingShadow','customTeamColor','elevPts','elevRad','file','fileVerFlags','fogRad','green','hideHeroBar','hideHeroDeathMsg','hideHeroMinimap','hideOnMinimap','maxPitch','maxRoll','modelScale','nbmmIcon','occH','red','run','scale','scaleBull','selCircOnWater','selZ','shadowH','shadowOnWater','shadowW','shadowX','shadowY','teamColor','tilesetSpecific','uberSplat','unitShadow','unitSound','walk',
|
|
},
|
|
['units\\unitweapons.slk'] = {
|
|
'unitWeapID','acquire','atkType1','atkType2','backSw1','backSw2','castbsw','castpt','cool1','cool2','damageLoss1','damageLoss2','dice1','dice2','dmgplus1','dmgplus2','dmgpt1','dmgpt2','dmgUp1','dmgUp2','Farea1','Farea2','Harea1','Harea2','Hfact1','Hfact2','impactSwimZ','impactZ','launchSwimZ','launchX','launchY','launchZ','minRange','Qarea1','Qarea2','Qfact1','Qfact2','rangeN1','rangeN2','RngBuff1','RngBuff2','showUI1','showUI2','sides1','sides2','spillDist1','spillDist2','spillRadius1','spillRadius2','splashTargs1','splashTargs2','targCount1','targCount2','targs1','targs2','weapsOn','weapTp1','weapTp2','weapType1','weapType2',
|
|
},
|
|
['doodads\\doodads.slk'] = {
|
|
'doodID','file','Name','doodClass','soundLoop','selSize','defScale','minScale','maxScale','maxPitch','maxRoll','visRadius','walkable','numVar','floats','shadow','showInFog','animInFog','fixedRot','pathTex','showInMM','useMMColor','MMRed','MMGreen','MMBlue','vertR01','vertG01','vertB01','vertR02','vertG02','vertB02','vertR03','vertG03','vertB03','vertR04','vertG04','vertB04','vertR05','vertG05','vertB05','vertR06','vertG06','vertB06','vertR07','vertG07','vertB07','vertR08','vertG08','vertB08','vertR09','vertG09','vertB09','vertR10','vertG10','vertB10',
|
|
},
|
|
}
|
|
|
|
local function add_end()
|
|
lines[#lines+1] = 'E'
|
|
end
|
|
|
|
local function add(x, y, k)
|
|
local strs = {}
|
|
strs[#strs+1] = 'C'
|
|
if x ~= cx then
|
|
cx = x
|
|
strs[#strs+1] = 'X' .. x
|
|
end
|
|
if y ~= cy then
|
|
cy = y
|
|
strs[#strs+1] = 'Y' .. y
|
|
end
|
|
if type(k) == 'string' then
|
|
k = ('"%s"'):format(k:gsub('\r\n', '|n'):gsub('[\r\n]', '|n'))
|
|
elseif math_type(k) == 'float' then
|
|
k = ('%.4f'):format(k):gsub('[0]+$', ''):gsub('%.$', '.0')
|
|
end
|
|
strs[#strs+1] = 'K' .. k
|
|
lines[#lines+1] = table_concat(strs, ';')
|
|
end
|
|
|
|
local function add_values(names, skeys, slk_name)
|
|
local clock = os_clock()
|
|
for y, name in ipairs(names) do
|
|
local obj = slk[name]
|
|
for x, key in ipairs(skeys) do
|
|
local value = obj[key]
|
|
if value then
|
|
add(x, y+1, value)
|
|
elseif slk_name == 'units\\unitabilities.slk' and key == 'auto'
|
|
or slk_name == 'units\\unitbalance.slk' and (key == 'Primary' or key == 'preventPlace' or key == 'requirePlace')
|
|
or slk_name == 'units\\unitui.slk' and key == 'file'
|
|
or slk_name == 'units\\destructabledata.slk' and key == 'texFile'
|
|
or slk_name == 'units\\abilitydata.slk' and (key == 'targs1' or key == 'targs2' or key == 'targs3' or key == 'targs4')
|
|
then
|
|
add(x, y+1, '_')
|
|
elseif slk_name == 'units\\upgradedata.slk' and key == 'used' then
|
|
add(x, y+1, 1)
|
|
end
|
|
end
|
|
if os_clock() - clock > 0.1 then
|
|
clock = os_clock()
|
|
progress(y / #names)
|
|
message(('正在转换: [%s] (%d/%d)'):format(name, y, #names))
|
|
end
|
|
end
|
|
end
|
|
|
|
local function add_title(names)
|
|
for x, name in ipairs(names) do
|
|
add(x, 1, name)
|
|
end
|
|
end
|
|
|
|
local function add_head(names, skeys)
|
|
lines[#lines+1] = 'ID;PWXL;N;E'
|
|
lines[#lines+1] = ('B;X%d;Y%d;D0'):format(#skeys, #names+1)
|
|
end
|
|
|
|
local function get_names()
|
|
local names = {}
|
|
for name in pairs(slk) do
|
|
names[#names+1] = name
|
|
end
|
|
table_sort(names, function(name1, name2)
|
|
if default[name1] and not default[name2] then
|
|
return true
|
|
elseif not default[name1] and default[name2] then
|
|
return false
|
|
else
|
|
return name1 < name2
|
|
end
|
|
end)
|
|
return names
|
|
end
|
|
|
|
local function convert_slk(slk_name)
|
|
local names = get_names()
|
|
local skeys = slk_keys[slk_name]
|
|
add_head(names, skeys)
|
|
add_title(skeys)
|
|
add_values(names, skeys, slk_name)
|
|
add_end()
|
|
end
|
|
|
|
local function to_type(tp, value)
|
|
if tp == 0 then
|
|
if not value or value == 0 then
|
|
return nil
|
|
end
|
|
return math_floor(wtonumber(value))
|
|
elseif tp == 1 or tp == 2 then
|
|
if not value or value == 0 then
|
|
return nil
|
|
end
|
|
return wtonumber(value) + 0.0
|
|
elseif tp == 3 then
|
|
if not value then
|
|
return nil
|
|
end
|
|
if value == '' then
|
|
return nil
|
|
end
|
|
value = tostring(value)
|
|
if not value:match '[^ %-%_]' then
|
|
return nil
|
|
end
|
|
if value:match '^%.[mM][dD][lLxX]$' then
|
|
return nil
|
|
end
|
|
return value
|
|
end
|
|
end
|
|
|
|
local function is_usable_string(str)
|
|
local char = str:sub(1, 1)
|
|
if char == '-' or tonumber(char) then
|
|
return false
|
|
end
|
|
if char == '.' and tonumber(str:sub(2, 2)) then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function load_data(meta, obj, key, slk_data, obj_data)
|
|
if not obj[key] then
|
|
return
|
|
end
|
|
local displaykey = meta.field
|
|
local tp = meta.type
|
|
if type(obj[key]) == 'table' then
|
|
local over_level
|
|
obj_data[key] = {}
|
|
if slk_type == 'doodad' then
|
|
for i = 11, #obj[key] do
|
|
if obj[key][i] ~= obj[key][10] then
|
|
obj_data[key][i] = obj[key][i]
|
|
over_level = true
|
|
end
|
|
end
|
|
for i = 1, 10 do
|
|
local value = to_type(tp, obj[key][i])
|
|
if value and tp == 3 and not is_usable_string(value) then
|
|
obj_data[key][i] = value
|
|
report_failed(obj, displaykey, '字符串可以被转换为数字', value)
|
|
else
|
|
slk_data[('%s%02d'):format(displaykey, i)] = value
|
|
end
|
|
end
|
|
else
|
|
for i = 5, #obj[key] do
|
|
if obj[key][i] ~= obj[key][4] then
|
|
obj_data[key][i] = obj[key][i]
|
|
over_level = true
|
|
end
|
|
end
|
|
for i = 1, 4 do
|
|
local value = to_type(tp, obj[key][i])
|
|
if value and tp == 3 and not is_usable_string(value) then
|
|
obj_data[key][i] = value
|
|
report_failed(obj, displaykey, '字符串可以被转换为数字', value)
|
|
else
|
|
slk_data[displaykey..i] = value
|
|
end
|
|
end
|
|
end
|
|
if over_level then
|
|
report_failed(obj, displaykey, '数据超过了4级', '')
|
|
end
|
|
else
|
|
local value = to_type(tp, obj[key])
|
|
if value and tp == 3 and not is_usable_string(value) then
|
|
obj_data[key] = value
|
|
report_failed(obj, displaykey, '字符串可以被转换为数字', value)
|
|
else
|
|
slk_data[displaykey] = value
|
|
end
|
|
end
|
|
end
|
|
|
|
local function load_obj(id, obj, slk_name)
|
|
if remove_unuse_object and not obj._mark then
|
|
return nil
|
|
end
|
|
local obj_data = object[id]
|
|
if not obj_data then
|
|
obj_data = {}
|
|
object[id] = obj_data
|
|
obj_data._id = obj._id
|
|
obj_data._slk = true
|
|
obj_data._code = obj._code
|
|
obj_data._mark = obj._mark
|
|
obj_data._parent = obj._parent
|
|
end
|
|
local slk_data = {}
|
|
slk_data[slk_keys[slk_name][1]] = id
|
|
slk_data['code'] = obj._code
|
|
slk_data['name'] = obj._name
|
|
obj._slk = true
|
|
for _, key in ipairs(keys) do
|
|
local meta = metadata[slk_type][key]
|
|
load_data(meta, obj, key, slk_data, obj_data)
|
|
end
|
|
if metadata[obj._code] then
|
|
for key, meta in pairs(metadata[obj._code]) do
|
|
load_data(meta, obj, key, slk_data, obj_data)
|
|
end
|
|
end
|
|
return slk_data
|
|
end
|
|
|
|
local function load_chunk(chunk, slk_name)
|
|
for id, obj in pairs(chunk) do
|
|
slk[id] = load_obj(id, obj, slk_name)
|
|
end
|
|
end
|
|
|
|
return function(w2l_, type, slk_name, chunk, report_, obj)
|
|
slk = {}
|
|
w2l = w2l_
|
|
report = report_
|
|
object = obj
|
|
cx = nil
|
|
cy = nil
|
|
remove_unuse_object = w2l.config.remove_unuse_object
|
|
lines = {}
|
|
metadata = w2l:metadata()
|
|
keys = w2l:keydata()[slk_name]
|
|
default = w2l:get_default()[type]
|
|
slk_type = type
|
|
|
|
load_chunk(chunk, slk_name)
|
|
convert_slk(slk_name)
|
|
return table_concat(lines, '\r\n')
|
|
end
|
|
|