打开/关闭菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

模块:FrameChart:修订间差异

来自骷髅女孩Wiki
无编辑摘要
无编辑摘要
第10行: 第10行:
   -- _, frameKind表示,忽略迭代过程中的第一个返回值(即k,键),只关心第二个返回值(即v,值,循环变量命名为frameKind)
   -- _, frameKind表示,忽略迭代过程中的第一个返回值(即k,键),只关心第二个返回值(即v,值,循环变量命名为frameKind)
   -- inpairs保证以数组顺序(1-n)遍历数组,所以以下代码意为将frameKind依次赋值为s、a、r并进行循环体操作
   -- inpairs保证以数组顺序(1-n)遍历数组,所以以下代码意为将frameKind依次赋值为s、a、r并进行循环体操作
   for _, frameKind in ipairs({"startup", "active", "recovery", "nsc"}) do
   for _, frameKind in ipairs({"startup", "active", "recovery", "nsc", "linefour"}) do
     local duration
     local duration
     if frame.args[frameKind] ~= nil then  -- frame.args[frameKind]表示访问frame表中frameKind键对应的值
     if frame.args[frameKind] ~= nil then  -- frame.args[frameKind]表示访问frame表中frameKind键对应的值
第31行: 第31行:
       elseif frameKind == "nsc" then
       elseif frameKind == "nsc" then
         parsed = parseNSC(duration)
         parsed = parseNSC(duration)
      elseif frameKind == "linefour" then
        parsed = parseLineFour(duration)
       end
       end
     -- 如果parsed为nil,表示解析失败,报错
     -- 如果parsed为nil,表示解析失败,报错
第58行: 第60行:
local startup = frame.args['startup']
local startup = frame.args['startup']
local active = frame.args['active']
local active = frame.args['active']
    local inactive = frame.args['inactive2']
  local inactive = frame.args['inactive2']
local recovery = frame.args['recovery']
local recovery = frame.args['recovery']
local nsc = frame.args['nsc']
local nsc = frame.args['nsc']
     local activeData = parseActive(active)
     local activeData = parseActive(active)
     local frameAll = parseStartup(startup).startup + parseActive(active).active + parseRecovery(recovery).recovery
     local frameAll = parseStartup(startup).startup + parseActive(active).active + parseRecovery(recovery).recovery
     if inactive2 ~= nil then
     if inactive ~= nil then
     local i = 2
     local i = 2
     while (frame.args['inactive' .. tostring(i)] ~= nil) do
     while (frame.args['inactive' .. tostring(i)] ~= nil) do
第113行: 第115行:
index = index + 1
index = index + 1
end
end
frameChartDataHtml2 = mw.html.create('div'):addClass('frameChart-data'):done()
frameChartDataHtml2 = mw.html.create('div'):addClass('frameChart-data'):done()
frameChartBody:node(frameChartDataHtml2)
frameChartBody:node(frameChartDataHtml2)
drawFrame(startup, "vuln", 2)
  if isInvul(frame.args['properties']) == false and frame.args['vul'] == nil then
drawFrame(active, "vuln", 2)
    drawFrame(startup, "vuln", 2)
index = 2
    drawFrame(active, "vuln", 2)
while tonumber(frame.args['active' .. index]) ~= nil or tonumber(frame.args['inactive' .. index]) ~= nil do
    index = 2
drawFrame(frame.args['active' .. index], "vuln", 2)
    while tonumber(frame.args['active' .. index]) ~= nil or tonumber(frame.args['inactive' .. index]) ~= nil do
drawFrame(frame.args['inactive' .. index], "vuln", 2)
      drawFrame(frame.args['active' .. index], "vuln", 2)
index = index + 1
      drawFrame(frame.args['inactive' .. index], "vuln", 2)
end
      index = index + 1
drawFrame(recovery, "vuln", 2)
    end
    drawFrame(recovery, "vuln", 2)
  else
    drawFrame(startup, "strike", 2)
  end


