模块:Driver

来自乐园数据管理室

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

local p = {}
local QueryHelper = require("Module:Xb2QueryHelper")
local html = require("Module:Html")
local BdatEnums = require("Module:BdatEnums")

function parseArtsDataPerLevel(Arts, level)
  -- 是否为普通攻击
  local isNormal = Arts.Normal
  if isNormal then
    level = 1
  end

  -- 按技能等级取值
  local DmgMgn = Arts["DmgMgn" .. level]
  local Enhance =
    Arts["Enhance" .. level] > 0 and QueryHelper.getEnhance(Arts["Enhance" .. level]).RenderedCaption or ""
  local Recast = Arts["Recast" .. level]

  local hitNum, hit, ReActText

  if Arts.ArtsType == 11 then -- 类型 格挡
    DmgMgn = tostring(DmgMgn / 30) .. "秒"
  else
    hitNum = 0 -- 判定数
    local HitFrm = {} -- 判定帧
    for i = 1, 16 do
      table.insert(HitFrm, Arts["HitFrm" .. i])
      if Arts["HitFrm" .. i] > 0 then
        hitNum = hitNum + 1
      end
    end

    local DmgRt = {}
    local DmgRtTotal = 0
    for i = 1, 16 do
      local potencyRatio = Arts["DmgRt" .. i]
      if potencyRatio == 255 then
        potencyRatio = -1
      end
      table.insert(DmgRt, potencyRatio)

      if potencyRatio > 0 then
        DmgRtTotal = DmgRtTotal + potencyRatio
      end
    end

    local ReAct, ReActDict = {}, {}
    for i = 1, 16 do
      local ReActStr = Arts["ReAct" .. i] > 0 and BdatEnums.ReActType[Arts["ReAct" .. i]] or ""
      table.insert(ReAct, ReActStr)
      ReActDict[Arts["ReAct" .. i]] = ReActStr
    end
    ReActDict[0] = nil

    ReActText = {}
    for _, v in pairs(ReActDict) do
      table.insert(ReActText, v)
    end
    ReActText = table.concat(ReActText, " ")

    hit = {}
    local DmgTotal = 0
    for i = 1, 16 do
      local dmgPerHit = 0
      if DmgRtTotal > 0 then -- DmgRt 总和大于0时,每hit伤害按 DmgRt 分割
        if DmgRt[i] > 0 then
          dmgPerHit = DmgMgn * DmgRt[i] / DmgRtTotal
        end
      else -- DmgRtTotal 为 0,每hit伤害按hit数平分
        if HitFrm[i] > 0 and DmgRt[i] == 0 then -- HitFrame存在且DmgRt不为-1
          dmgPerHit = DmgMgn / hitNum
        end
      end

      if HitFrm[i] > 0 or dmgPerHit > 0 or #ReAct[i] > 0 then
        local dmg = tonumber(mw.ustring.format("%.2f", dmgPerHit))
        table.insert(
          hit,
          {
            HitTime = tonumber(mw.ustring.format("%.2f", HitFrm[i] / 30)),
            Dmg = dmg > 0 and dmg or "",
            ReAct = ReAct[i]
          }
        )
        DmgTotal = DmgTotal + dmgPerHit
      end
    end
  end

  return {
    Level = not isNormal and level or nil,
    DmgMgn = DmgMgn,
    Recast = Recast,
    Enhance = Enhance,
    HitNum = hitNum,
    PerHits = hit,
    ReActText = ReActText or ""
  }
end

function p.getDataByDriverId(DriverId)
  local DriverData = QueryHelper.getDr(DriverId)

  DriverData.Arts = QueryHelper.getArtsDrListByDriver(DriverId)
  DriverData.Skill = QueryHelper.getSkillDrList(DriverId)
  return DriverData
end

function p.getArtsByDriverIdAndWpnType(driverId, WpnType)
  return QueryHelper.getArtsDrListByDriverAndWpnType(driverId, WpnType)
end