frameChartDataHtml3 = mw.html.create('div'):addClass('frameChart-data'):done()
frameChartDataHtml3 = mw.html.create('div'):addClass('frameChart-data'):done()
第131行: 第138行:
-- 检查是否存在更多的非活跃帧,遍历处理,逻辑类似于上文的取消窗口
-- 检查是否存在更多的非活跃帧,遍历处理,逻辑类似于上文的取消窗口
index = 2
index = 2
while tonumber(frame.args['nsc' .. index]) ~= nil do
while tonumber(frame.args['nsc' .. index]) ~= nil or tonumber(frame.args['inactive' .. index]) ~= nil do
drawFrame(frame.args['nsc' .. index], "nsc", 3)
drawFrame(frame.args['nsc' .. index], "nsc", 3)
drawFrame(frame.args['nsc' .. index + 1], "placeholder", 3)
drawFrame(frame.args['nsc' .. index], "placeholder", 3)
index = index + 2
index = index + 1
end
end
  if frame.args['spe'] ~= nil then
    frameChartDataHtml4 = mw.html.create('div'):addClass('frameChart-data'):done()
    frameChartBody:node(frameChartDataHtml4)
    if frame.args['spetype'] == nil then
      drawFrame(frame.args['spe'], "placeholder", 4)
    else
      drawFrame(frame.args['spe'], tostring(frame.args['spetype']), 4)
    end
    local i = 2
    while frame.args['spe' .. tostring(i)] do
      drawFrame(frame.args['spe' .. tostring(i)], tostring(frame.args['spetype' .. tostring(i)]), 4)
    end
  end 
   -- 将生成的HTML结构转换为字符串,并使用定义在Module:FrameChart/styles.css中的样式渲染
   -- 将生成的HTML结构转换为字符串,并使用定义在Module:FrameChart/styles.css中的样式渲染
return tostring(html) .. mw.getCurrentFrame():extensionTag{
return tostring(html) .. mw.getCurrentFrame():extensionTag{
第215行: 第236行:
       pos = p2
       pos = p2
     end
     end
     if pos ~= string.len(duration)+1 then
     if pos ~= string.len(duration) + 1 then
       return nil -- trailing stuff at the end
       return nil -- trailing stuff at the end
     end
     end
第260行: 第281行:
       return true
       return true
     elseif string.find(str, "{{特性|指令投}}") then
     elseif string.find(str, "{{特性|指令投}}") then
      return true
    else
      return false
    end
  end
end
function isInvul(str)
  if str ~= nil then
    if string.find(str, "无敌") then
       return true
       return true
     else
     else
第289行: 第320行:
       pos = p2
       pos = p2
     end
     end
     if pos ~= string.len(duration)+1 then
     if pos ~= string.len(duration) + 1 then
       return nil -- trailing stuff at the end
       return nil -- trailing stuff at the end
    end
    return out
  end
  return nil
end
function parseLineFour(duration)
  local out = {}
  if string.match(duration, "^%a+%d+") == 0 then
    local firstval, pos = string.match(duration, "^(%d+)()")
    out['spe'] = firstval
  else
    local firsttype, firstval, pos = string.match(duration, "^(%a+)(%d+)()")
    if firstval == nil then
      return nil
    end
    out['spe'] = firstval
    out['spetype'] = firsttype
    local ordinal = 2
    for p1, type, dur, p2 in string.gmatch(duration, "(),%s*(%a+)(%d+)%s*()") do
      if pos ~= p1 then
        return nil
      end
      out['spe' .. tostring(ordinal)] = dur
      out['spetype' .. tostring(ordinal)] = type
      ordinal = ordinal + 1
      pos = p2
      if pos ~= string.len(duration) + 1 then
        return nil
      end
    end
    if out['spetype'] ~= nil then
      if string.match(out['spetype'], "arm") ~= nil then
        string.gsub(out['spetype'], "arm", "armor")
      end
      if string.match(out['spetype'], "hyp") ~= nil then
        string.gsub(out['spetype'], "hyp", "hyperarmor")
      end
      if string.match(out['spetype'], "pro") ~= nil then
        string.gsub(out['spetype'], "pro", "hyperarmor-proj")
      end
      if string.match(out['spetype'], "hat") ~= nil then
        string.gsub(out['spetype'], "hat", "hatredguard")
      end
      if string.match(out['spetype'], "noc") ~= nil then
        string.gsub(out['spetype'], "noc", "nocollision")
      end
      if string.match(out['spetype'], "nocwh") ~= nil then
        string.gsub(out['spetype'], "nocwh", "nocollision-when-hit")
      end
    end
    local i = 2
    while out['spetype' .. tostring(i)] do
      if string.match(out['spetype' .. tostring(i)], "arm") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "arm", "armor")
      end
      if string.match(out['spetype' .. tostring(i)], "hyp") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "hyp", "hyperarmor")
      end
      if string.match(out['spetype' .. tostring(i)], "pro") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "pro", "hyperarmor-proj")
      end
      if string.match(out['spetype' .. tostring(i)], "hat") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "hat", "hatredguard")
      end
      if string.match(out['spetype' .. tostring(i)], "noc") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "noc", "nocollision")
      end
      if string.match(out['spetype' .. tostring(i)], "nocwh") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "nocwh", "nocollision-when-hit")
      end
      i = i + 1
     end
     end
     return out
     return out

2025年1月1日 (三) 00:45的版本

此模块的文档可以在模块:FrameChart/doc创建

-- FrameChart
local p = {}
local html
local frameChartDataHtml

function p.drawFrameData( frame )
  -- 开始循环,遍历包含{startup, active, recovery}的列表
  -- 对于每种帧类型,从fd中获取相应的帧数数据并存储在duration中
  -- 如果frame参数中有对应的帧类型字段,使用frame:preprocess方法处理该字段值
  -- _, frameKind表示,忽略迭代过程中的第一个返回值(即k,键),只关心第二个返回值(即v,值,循环变量命名为frameKind)
  -- inpairs保证以数组顺序(1-n)遍历数组,所以以下代码意为将frameKind依次赋值为s、a、r并进行循环体操作
  for _, frameKind in ipairs({"startup", "active", "recovery", "nsc", "linefour"}) do
    local duration
    if frame.args[frameKind] ~= nil then  -- frame.args[frameKind]表示访问frame表中frameKind键对应的值
      duration = frame:preprocess(frame.args[frameKind])  -- frame:preprocess的参数中如有MW模板或变量(如{{Var}}),会自动展开
    end
    -- 如果duration为nil,则跳过这个帧类型
    if duration == nil then
    -- 如果duration可以直接转化为数字,则直接赋值给frame.args[frameKind]
    elseif tonumber(duration) ~= nil then
      frame.args[frameKind] = duration
    -- 如果不是数字,则使用对应的解析函数解析帧数字并存储于
    else
      local parsed = nil
      if frameKind == "active" then
        parsed = parseActive(duration)
      elseif frameKind == "startup" then
        parsed = parseStartup(duration)
      elseif frameKind == "recovery" then
        parsed = parseRecovery(duration)
      elseif frameKind == "nsc" then
        parsed = parseNSC(duration)
      elseif frameKind == "linefour" then
        parsed = parseLineFour(duration)
      end
    -- 如果parsed为nil,表示解析失败,报错
      if parsed == nil then
        return string.format('<span class="error">The number of %s frames is not a simple number (%s). Please specify explicitly.</span>', frameKind, tostring(duration))
      end
    -- 如果解析成功,遍历解析后的表parsed,将每个字段值赋给frame.args[k]
      for k, v in pairs(parsed) do  -- parsed格式会是{startup = s, active = a, recovery = r}的数据格式
        frame.args[k] = v
      end
    end
  end
  -- 将此模块中p.drawFrameData的参数处理后输入给调用Module:MetaFrameChart的drawFrameData函数
  return metaFrameData(frame)
end