function parseArtsDr(Arts)
  local ArtsType = BdatEnums.ArtsType[Arts.ArtsType]
  local RangeTypeText = Arts.ArtsType == 3 and "" or BdatEnums.ArtsRangeType[Arts.RangeType]
  local ArtsBuff = Arts.ArtsBuff > 0 and BdatEnums.ArtsBuff[Arts.ArtsBuff] or ""
  local ArtsDeBuff = Arts.ArtsDeBuff > 0 and QueryHelper.getBuff(Arts.ArtsDeBuff).NameText or ""
  local Target = BdatEnums.ArtsTarget[Arts.Target]

  local PerLevel = {}
  if Arts.Normal then
    table.insert(PerLevel, parseArtsDataPerLevel(Arts))
  else
    for i = 1, 6 do -- 武技六段强化
      table.insert(PerLevel, parseArtsDataPerLevel(Arts, i))
    end
  end

  local ReleaseLv
  local NeedWP
  if not Arts.Normal then
    ReleaseLv = {}
    NeedWP = {}
    for level = 1, 5 do
      table.insert(ReleaseLv, Arts["ReleaseLv" .. level])
      table.insert(NeedWP, Arts["NeedWP" .. level])
    end
  end

  return {
    ID = Arts.row_id,
    Name = Arts.Name == 0 and "" or Arts.NameText,
    DebugName = Arts.DebugName,
    Caption = Arts.Caption,
    CaptionText = Arts.CaptionText or "",
    CondCapText = Arts.CondCapText,
    TalentCapText = Arts.TalentCapText,
    DriverId = Arts.Driver,
    WpnType = Arts.WpnType,
    ActNo = Arts.ActNo,
    IrType = Arts.IrType,
    ActSpeed = Arts.ActSpeed .. "%",
    ArtsType = ArtsType,
    ArtsBuff = ArtsBuff,
    ArtsDeBuff = ArtsDeBuff,
    Target = Target,
    Distance = Arts.Distance,
    RangeType = Arts.RangeType,
    RangeTypeText = RangeTypeText,
    Radius = Arts.Radius,
    Length = Arts.Length,
    Hate = Arts.Hate,
    HitRevise = Arts.HitRevise .. "%",
    CriRevise = Arts.CriRevise .. "%",
    Normal = Arts.Normal,
    SArmor1 = Arts.SArmor1,
    SArmor2 = Arts.SArmor2,
    PerLevel = PerLevel,
    ReleaseLv = ReleaseLv,
    NeedWP = NeedWP
  }
end

local IrArtsType = {
  [0] = {label = "VanguardArts", name = "攻击者武技"},
  [1] = {label = "RearGuardArts", name = "支援者武技"},
  [2] = {label = "TalentArt", name = "天赋武技"},
  [3] = {label = "SwitchArt", name = "切换武技"}
}