function metaFrameData(frame)
	currentFrame = 0  --跟踪当前帧数的变量
	html = mw.html.create('div')  --mw的html方法,创造一个div标签
	html:addClass('frameChart')   --给这个div标签添加frameChart类
	-- 如果传递的框架frame中有title参数,则将其值赋给title临时变量
	local title
	if frame.args['title'] ~= nil then
		title = frame.args['title']
	end
	-- 从传递的框架frame中获取startup、active、recovery值并赋值给同名变量
	local startup = frame.args['startup']
	local active = frame.args['active']
  local inactive = frame.args['inactive2']
	local recovery = frame.args['recovery']
	local nsc = frame.args['nsc']
    local activeData = parseActive(active)
    local frameAll = parseStartup(startup).startup + parseActive(active).active + parseRecovery(recovery).recovery
    if inactive ~= nil then
    local i = 2
    while (frame.args['inactive' .. tostring(i)] ~= nil) do
      frameAll = frameAll + frame.args['inactive' .. tostring(i)] + frame.args['active' .. tostring(i+1)]
      i = i + 2
      end
    end
    local backWidth = 22 + 12 * frameAll
    local backWidthArg = 'width: ' .. (math.ceil((12 * frameAll - 60) / 60) * 60 + 82) .. 'px'
    local backBlockN = math.ceil((backWidth - 82) / 60 )
    local scrollbarArg = 'scrollbar-color: var(--color-base) var(--color-surface-3); scrollbar-width: thin;'
    -- 如果提供title变量,则创建一个包含title的div标签,并添加到HTML结构中,添加frameChart-title类
	if title ~= nil then
		html:tag('div'):addClass('frameChart-title'):wikitext(title):done()
	end
	-- 创建div标签并添加类frameChart-data,将此帧数数据容器添加到HTML主结构中
    frameChartContainer = mw.html.create('div'):addClass('frameChart-back'):attr('style', scrollbarArg .. backWidthArg):done()
    html:node(frameChartContainer)
    frameChartStrap = {frameChartStrap1, frameChartStrap2, frameChartStrap3, frameChartStrap4, frameChartStrap5, frameChartStrap6}
    local frameChartStrapIndex = 1
    while frameChartStrapIndex < 7 do
    	frameChartContainer:node(mw.html.create('div'):addClass('frameChart-back-strap'):addClass('frameChart-back-strap-pos-' .. frameChartStrapIndex):attr('style', backWidthArg))
		frameChartStrapIndex = frameChartStrapIndex +1
    end
    frameChartContainer:node(mw.html.create('div'):addClass('frameChart-back-block-first'):done())
    local frameChartBlockIndex = 1
    while frameChartBlockIndex < (backBlockN + 1) do
    	local leftArg = 'left: ' .. (80 + 60 * (frameChartBlockIndex - 1) + 2) .. 'px'
    	frameChartContainer:node(mw.html.create('div'):addClass('frameChart-back-block'):attr('style', leftArg))
    	frameChartBlockIndex = frameChartBlockIndex + 1
    end
    frameChartContainer:node(mw.html.create('div'):addClass('frameChart-startline'):done())
    frameChartBody = mw.html.create('div'):addClass('frameChart-body'):done()
	frameChartContainer:node(frameChartBody)
	frameChartDataHtml1 = mw.html.create('div'):addClass('frameChart-data'):done()
	frameChartBody:node(frameChartDataHtml1)
    -- 调用drawFrame函数,绘制第一行启动帧占位
	drawFrame(startup, "placeholder", 1)
	if isThrow(frame.args['properties']) then
    drawFrame(active, "throw", 1)
  else
    drawFrame(active, "strike", 1)
  end
	-- 检查是否存在更多的非活跃帧,遍历处理,逻辑类似于上文的取消窗口
	index = 2
	while tonumber(frame.args['active' .. index]) ~= nil or tonumber(frame.args['inactive' .. index]) ~= nil do
		drawFrame(frame.args['active' .. index], "strike", 1)
		drawFrame(frame.args['inactive' .. index], "placeholder", 1)
		index = index + 1
	end

	frameChartDataHtml2 = mw.html.create('div'):addClass('frameChart-data'):done()
	frameChartBody:node(frameChartDataHtml2)
  if isInvul(frame.args['properties']) == false and frame.args['vul'] == nil then
    drawFrame(startup, "vuln", 2)
    drawFrame(active, "vuln", 2)
    index = 2
    while tonumber(frame.args['active' .. index]) ~= nil or tonumber(frame.args['inactive' .. index]) ~= nil do
      drawFrame(frame.args['active' .. index], "vuln", 2)
      drawFrame(frame.args['inactive' .. index], "vuln", 2)
      index = index + 1
    end
    drawFrame(recovery, "vuln", 2)
  else
    drawFrame(startup, "strike", 2)
  end

	frameChartDataHtml3 = mw.html.create('div'):addClass('frameChart-data'):done()
	frameChartBody:node(frameChartDataHtml3)
    -- 调用drawFrame函数,绘制第一行启动帧占位
	drawFrame(nsc, "placeholder", 3)
	-- 检查是否存在更多的非活跃帧,遍历处理,逻辑类似于上文的取消窗口
	index = 2
	while tonumber(frame.args['nsc' .. index]) ~= nil or tonumber(frame.args['inactive' .. index]) ~= nil do
		drawFrame(frame.args['nsc' .. index], "nsc", 3)
		drawFrame(frame.args['nsc' .. index], "placeholder", 3)
		index = index + 1
	end

  if frame.args['spe'] ~= nil then
    frameChartDataHtml4 = mw.html.create('div'):addClass('frameChart-data'):done()
    frameChartBody:node(frameChartDataHtml4)
    if frame.args['spetype'] == nil then
      drawFrame(frame.args['spe'], "placeholder", 4)
    else
      drawFrame(frame.args['spe'], tostring(frame.args['spetype']), 4)
    end
    local i = 2
    while frame.args['spe' .. tostring(i)] do
      drawFrame(frame.args['spe' .. tostring(i)], tostring(frame.args['spetype' .. tostring(i)]), 4)
    end
  end  
  -- 将生成的HTML结构转换为字符串,并使用定义在Module:FrameChart/styles.css中的样式渲染
	return tostring(html) .. mw.getCurrentFrame():extensionTag{
		name = 'templatestyles', args = { src = 'Module:FrameChart/styles.css' }
	}
end

function drawFrame(frames, frameType, line)  -- frames为绘制的帧数,是数字;frameType表示帧类型,如active等
	if tonumber(frames) ~= nil then    -- 将frames转化为数字,如果转化成功说明其为一个有效的数字字符串
		for i=1, tonumber(frames) do   -- 循环从1到frames(帧数)次
			--currentFrame = currentFrame + 1  -- 每次循环时更新currentFrame数
			local frameDataHtml = mw.html.create('div')  --创建新div标签表示单个帧(小方块)
			frameDataHtml:addClass('frame-data'):addClass('frame-data-' .. frameType):done()  --为其添加类frame-data和类frame-data-frameType
			if tonumber(line) == 1 then
				frameChartDataHtml1:node(frameDataHtml)  --完成div标签列的构建并添加到frameChartDataHtml1结构中
			elseif tonumber(line) == 2 then
				frameChartDataHtml2:node(frameDataHtml)
			elseif tonumber(line) == 3 then
				frameChartDataHtml3:node(frameDataHtml)
			elseif tonumber(line) == 4 then
				frameChartDataHtml4:node(frameDataHtml)
			end
		end
	end
end

-- 解析函数,类似MetaFrameChart模块中的功能
function parseActive(duration)
  -- simple number
  if tonumber(duration) ~= nil then
    return {active = tonumber(duration)}
  end

  if string.find(duration, "%d+%s*,") ~= nil then
    -- 1,2,3,4 format -- multihit with no gaps
    local totalActive = 0

    -- first match - just a number
    local firstval, pos = string.match(duration, "^(%d+)%s*()")
    if firstval == nil then
      return nil
    end
    -- subsequent matches - coma, then number. Might have spaces between them
    totalActive = totalActive + tonumber(firstval)
    for p1, dur, p2 in string.gmatch(duration, "(),%s*(%d+)%s*()") do
      if pos ~= p1 then
        return nil
      end
      pos = p2
      totalActive = totalActive + tonumber(dur)
    end
    if pos ~= string.len(duration)+1 then
      return nil -- trailing stuff at the end
    end
    -- Done.
    local out = {active = totalActive}
    return out 
  elseif mw.ustring.find(duration, "^%d+[x×]%d+$") ~= nil then
    -- 3x4 format -- also multihit with no gaps
    local a, b = mw.ustring.match(duration, "^(%d+)[x×](%d+)$")
    local out = { active = tonumber(a) * tonumber(b) }
    return out
  elseif string.find(duration, "^%d+%(%d+%)") ~= nil then
    -- 1(2)3(4)5 format -- multihit with gaps
    local out = {}
    -- special handling for the first number
    local firstval, pos = string.match(duration, "^(%d+)()")
    out['active'] = firstval

    local ordinal = 2
    -- then we just have a groups of "(inactive)active"
    for p1, d1, d2, p2 in string.gmatch(duration, "()%((%d+)%)(%d+)()") do
      if pos ~= p1 then
        return nil -- not directly following the previous match, so basically not what we expect
      end
      out['inactive' .. tostring(ordinal)] = d1
      out['active' .. tostring(ordinal+1)] = d2
      ordinal = ordinal + 2
      pos = p2
    end
    if pos ~= string.len(duration) + 1 then
      return nil -- trailing stuff at the end
    end
    -- Done.
    return out
  end
  -- unrecognized format
  return nil
end

function parseStartup(duration)
  -- simple number
  if tonumber(duration) ~= nil then
    return {startup = tonumber(duration)}
  end

  -- 1+2 -- common for supers
  if string.find(duration, "^%d+%+%d+$") ~= nil then
    local first, second = string.match(duration, "^(%d+)%+(%d+)$")
    return {startup = tonumber(first) + tonumber(second) }
  end
  return nil
end