function p.formatArtsData(ArtsDataTable)
  local WpnArts = {}
  for _, ArtsRow in ipairs(ArtsDataTable) do
    local ArtsData = parseArtsDr(ArtsRow)
    WpnArts[ArtsRow.WpnType] = WpnArts[ArtsRow.WpnType] or {}

    if ArtsRow.Normal then
      -- 自动攻击
      WpnArts[ArtsRow.WpnType].NormalAttack = WpnArts[ArtsRow.WpnType].NormalAttack or {}
      -- 将lv1的属性合并到根属性
      for k, v in pairs(ArtsData.PerLevel[1]) do
        ArtsData[k] = v
      end
      ArtsData.PerLevel = nil

      -- 展开判断表
      ArtsData.HitText =
        ArtsData.PerHits and
        html.wikitable(
          {
            {scope = "HitTime", label = "秒"},
            {scope = "Dmg", label = "伤害倍率"}
          },
          ArtsData.PerHits,
          {class = "borderless-table"}
        ) or
        ""

      table.insert(WpnArts[ArtsRow.WpnType].NormalAttack, ArtsData)
    else
      -- 御刃者武技
      if ArtsData.DriverId < 17 then
        WpnArts[ArtsRow.WpnType].DriverArts = WpnArts[ArtsRow.WpnType].DriverArts or {}
      else
        for i = 0, 3 do
          WpnArts[ArtsRow.WpnType][IrArtsType[i].label] = WpnArts[ArtsRow.WpnType][IrArtsType[i].label] or {}
        end
      end

      -- 数据处理
      if ArtsData.RangeType == 2 or ArtsData.RangeType == 3 then
        -- 前后方的方形判定范围,增加LengthWidth属性
        ArtsData.LengthWidth = ArtsData.Length .. " x " .. ArtsData.Radius
        ArtsData.Radius = nil
      elseif ArtsData.RangeType == 0 then
        ArtsData.Radius = nil
      end

      -- 按等级复制table
      for level = 1, 6 do
        local perLevelData = {}
        -- 把当前lv的属性拷贝到新对象
        for k, v in pairs(ArtsData.PerLevel[level]) do
          perLevelData[k] = v
        end

        -- 定义ArtsData上需要拷贝的字段
        local propertyList = {
          "ActNo",
          "ActSpeed",
          "ArtsBuff",
          "ArtsDeBuff",
          "ArtsType",
          "Caption",
          "CaptionText",
          "CriRevise",
          "DebugName",
          "Distance",
          "DriverId",
          "Hate",
          "HitRevise",
          "ID",
          "Length",
          "Name",
          "Normal",
          "Radius",
          "RangeType",
          "RangeTypeText",
          "SArmor1",
          "SArmor2",
          "Target",
          "WpnType"
        }
        for _, property in ipairs(propertyList) do
          perLevelData[property] = ArtsData[property]
        end

        perLevelData.ReleaseLv = ArtsData.ReleaseLv[level]
        perLevelData.NeedWP = ArtsData.NeedWP[level]

        -- 展开hit表
        perLevelData.HitText =
          perLevelData.PerHits and
          html.wikitable(
            {
              {scope = "HitTime", label = "秒"},
              {scope = "Dmg", label = "伤害倍率"},
              {scope = "ReAct", label = "特效"}
            },
            perLevelData.PerHits,
            {class = "borderless-table"}
          ) or
          ""

        if ArtsData.DriverId < 17 then
          WpnArts[ArtsRow.WpnType].DriverArts[level] = WpnArts[ArtsRow.WpnType].DriverArts[level] or {}
          table.insert(WpnArts[ArtsRow.WpnType].DriverArts[level], perLevelData)
        else
          if ArtsData.IrType == 0 then
            WpnArts[ArtsRow.WpnType].VanguardArts[level] = WpnArts[ArtsRow.WpnType].VanguardArts[level] or {}
            table.insert(WpnArts[ArtsRow.WpnType].VanguardArts[level], perLevelData)
          elseif ArtsData.IrType == 1 then
            WpnArts[ArtsRow.WpnType].RearGuardArts[level] = WpnArts[ArtsRow.WpnType].RearGuardArts[level] or {}
            table.insert(WpnArts[ArtsRow.WpnType].RearGuardArts[level], perLevelData)
          elseif ArtsData.IrType == 2 then
            WpnArts[ArtsRow.WpnType].SwitchArt[level] = WpnArts[ArtsRow.WpnType].SwitchArt[level] or {}
            table.insert(WpnArts[ArtsRow.WpnType].SwitchArt[level], perLevelData)
          elseif ArtsData.IrType == 3 then
            WpnArts[ArtsRow.WpnType].TalentArt[level] = WpnArts[ArtsRow.WpnType].TalentArt[level] or {}
            table.insert(WpnArts[ArtsRow.WpnType].TalentArt[level], perLevelData)
          end
        end
      end
    end
  end
  return WpnArts
end