function parseRecovery(duration)
  -- simple number
  if tonumber(duration) ~= nil then
    return {recovery = tonumber(duration)}
  end

  -- "Until L+10" -- aerial moves, such as grabs.
  if string.find(duration, "^Until L%+%d+$") ~= nil then
    local recovery = string.match(duration, "^Until L%+(%d+)$")
    return {recovery=0, specialRecovery=recovery }
  end
  return nil
end

function isThrow(str)
  if str ~= nil then
    if string.find(str, "{{特性|投}}") then
      return true
    elseif string.find(str, "{{特性|空投}}") then
      return true
    elseif string.find(str, "{{特性|指令投}}") then
      return true
    else
      return false
    end
  end
end

function isInvul(str)
  if str ~= nil then
    if string.find(str, "无敌") then
      return true
    else
      return false
    end
  end
end

function parseNSC(duration)
  -- simple number
  if tonumber(duration) ~= nil then
    return {nsc = tonumber(duration)}
  end
  if string.find(duration, "^%d+%(%d+%)") ~= nil then
    -- 1(2)3(4)5 format -- multihit with gaps
    local out = {}
    -- special handling for the first number
    local firstval, pos = string.match(duration, "^(%d+)()")
    out['nsc'] = firstval
    local ordinal = 2
    -- then we just have a groups of "(inactive)active"
    for p1, d1, d2, p2 in string.gmatch(duration, "()%((%d+)%)(%d+)()") do
      if pos ~= p1 then
        return nil -- not directly following the previous match, so basically not what we expect
      end
      out['nsc' .. tostring(ordinal)] = d1
      out['nsc' .. tostring(ordinal+1)] = d2
      ordinal = ordinal + 2
      pos = p2
    end
    if pos ~= string.len(duration) + 1 then
      return nil -- trailing stuff at the end
    end
    return out
  end
  return nil
end

function parseLineFour(duration)
  local out = {}
  if string.match(duration, "^%a+%d+") == 0 then
    local firstval, pos = string.match(duration, "^(%d+)()")
    out['spe'] = firstval
  else
    local firsttype, firstval, pos = string.match(duration, "^(%a+)(%d+)()")
    if firstval == nil then
      return nil
    end
    out['spe'] = firstval
    out['spetype'] = firsttype
    local ordinal = 2
    for p1, type, dur, p2 in string.gmatch(duration, "(),%s*(%a+)(%d+)%s*()") do
      if pos ~= p1 then
        return nil
      end
      out['spe' .. tostring(ordinal)] = dur
      out['spetype' .. tostring(ordinal)] = type
      ordinal = ordinal + 1
      pos = p2
      if pos ~= string.len(duration) + 1 then
        return nil
      end
    end
    if out['spetype'] ~= nil then
      if string.match(out['spetype'], "arm") ~= nil then
        string.gsub(out['spetype'], "arm", "armor")
      end
      if string.match(out['spetype'], "hyp") ~= nil then
        string.gsub(out['spetype'], "hyp", "hyperarmor")
      end
      if string.match(out['spetype'], "pro") ~= nil then
        string.gsub(out['spetype'], "pro", "hyperarmor-proj")
      end
      if string.match(out['spetype'], "hat") ~= nil then
        string.gsub(out['spetype'], "hat", "hatredguard")
      end
      if string.match(out['spetype'], "noc") ~= nil then
        string.gsub(out['spetype'], "noc", "nocollision")
      end
      if string.match(out['spetype'], "nocwh") ~= nil then
        string.gsub(out['spetype'], "nocwh", "nocollision-when-hit")
      end
    end
    local i = 2
    while out['spetype' .. tostring(i)] do
      if string.match(out['spetype' .. tostring(i)], "arm") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "arm", "armor")
      end
      if string.match(out['spetype' .. tostring(i)], "hyp") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "hyp", "hyperarmor")
      end
      if string.match(out['spetype' .. tostring(i)], "pro") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "pro", "hyperarmor-proj")
      end
      if string.match(out['spetype' .. tostring(i)], "hat") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "hat", "hatredguard")
      end
      if string.match(out['spetype' .. tostring(i)], "noc") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "noc", "nocollision")
      end
      if string.match(out['spetype' .. tostring(i)], "nocwh") ~= nil then
        string.gsub(out['spetype' .. tostring(i)], "nocwh", "nocollision-when-hit")
      end
      i = i + 1
    end
    return out
  end
  return nil
end

p.drawFrame = drawFrame
return p