function p.outputArtsSection(WpnArts, WpnId)
  local out = ""

  out = out .. html.h4("自动攻击")
  out =
    out ..
    mw.ustring.format(
      [[
        <table class="wikitable">
            <tr>
                <th>类型</th>
                <th>攻击距离</th>
                <th>仇恨</th>
                <th>伤害倍率</th>
                <th>攻击次数</th>
            </tr>
            <tr>
                <td rowspan="3">%s</td>
                <td rowspan="3">%s</td>
                <td rowspan="3">%s</td>
                <td>%s</td>
                <td>%s</td>
            </tr>
            <tr>
                <td>%s</td>
                <td>%s</td>
            </tr>
            <tr>
                <td>%s</td>
                <td>%s</td>
            </tr>
        </table>
      ]],
      WpnArts[WpnId].NormalAttack[1].ArtsType,
      WpnArts[WpnId].NormalAttack[1].Distance,
      WpnArts[WpnId].NormalAttack[1].Hate,
      WpnArts[WpnId].NormalAttack[1].DmgMgn,
      #WpnArts[WpnId].NormalAttack[1].PerHits,
      WpnArts[WpnId].NormalAttack[2].DmgMgn,
      #WpnArts[WpnId].NormalAttack[2].PerHits,
      WpnArts[WpnId].NormalAttack[3].DmgMgn,
      #WpnArts[WpnId].NormalAttack[3].PerHits
    )

  if WpnArts[WpnId].DriverArts and #WpnArts[WpnId].DriverArts > 0 then -- DriverArts属性存在,表示本篇御刃者
    out = out .. html.h4("御刃者武技")
    out = out .. '<div class="xbxbtabs arts">'
    for lv, perLevelData in ipairs(WpnArts[WpnId].DriverArts) do
      local tabslabel = "Lv" .. lv
      local tabsname = WpnId .. tabslabel
      out =
        out ..
        '<div data-name="' ..
          tabsname ..
            '" data-label="' ..
              tabslabel ..
                '" >' ..
                  html.wikitable(
                    {
                      {
                        scope = "Name",
                        label = "名称"
                      },
                      {
                        scope = "ArtsType",
                        label = "类型"
                      },
                      {
                        scope = "RangeTypeText",
                        label = "范围"
                      },
                      {
                        scope = "Distance",
                        label = "目标距离"
                      },
                      {
                        scope = "DmgMgn",
                        label = "倍率"
                      },
                      {
                        scope = "Recast",
                        label = "填充量"
                      },
                      {
                        scope = "Hate",
                        label = "仇恨"
                      },
                      {
                        scope = "ReActText",
                        label = "反应动作"
                      },
                      {
                        scope = "Enhance",
                        label = "特效"
                      }
                    },
                    perLevelData,
                    {
                      width = "100%",
                      expandRow = {
                        {scope = "Radius", label = "判定半径"},
                        {scope = "LengthWidth", label = "判定范围"},
                        {scope = "HitRevise", label = "命中修正"},
                        {scope = "CriRevise", label = "暴击修正"},
                        {scope = "ArtsBuff", label = "技能使用中状态"},
                        {scope = "ArtsDeBuff", label = "减益效果"},
                        {scope = "ReleaseLv", label = "习得等级"},
                        {scope = "NeedWP", label = "升级WP"},
                        {scope = "HitText", label = "技能判定"}
                      },
                      expandRowKey = "ID"
                    }
                  ) ..
                    "</div>"
    end
    out = out .. "</div>"
  else -- 黄金国角色
    for i = 0, 3 do
      out = out .. html.h4(IrArtsType[i].name)
      out = out .. '<div class="xbxbtabs arts">'
      for lv, perLevelData in ipairs(WpnArts[WpnId][IrArtsType[i].label]) do
        local tabslabel = "Lv" .. lv
        local tabsname = WpnId .. tabslabel
        out =
          out ..
          '<div data-name="' ..
            tabsname ..
              '" data-label="' ..
                tabslabel ..
                  '" >' ..
                    html.wikitable(
                      {
                        {
                          scope = "Name",
                          label = "名称"
                        },
                        {
                          scope = "ArtsType",
                          label = "类型"
                        },
                        {
                          scope = "RangeTypeText",
                          label = "范围"
                        },
                        {
                          scope = "Distance",
                          label = "目标距离"
                        },
                        {
                          scope = "DmgMgn",
                          label = "倍率"
                        },
                        {
                          scope = "Recast",
                          label = "填充量"
                        },
                        {
                          scope = "Hate",
                          label = "仇恨"
                        },
                        {
                          scope = "ReActText",
                          label = "反应动作"
                        },
                        {
                          scope = "Enhance",
                          label = "特效"
                        }
                      },
                      perLevelData,
                      {
                        width = "100%",
                        expandRow = {
                          {scope = "Radius", label = "判定半径"},
                          {scope = "LengthWidth", label = "判定范围"},
                          {scope = "HitRevise", label = "命中修正"},
                          {scope = "CriRevise", label = "暴击修正"},
                          {scope = "ArtsBuff", label = "技能使用中状态"},
                          {scope = "ArtsDeBuff", label = "减益效果"},
                          {scope = "ReleaseLv", label = "习得等级"},
                          {scope = "NeedWP", label = "升级WP"},
                          {scope = "HitText", label = "技能判定"}
                        },
                        expandRowKey = "ID"
                      }
                    ) ..
                      "</div>"
      end
      out = out .. "</div>"
    end
  end

  return out
end

function p.main(frame)
  local currentTitle = mw.title.getCurrentTitle()
  local ira = false
  if "黄金之国" == mw.ustring.sub(tostring(currentTitle), 1, 4) then
    ira = true
  end

  local data = p.getDataByDriverId(frame.args[1])

  local out = ""

  -- flex
  out = out .. '<div style="display:flex;flex-wrap:wrap-reverse;justify-content:space-between;">'

  out = out .. "<div>"
  -- 能力表
  out =
    out ..
    mw.ustring.format(
      [[
    <table class="wikitable">
        <tr>
            <th>能力值</th>
            <th>Lv1</th>
            <th>Lv99</th>
        </tr>
        <tr>
            <td>HP</td>
            <td>%s</td>
            <td>%s</td>
        </tr>
        <tr>
            <td>力量</td>
            <td>%s</td>
            <td>%s</td>
        </tr>
        <tr>
            <td>以太力</td>
            <td>%s</td>
            <td>%s</td>
        </tr>
        <tr>
            <td>灵巧</td>
            <td>%s</td>
            <td>%s</td>
        </tr>
        <tr>
            <td>敏捷</td>
            <td>%s</td>
            <td>%s</td>
        </tr>
        <tr>
            <td>运气</td>
            <td>%s</td>
            <td>%s</td>
        </tr>
    </table>
  ]],
      data.HpMaxLv1,
      data.HpMaxLv99,
      data.StrengthLv1,
      data.StrengthLv99,
      data.PowEtherLv1,
      data.PowEtherLv99,
      data.DexLv1,
      data.DexLv99,
      data.AgilityLv1,
      data.AgilityLv99,
      data.LuckLv1,
      data.LuckLv99
    )

  -- 收纳包
  out =
    out ..
    mw.ustring.format(
      [[
      <table class="wikitable">
          <tr>
              <th colspan="3">喜好的收纳包道具</th>
          </tr>
          <tr>
              <td>喜欢的分类</td>
              <td>%s</td>
              <td>%s</td>
          </tr>
          <tr>
              <td>喜欢的道具</td>
              <td>%s</td>
              <td>%s</td>
          </tr>
      </table>
    ]],
      data.FavoriteCategory1Text,
      data.FavoriteCategory2Text,
      "[[" .. (ira and "黄金之国物品:" or "物品:") .. data.FavoriteItem1Text .. "|" .. data.FavoriteItem1Text .. "]]",
      "[[" .. (ira and "黄金之国物品:" or "物品:") .. data.FavoriteItem2Text .. "|" .. data.FavoriteItem2Text .. "]]"
    )
  out = out .. "</div>"

  -- 立绘
  local images = {}
  for i, v in ipairs(frame.args) do
    images[i] = v
  end
  if #images > 1 then
    if #images > 2 then
      out = out .. '<div class="xbxbtabs" style="display: none;">'
      for i = 1, #images / 2 do
        local tabslabel = images[i * 2]
        local filename = images[i * 2 + 1]
        out =
          out ..
          '<div data-label="' ..
            tabslabel .. '" data-name="' .. filename .. '">[[文件:' .. filename .. "|none|x400px]]</div>"
      end
      out = out .. "</div>"
    else
      out = out .. "[[文件:" .. images[2] .. "|none|x400px]]"
    end
  end

  -- 闭合flex
  out = out .. "</div>"

  if data.Skill then
    out = out .. html.h2("牵绊圆环")

    local OvertAffinity, HiddenAffinity = {}, {}

    for _, skillrow in ipairs(data.Skill) do
      skillrow.EnhanceText = skillrow.Enhance.RenderedCaption
      if skillrow.Round == 1 then
        table.insert(OvertAffinity, skillrow)
      elseif skillrow.Round == 2 then
        table.insert(HiddenAffinity, skillrow)
      end
    end

    out = out .. html.h3("表象牵绊")
    out =
      out ..
      html.wikitable(
        {
          {
            scope = "NameText",
            label = "名称"
          },
          {
            scope = "EnhanceText",
            label = "效果"
          },
          {
            scope = "NeedSp",
            label = "升级所需SP"
          }
        },
        OvertAffinity
      )
    out = out .. html.h3("内心牵绊")
    out =
      out ..
      html.wikitable(
        {
          {
            scope = "NameText",
            label = "名称"
          },
          {
            scope = "EnhanceText",
            label = "效果"
          },
          {
            scope = "NeedSp",
            label = "升级所需SP"
          }
        },
        HiddenAffinity
      )
  end

  local WpnArts = p.formatArtsData(data.Arts)

  -- mw.log(mw.text.jsonEncode(WpnArts, mw.text.JSON_PRETTY))

  out = out .. html.h2("武技")

  for _, Wpn in ipairs(data.WpnType) do
    local WpnId = Wpn.row_id
    out = out .. html.h3("[[" .. Wpn.Name .. "]]")

    out = out .. p.outputArtsSection(WpnArts, WpnId)
  end

  return out
end

return p