Module:Skills/Summoning: Difference between revisions

From Melvor Idle
(Substitute links with Icons.Icon() where possible to benefit from ambiguous link handling)
(Fix max hit handling due to change in source data)
(33 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local p = {}
local p = {}
local ItemData = mw.loadData('Module:Items/data')
local SkillData = mw.loadData('Module:Skills/data')


local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local Common = require('Module:Common')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Modifiers = require('Module:Modifiers')
local Skills = require('Module:Skills')
local Items = require('Module:Items')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
 
local Shop = require('Module:Shop')
function p.getFamiliars()
local Num = require('Module:Number')
  return Items.getItems(function(item) return item.type == "Familiar" end)
end


function p.getMarkTable(frame)
function p.getMarkTable(frame)
  local result = ''
local args = frame.args ~= nil and frame.args or frame
  result = result..'{| class="wikitable sortable stickyHeader"'
local realmName = args.realm
  result = result..'\r\n|- class="headerRow-0"'
local realm = Skills.getRealmFromName(realmName)
  result = result..'\r\n!colspan="2"|Mark!!'..Icons.Icon({'Summoning', type='skill', notext=true})..' Level'
if realm == nil then
  result = result..'!!Discovered in'
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local skillID = 'Summoning'


  local Familiars = p.getFamiliars()
local html = mw.html.create('table')
  table.sort(Familiars, function(a, b)  
:addClass('wikitable sortable stickyHeader col-1-img col-5-img')
                          if a.summoningLevel == b.summoningLevel then
                            return a.summoningID < b.summoningID
html:tag('tr'):addClass('headerRow-0')
                          else
:tag('th'):wikitext('Mark')
                            return a.summoningLevel < b.summoningLevel
  :attr('colspan', 2)
                          end
:tag('th'):wikitext('DLC')  
                        end)
:tag('th'):wikitext(Icons._SkillRealmIcon(skillID, realm.id) .. '<br>Level')
:tag('th'):wikitext('Tier')
:tag('th'):wikitext('Discovered in')


  local rowArray = {}
local Familiars = GameData.getEntities(SkillData.Summoning.recipes,
function(obj)
return Skills.getRecipeRealm(obj) == realm.id
end
)
table.sort(Familiars, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)


  for i, Fam in Shared.skpairs(Familiars) do
local rowArray = {}
    local rowText = '|-'
for i, Fam in ipairs(Familiars) do
    rowText = rowText..'\r\n|data-sort-value="'..Fam.name..'"|'..Icons.Icon({Fam.name, type='mark', notext=true, size='50'})
local level = Skills.getRecipeLevel(skillID, Fam)
    rowText = rowText..'||'..Icons.Icon({Fam.name, 'Mark of the ' .. Fam.name, type='mark', noicon=true})
local reqText = Skills.getRecipeRequirementText(SkillData.Summoning.name, Fam)
    rowText = rowText..'||style="text-align:right"|'..Fam.summoningLevel
local item = Items.getItemByID(Fam.productID)
    local discoveredArray = {}
if item ~= nil then
    for j, SkillID in Shared.skpairs(Fam.summoningSkills) do
local row = html:tag('tr')
      table.insert(discoveredArray, Icons.Icon({Constants.getSkillName(SkillID), type='skill'}))
row:tag('td'):wikitext(Icons.Icon({item.name, type='mark', notext=true}))
    end
:attr('data-sort-value', item.name)
    rowText = rowText..'||'..table.concat(discoveredArray, '<br/>')
row:tag('td'):wikitext(Icons.Icon({item.name, 'Mark of the ' .. item.name, type='mark', noicon=true}))
    table.insert(rowArray, rowText)
row:tag('td'):wikitext(Icons.getDLCColumnIcon(Fam.id))
  end
:attr('data-sort-value', Icons.getExpansionID(Fam.id))
:css('text-align', 'center')
row:tag('td'):wikitext(level)
:css('text-align', 'center')
row:tag('td'):wikitext(Fam.tier)
:css('text-align', 'center')


  result = result..'\r\n'..table.concat(rowArray, '\r\n')
local discoveredArray = {}
 
for j, SkillID in ipairs(Fam.skillIDs) do
  result = result..'\r\n|}'
table.insert(discoveredArray, Icons.Icon({Constants.getSkillName(SkillID), type='skill'}))
  return result
end
row:tag('td'):wikitext(table.concat(discoveredArray, '<br/>'))
end
end
return tostring(html)
end
end


function p.getTabletTable(frame)
function p.getTabletTable(frame)
  local result = ''
local args = frame.args ~= nil and frame.args or frame
  result = result..'{| class="wikitable sortable stickyHeader"'
local realmName = args.realm
  result = result..'\r\n|- class="headerRow-0"'
local category = args.category
  result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Summoning', type='skill', notext=true})..' Level'
local realm = Skills.getRealmFromName(realmName)
  result = result..'!!Tier!!Effect!!' .. Icons.Icon({'Melee', notext=true, nolink=true}) .. ' Max Hit!!Description!!Shard Cost!!Secondary!!Creation XP'
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local skillID = 'Summoning'
local html = mw.html.create('table')
:addClass('wikitable sortable stickyHeader col-1-img col-3-img')
local header = html:tag('tr'):addClass('headerRow-0')
header:tag('th'):wikitext('Name')
            :attr('colspan', 2)
header:tag('th'):wikitext('DLC')
header:tag('th'):wikitext(Icons._SkillRealmIcon(skillID, realm.id) .. '<br>Level')
header:tag('th'):wikitext('Tier')
header:tag('th'):wikitext('Creation<br>XP')
header:tag('th'):wikitext('Shards')
header:tag('th'):wikitext('Cost of shards')
if category == 'Combat' then
header:tag('th'):wikitext(Icons.Icon({'Melee', notext=true, nolink=true}) .. '<br>Max Hit')
end
local function getShardCosts(familiar)
local shardCosts = {}
for _, shard in ipairs(familiar.itemCosts) do
local shopItem = Shop.getPurchaseByID(shard.id)
for _, shardCost in ipairs(shopItem.cost.currencies) do
local addCost = shardCost.cost * shard.quantity
if shardCosts[shardCost.currency] ~= nil then
shardCosts[shardCost.currency] = shardCosts[shardCost.currency] + addCost
else
shardCosts[shardCost.currency] = addCost
end
end
end
    return shardCosts
end


  local Familiars = p.getFamiliars()
local function isCombatFamiliar(fam)
  table.sort(Familiars, function(a, b)  
local item = Items.getItemByID(fam.productID)
                          if a.summoningLevel == b.summoningLevel then
local maxHit = Items._getItemStat(item, 'summoningMaxhitAbyssal') or Items._getItemStat(item, 'summoningMaxhit')
                            return a.summoningID < b.summoningID
return maxHit ~= nil
                          else
end
                            return a.summoningLevel < b.summoningLevel
                          end
                        end)


  local rowArray = {}
local Familiars = GameData.getEntities(SkillData.Summoning.recipes,
function(recipe)
    return Skills.getRecipeRealm(recipe) == realm.id and
          ((category == 'Combat' and isCombatFamiliar(recipe)) or
          (category ~= 'Combat' and not isCombatFamiliar(recipe)))
end
)
table.sort(Familiars, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)


  for i, Fam in Shared.skpairs(Familiars) do
local rowArray = {}
    local maxHit, maxHitText = Items._getItemStat(Fam, 'summoningMaxhit'), ''
for i, Fam in ipairs(Familiars) do
    if maxHit ~= nil then
local level, isAbyssal = Skills.getRecipeLevelRealm(skillID, Fam)
      maxHit = maxHit * 10
local baseXP = Fam.baseAbyssalExperience or Fam.baseExperience
      maxHitText = 'style="text-align:right"; data-sort-value="' .. maxHit .. '"|' .. Shared.formatnum(maxHit)
local reqText = Skills.getRecipeRequirementText(SkillData.Summoning.name, Fam)
    end
local item = Items.getItemByID(Fam.productID)
    local rowText = '|-'
if item ~= nil then
    rowText = rowText..'\r\n|data-sort-value="'..Fam.name..'"|'..Icons.Icon({Fam.name, type='item', notext=true, size='50'})
local effectDesc = Modifiers.getModifiersText(item.modifiers, false, false, 10)
    rowText = rowText..'||'..Icons.Icon({Fam.name, type='item', noicon=true})
    rowText = rowText..'||style="text-align:right"|'..Fam.summoningLevel
local row = html:tag('tr')
    rowText = rowText..'||style="text-align:right"|'..Fam.summoningTier
row:tag('td'):wikitext(Icons.Icon({item.name, type='item', notext=true}))
    rowText = rowText..'||'..Fam.description..'||'..maxHitText..'||'..Fam.summoningDescription
:attr('data-sort-value', item.name)
row:tag('td'):wikitext('[[' .. item.name .. ']]')
row:tag('td'):wikitext(Icons.getDLCColumnIcon(Fam.id))
:attr('data-sort-value', Icons.getExpansionID(Fam.id))
row:tag('td'):wikitext(level)
:css('text-align', 'center')
row:tag('td'):wikitext(Fam.tier)
:css('text-align', 'center')
row:tag('td'):wikitext(Num.formatnum(baseXP))
:css('text-align', 'right')
:attr('data-sort-value', baseXP)
local shardCell = row:tag('td')
local shardCostCell = row:tag('td')
if category == 'Combat' then
local maxHit = (Items._getItemStat(item, 'summoningMaxhitAbyssal') or Items._getItemStat(item, 'summoningMaxhit')) * 10
row:tag('td'):wikitext(Num.formatnum(maxHit))
:css('text-align', 'right')
:attr('data-sort-value', maxHit)
end


    --Currently assuming the shard cost is the same for all recipe variants
-- Shards required
    local ShardCostArray = {}
for _, cost in ipairs(Fam.itemCosts) do
    for j, cost in Shared.skpairs(Fam.summoningReq[1]) do
local shard = Items.getItemByID(cost.id)
      if cost.id >= 0 then
if shard ~= nil then
        local item = Items.getItemByID(cost.id)
local sub = mw.html.create('sub')
        if item.type == 'Shard' then
:wikitext(cost.quantity .. 'x'):addClass('item-qty'):done()
          table.insert(ShardCostArray, Icons.Icon({item.name, type='item', notext=true, qty=cost.qty}))
shardCell:node(sub)
        end
shardCell:wikitext(Icons.Icon({shard.name, type='item', notext=true}))
      end
end
    end
end
    rowText = rowText..'||style="text-align:right"|'..table.concat(ShardCostArray, ', ')..'&nbsp;'
local costTbl = {}
for costType, cost in pairs(getShardCosts(Fam)) do
table.insert(costTbl, Icons._Currency(costType, cost))
end
shardCostCell:wikitext(table.concat(costTbl, '<br>'))
:css('text-align', 'right')
end
end


    --Now to get all the other cost options
return tostring(html)
    local OtherCostArray = {}
end
    local recipeGPCost = SkillData.Summoning.Settings.recipeGPCost
    for j, altCost in Shared.skpairs(Fam.summoningReq) do
      local nonShardArray = {}
      for k, cost in Shared.skpairs(altCost) do
        if cost.id >= 0 then
          local item = Items.getItemByID(cost.id)
          if item.type ~= 'Shard' then
            local sellPrice = item.sellsFor
            if sellPrice < 20 then sellPrice = 20 end
            table.insert(nonShardArray, Icons.Icon({item.name, type='item', notext=true, qty=math.max(1, math.floor(recipeGPCost / sellPrice))}))
          end
        else
          if cost.id == -4 then
            table.insert(nonShardArray, Icons.GP(recipeGPCost))
          elseif cost.id == -5 then
            table.insert(nonShardArray, Icons.SC(recipeGPCost))
          end
        end
      end
      table.insert(OtherCostArray, table.concat(nonShardArray, ', '))
    end
    rowText = rowText..'||style="text-align:right"|'..table.concat(OtherCostArray, "<br/>'''OR''' ")


    rowText = rowText..'||style="text-align:right"|'..(5 + 2 * math.floor(Fam.summoningLevel / 5))
function p._getSynergyTable(familiarIDs)
local skillID = 'Summoning'
local result = ''
result = result..'{| class="wikitable sortable stickyHeader col-1-img col-3-img"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Familiar 1!!colspan="2"|Familiar 2!!Effect!!Requirements'


    table.insert(rowArray, rowText)
local recipesByID, famNames = {}, {}
  end
for i, recipe in ipairs(SkillData.Summoning.recipes) do
recipesByID[recipe.id] = recipe
local item = Items.getItemByID(recipe.productID)
if item ~= nil then
famNames[recipe.id] = item.name
end
end


  result = result..'\r\n'..table.concat(rowArray, '\r\n')
local synergyList = GameData.getEntities(SkillData.Summoning.synergies,
function(synergy)
for i, summonID in ipairs(synergy.summonIDs) do
if Shared.contains(familiarIDs, summonID) then
return true
end
end
return false
end)
table.sort(synergyList,
function (a, b)
local recA1, recB1 = recipesByID[a.summonIDs[1]], recipesByID[b.summonIDs[1]]


  result = result..'\r\n|}'
if ((recA1.abyssalLevel or 0) == (recB1.abyssalLevel or 0)) and (recA1.level == recB1.level) then
  return result
return (
end
(a.summonIDs[1] == b.summonIDs[1] and a.summonIDs[2] < b.summonIDs[2])
 
or a.summonIDs[1] < b.summonIDs[1]
function p.getSynergyTable(frame)
)
  local result = ''
else
  result = result..'{| class="wikitable sortable stickyHeader"'
return Skills.standardRecipeSort(skillID, recA1, recB1)
  result = result..'\r\n|- class="headerRow-0"'
end
  result = result..'\r\n!colspan="2"|Familiar 1!!colspan="2"|Familiar 2!!Effect!!Requirements'
end
)


  local Familiars = p.getFamiliars()
local rowArray = {}
  local FamArrayByID = {}
for i, syn in ipairs(synergyList) do
  for i, Fam in Shared.skpairs(Familiars) do
local Fam1 = recipesByID[syn.summonIDs[1]]
    FamArrayByID[Fam.summoningID + 1] = Fam
local Fam2 = recipesByID[syn.summonIDs[2]]
  end
if Fam1 ~= nil and Fam2 ~= nil then
local FamName1 = famNames[Fam1.id] or 'Unknown'
local FamName2 = famNames[Fam2.id] or 'Unknown'
local synDesc = syn.customDescription
if synDesc == nil then
-- Generate description from modifiers
synDesc = Modifiers.getModifiersText(syn.modifiers, false, false, 10) or ''
end
local rowText = '|-'
rowText = rowText..'\r\n|data-sort-value="'..FamName1..'"|'..Icons.Icon({FamName1, type='item', notext=true})
rowText = rowText..'||' .. Icons.getExpansionIcon(Fam1.id) .. Icons.Icon({FamName1, type='item', noicon=true})
rowText = rowText..'||data-sort-value="'..FamName2..'"|'..Icons.Icon({FamName2, type='item', notext=true})
rowText = rowText..'||' .. Icons.getExpansionIcon(Fam2.id) .. Icons.Icon({FamName2, type='item', noicon=true})
rowText = rowText..'||'..synDesc


  local rowArray = {}
local reqArray = {}
local reqFam = (Skills.getRecipeLevel(skillID, Fam1) > Skills.getRecipeLevel(skillID, Fam2) and Fam1) or Fam2
local reqLvl = Skills.getRecipeLevel(skillID, reqFam)
table.insert(reqArray, Skills.getRecipeRequirementText(skillID, reqFam))
table.insert(reqArray, FamName1..' Mark Level '..(Fam2.tier + 1))
table.insert(reqArray, FamName2..' Mark Level '..(Fam1.tier + 1))


  for i, SynCat in Shared.skpairs(SkillData.Summoning.Synergies) do
rowText = rowText..'||data-sort-value="'..reqLvl..'"|'..table.concat(reqArray, '<br/>')
    local Fam1 = FamArrayByID[i]
    for j, Syn in Shared.skpairs(SynCat) do
      local Fam2 = FamArrayByID[j]
      local rowText = '|-'
      rowText = rowText..'\r\n|data-sort-value="'..Fam1.name..'"|'..Icons.Icon({Fam1.name, type='item', notext=true, size='30'})
      rowText = rowText..'||'..Icons.Icon({Fam1.name, type='item', noicon=true})
      rowText = rowText..'||data-sort-value="'..Fam2.name..'"|'..Icons.Icon({Fam2.name, type='item', notext=true, size='30'})
      rowText = rowText..'||'..Icons.Icon({Fam2.name, type='item', noicon=true})
      rowText = rowText..'||'..Syn.description


      local reqArray = {}
table.insert(rowArray, rowText)
      local reqLvl = math.max(Fam1.summoningLevel, Fam2.summoningLevel)
end
      table.insert(reqArray, Icons._SkillReq('Summoning', reqLvl))
end
      table.insert(reqArray, Fam1.name..' Mark Level '..(Fam2.summoningTier + 1))
      table.insert(reqArray, Fam2.name..' Mark Level '..(Fam1.summoningTier + 1))


      rowText = rowText..'||data-sort-value="'..reqLvl..'"|'..table.concat(reqArray, '<br/>')
result = result..'\r\n'..table.concat(rowArray, '\r\n')
      table.insert(rowArray, rowText)
    end
  end


  result = result..'\r\n'..table.concat(rowArray, '\r\n')
result = result..'\r\n|}'
 
return result
  result = result..'\r\n|}'
  return result
end
end


function p._getFamiliarSynergyTable(familiar)
function p.getSynergyTable(frame)
  local result = ''
local args = frame.args ~= nil and frame.args or frame
  result = result..'{| class="wikitable sortable stickyHeader"'
local realmName = args.realm
  result = result..'\r\n|- class="headerRow-0"'
local realm = Skills.getRealmFromName(realmName)
  result = result..'\r\n!colspan="2"|Familiar 1!!colspan="2"|Familiar 2!!Effect!!Requirements'
if realm == nil then
 
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
  local Familiars = p.getFamiliars()
end
  local FamArrayByID = {}
  for i, Fam in Shared.skpairs(Familiars) do
    FamArrayByID[Fam.summoningID + 1] = Fam
  end
 
  local rowArray = {}
 
  for i, SynCat in Shared.skpairs(SkillData.Summoning.Synergies) do
    local Fam1 = FamArrayByID[i]
    for j, Syn in Shared.skpairs(SynCat) do
      local Fam2 = FamArrayByID[j]
      if i - 1 == familiar.summoningID or j - 1 == familiar.summoningID then
        local rowText = '|-'
        rowText = rowText..'\r\n|data-sort-value="'..Fam1.name..'"|'..Icons.Icon({Fam1.name, type='item', notext=true, size='30'})
        rowText = rowText..'||'..Icons.Icon({Fam1.name, type='item', noicon=true})
        rowText = rowText..'||data-sort-value="'..Fam2.name..'"|'..Icons.Icon({Fam2.name, type='item', notext=true, size='30'})
        rowText = rowText..'||'..Icons.Icon({Fam2.name, type='item', noicon=true})
        rowText = rowText..'||'..Syn.description
 
        local reqArray = {}
        local reqLvl = math.max(Fam1.summoningLevel, Fam2.summoningLevel)
        table.insert(reqArray, Icons._SkillReq('Summoning', reqLvl))
        table.insert(reqArray, Fam1.name..' Mark Level '..(Fam2.summoningTier + 1))
        table.insert(reqArray, Fam2.name..' Mark Level '..(Fam1.summoningTier + 1))
 
        rowText = rowText..'||data-sort-value="'..reqLvl..'"|'..table.concat(reqArray, '<br/>')
        table.insert(rowArray, rowText)
      end
    end
  end


  result = result..'\r\n'..table.concat(rowArray, '\r\n')
local familiarIDs = {}
for i, recipe in ipairs(SkillData.Summoning.recipes) do
if Skills.getRecipeRealm(recipe) == realm.id then
table.insert(familiarIDs, recipe.id)
end
end


  result = result..'\r\n|}'
return p._getSynergyTable(familiarIDs)
  return result
end
end


function p.getFamiliarSynergyTable(frame)
function p.getFamiliarSynergyTable(frame)
  local famName = frame.args ~= nil and frame.args[1] or frame
local famName = frame.args ~= nil and frame.args[1] or frame
  local familiar = Items.getItem(famName)
local familiarID = nil
  if familiar == nil or familiar.type ~= 'Familiar' then
local familiarItem = Items.getItem(famName)
    return 'ERROR: Not a valid familiar[[Category:Pages with script errors]]'
if familiarItem == nil then
  end
return Shared.printError('Not a valid familiar')
  return p._getFamiliarSynergyTable(familiar)
else
for i, recipe in ipairs(SkillData.Summoning.recipes) do
if recipe.productID == familiarItem.id then
familiarID = recipe.id
break
end
end
if familiarID == nil then
return Shared.printError('Not a valid familiar')
else
return p._getSynergyTable({ familiarID })
end
end
end
end


function p._getSkillSummoningBonusTable(skill)
function p._getSkillSummoningBonusTable(skill)
  local rowArray = {}
local rowArray = {}
local famNames = {}
 
-- Familiars
for i, recipe in ipairs(SkillData.Summoning.recipes) do
local item = Items.getItemByID(recipe.productID)
if item ~= nil then
famNames[recipe.id] = item.name
if item.modifiers ~= nil and not Shared.tableIsEmpty(item.modifiers) then
local famSkills = Modifiers.getModifierSkills(item.modifiers)
if Shared.contains(famSkills, skill) then
table.insert(rowArray, {Fam1 = item.name, FamID1 = item.id, Fam2 = nil, FamID2 = nil, Descrip = Modifiers.getModifiersText(item.modifiers, false)})
end
end
end
end


  local Familiars = p.getFamiliars()
-- Synergies
  local FamArrayByID = {}
for i, syn in ipairs(SkillData.Summoning.synergies) do
  for i, Fam in Shared.skpairs(Familiars) do
local synSkills = Modifiers.getModifierSkills(syn.modifiers)
    FamArrayByID[Fam.summoningID + 1] = Fam
if Shared.contains(synSkills, skill) then
    local famSkills = Constants.getModifierSkills(Fam.modifiers)
local FamName1 = famNames[syn.summonIDs[1]] or 'Unknown'
    for k, skillName in Shared.skpairs(famSkills) do
local FamName2 = famNames[syn.summonIDs[2]] or 'Unknown'
      if skillName == skill then
local synDesc = syn.customDescription
        table.insert(rowArray, {Fam1 = Fam.name, Fam2 = nil, Descrip = Constants.getModifiersText(Fam.modifiers, false)})
if synDesc == nil then
      end
-- Generate description from modifiers
    end
synDesc = Modifiers.getModifiersText(syn.modifiers, false) or ''
  end
end
table.insert(rowArray, {Fam1 = FamName1, FamID1 = syn.summonIDs[1], Fam2 = FamName2, FamID2 = syn.summonIDs[2], Descrip = synDesc})
end
end


  for i, SynCat in Shared.skpairs(SkillData.Summoning.Synergies) do
if Shared.tableIsEmpty(rowArray) then
    local Fam1 = FamArrayByID[i]
return ''
end


    for j, Syn in Shared.skpairs(SynCat) do
local html = mw.html.create('table')
      local Fam2 = FamArrayByID[j]
:addClass('wikitable sortable stickyHeader col-1-img col-3-img')
      local skills = Constants.getModifierSkills(Syn.modifiers)
      for k, skillName in Shared.skpairs(skills) do
html:tag('tr'):addClass('headerRow-0')
        if skillName == skill then
:tag('th'):wikitext('Familiar 1')
          table.insert(rowArray, {Fam1 = Fam1.name, Fam2 = Fam2.name, Descrip = Syn.description})
  :attr('colspan', 2)
        end
:tag('th'):wikitext('Familiar 2')
      end
  :attr('colspan', 2)
    end
:tag('th'):wikitext('DLC')
  end
:tag('th'):wikitext('Effect')


for i, rowItem in ipairs(rowArray) do
local DLCIcon = Icons.getExpansionIcon(rowItem.FamID1)
local row = html:tag('tr')
row:tag('td'):wikitext(Icons.Icon({rowItem.Fam1, type='item', notext=true}))
:attr('data-sort-value', rowItem.Fam1)
row:tag('td'):wikitext(Icons.Icon({rowItem.Fam1, type='item', noicon=true}))
if rowItem.Fam2 ~= nil then
-- If Fam1 has no DLC, try setting it for Fam2
if DLCIcon == nil or DLCIcon == '' then
DLCIcon = Icons.getExpansionIcon(rowItem.FamID2)
end
row:tag('td'):wikitext(Icons.Icon({rowItem.Fam2, type='item', notext=true}))
:attr('data-sort-value', rowItem.Fam2)
row:tag('td'):wikitext(Icons.Icon({rowItem.Fam2, type='item', noicon=true}))
else
row:tag('td'):tag('td')
end


  local result = ''
-- If both familiars have no DLC, default to melvor icon.
  result = result..'{| class="wikitable sortable stickyHeader"'
if DLCIcon == nil or DLCIcon == '' then
  result = result..'\r\n|- class="headerRow-0"'
DLCIcon = Icons.Melvor()
  result = result..'\r\n!colspan="2"|Familiar 1!!colspan="2"|Familiar 2!!Effect'
end
-- As of 06/07/2024, no synergies exist that use two DLCs
row:tag('td'):wikitext(DLCIcon)
:css('text-align', 'center')


  for i, rowItem in Shared.skpairs(rowArray) do
row:tag('td'):wikitext(rowItem.Descrip or ' ')
    result = result..'\r\n|-'
end
    result = result..'\r\n|data-sort-value="'..rowItem.Fam1..'"|'..Icons.Icon({rowItem.Fam1, type='item', notext=true, size='30'})
    result = result..'||'..Icons.Icon({rowItem.Fam1, type='item', noicon=true})
return tostring(html)
    if rowItem.Fam2 ~= nil then
      result = result..'||data-sort-value="'..rowItem.Fam2..'"|'..Icons.Icon({rowItem.Fam2, type='item', notext=true, size='30'})
      result = result..'||'..Icons.Icon({rowItem.Fam2, type='item', noicon=true})
    else
      result = result..'|| || '
    end
    if rowItem.Descrip == nil then
      if rowItem.Fam2 == nil then
        mw.log(rowItem.Fam1)
      else
        mw.log(rowItem.Fam1..' + '..rowItem.Fam2)
      end
    else
      result = result..'||'..rowItem.Descrip
    end
  end


  result = result..'\r\n|}'
  return result
end
end


function p.getSkillSummoningBonusTable(frame)
function p.getSkillSummoningBonusTable(frame)
  local skillName = frame.args ~= nil and frame.args[1] or frame
local skillName = frame.args ~= nil and frame.args[1] or frame
 
return p._getSkillSummoningBonusTable(skillName)
  return p._getSkillSummoningBonusTable(skillName)
end
end


return p
return p

Revision as of 15:02, 16 July 2024

Documentation for this module may be created at Module:Skills/Summoning/doc

local p = {}

local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Common = require('Module:Common')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Modifiers = require('Module:Modifiers')
local Skills = require('Module:Skills')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Shop = require('Module:Shop')
local Num = require('Module:Number')

function p.getMarkTable(frame)
	local args = frame.args ~= nil and frame.args or frame
	local realmName = args.realm
	local realm = Skills.getRealmFromName(realmName)
	if realm == nil then
		return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
	end
	local skillID = 'Summoning'

	local html = mw.html.create('table')
		:addClass('wikitable sortable stickyHeader col-1-img col-5-img')
		
	html:tag('tr'):addClass('headerRow-0')
			:tag('th'):wikitext('Mark')
					  :attr('colspan', 2)
			:tag('th'):wikitext('DLC')					  
			:tag('th'):wikitext(Icons._SkillRealmIcon(skillID, realm.id) .. '<br>Level')
			:tag('th'):wikitext('Tier')
			:tag('th'):wikitext('Discovered in')

	local Familiars = GameData.getEntities(SkillData.Summoning.recipes,
		function(obj)
			return Skills.getRecipeRealm(obj) == realm.id
		end
	)
	table.sort(Familiars, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)

	local rowArray = {}
	for i, Fam in ipairs(Familiars) do
		local level = Skills.getRecipeLevel(skillID, Fam)
		local reqText = Skills.getRecipeRequirementText(SkillData.Summoning.name, Fam)
		local item = Items.getItemByID(Fam.productID)
		if item ~= nil then
			local row = html:tag('tr')
			row:tag('td'):wikitext(Icons.Icon({item.name, type='mark', notext=true}))
						 :attr('data-sort-value', item.name)
			row:tag('td'):wikitext(Icons.Icon({item.name, 'Mark of the ' .. item.name, type='mark', noicon=true}))
			row:tag('td'):wikitext(Icons.getDLCColumnIcon(Fam.id))
						 :attr('data-sort-value', Icons.getExpansionID(Fam.id))		
						 :css('text-align', 'center')
			row:tag('td'):wikitext(level)
						 :css('text-align', 'center')	
			row:tag('td'):wikitext(Fam.tier)
						 :css('text-align', 'center')

			local discoveredArray = {}
			for j, SkillID in ipairs(Fam.skillIDs) do
				table.insert(discoveredArray, Icons.Icon({Constants.getSkillName(SkillID), type='skill'}))
			end
			
			row:tag('td'):wikitext(table.concat(discoveredArray, '<br/>'))
		end
	end
	
	return tostring(html)
end

function p.getTabletTable(frame)
	local args = frame.args ~= nil and frame.args or frame
	local realmName = args.realm
	local category = args.category
	local realm = Skills.getRealmFromName(realmName)
	if realm == nil then
		return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
	end
	local skillID = 'Summoning'
	
	local html = mw.html.create('table')
		:addClass('wikitable sortable stickyHeader col-1-img col-3-img')
	
	local header = html:tag('tr'):addClass('headerRow-0')
	header:tag('th'):wikitext('Name')
		            :attr('colspan', 2)
	header:tag('th'):wikitext('DLC')
	header:tag('th'):wikitext(Icons._SkillRealmIcon(skillID, realm.id) .. '<br>Level')
	header:tag('th'):wikitext('Tier')
	header:tag('th'):wikitext('Creation<br>XP')
	header:tag('th'):wikitext('Shards')
	header:tag('th'):wikitext('Cost of shards')
	
	if category == 'Combat' then
		header:tag('th'):wikitext(Icons.Icon({'Melee', notext=true, nolink=true}) .. '<br>Max Hit')
	end
	
	local function getShardCosts(familiar)
		local shardCosts = {}
		for _, shard in ipairs(familiar.itemCosts) do
			local shopItem = Shop.getPurchaseByID(shard.id)
			for _, shardCost in ipairs(shopItem.cost.currencies) do
				local addCost = shardCost.cost * shard.quantity
				if shardCosts[shardCost.currency] ~= nil then
					shardCosts[shardCost.currency] = shardCosts[shardCost.currency] + addCost
				else
					shardCosts[shardCost.currency] = addCost
				end
			end
		end
    	return shardCosts
	end

	local function isCombatFamiliar(fam)
		local item = Items.getItemByID(fam.productID)
		local maxHit = Items._getItemStat(item, 'summoningMaxhitAbyssal') or Items._getItemStat(item, 'summoningMaxhit')
		return maxHit ~= nil
	end

	local Familiars = GameData.getEntities(SkillData.Summoning.recipes,
		function(recipe)
    		return Skills.getRecipeRealm(recipe) == realm.id and 
           ((category == 'Combat' and isCombatFamiliar(recipe)) or
           (category ~= 'Combat' and not isCombatFamiliar(recipe)))
		end
	)
	table.sort(Familiars, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)

	local rowArray = {}
	for i, Fam in ipairs(Familiars) do
		local level, isAbyssal = Skills.getRecipeLevelRealm(skillID, Fam)
		local baseXP = Fam.baseAbyssalExperience or Fam.baseExperience
		local reqText = Skills.getRecipeRequirementText(SkillData.Summoning.name, Fam)
		local item = Items.getItemByID(Fam.productID)
		if item ~= nil then
			local effectDesc = Modifiers.getModifiersText(item.modifiers, false, false, 10)
			
			local row = html:tag('tr')
			row:tag('td'):wikitext(Icons.Icon({item.name, type='item', notext=true}))
						 :attr('data-sort-value', item.name)
			row:tag('td'):wikitext('[[' .. item.name .. ']]')
			row:tag('td'):wikitext(Icons.getDLCColumnIcon(Fam.id))
						 :attr('data-sort-value', Icons.getExpansionID(Fam.id))
			row:tag('td'):wikitext(level)
						 :css('text-align', 'center')
			row:tag('td'):wikitext(Fam.tier)
						 :css('text-align', 'center')
			row:tag('td'):wikitext(Num.formatnum(baseXP))
						 :css('text-align', 'right')
						 :attr('data-sort-value', baseXP)
			local shardCell = row:tag('td')
			local shardCostCell = row:tag('td')
			
			if category == 'Combat' then
				local maxHit = (Items._getItemStat(item, 'summoningMaxhitAbyssal') or Items._getItemStat(item, 'summoningMaxhit')) * 10
				row:tag('td'):wikitext(Num.formatnum(maxHit))
							 :css('text-align', 'right')
							 :attr('data-sort-value', maxHit)
			end

			-- Shards required
			for _, cost in ipairs(Fam.itemCosts) do
				local shard = Items.getItemByID(cost.id)
				if shard ~= nil then
					local sub = mw.html.create('sub')
						:wikitext(cost.quantity .. 'x'):addClass('item-qty'):done()
					shardCell:node(sub)
					shardCell:wikitext(Icons.Icon({shard.name, type='item', notext=true}))
				end
			end
			
			local costTbl = {}
			for costType, cost in pairs(getShardCosts(Fam)) do
				table.insert(costTbl, Icons._Currency(costType, cost))
			end
			shardCostCell:wikitext(table.concat(costTbl, '<br>'))
						 :css('text-align', 'right')
		end
	end

	return tostring(html)
end

function p._getSynergyTable(familiarIDs)
	local skillID = 'Summoning'
	local result = ''
	result = result..'{| class="wikitable sortable stickyHeader col-1-img col-3-img"'
	result = result..'\r\n|- class="headerRow-0"'
	result = result..'\r\n!colspan="2"|Familiar 1!!colspan="2"|Familiar 2!!Effect!!Requirements'

	local recipesByID, famNames = {}, {}
	for i, recipe in ipairs(SkillData.Summoning.recipes) do
		recipesByID[recipe.id] = recipe
		local item = Items.getItemByID(recipe.productID)
		if item ~= nil then
			famNames[recipe.id] = item.name
		end
	end

	local synergyList = GameData.getEntities(SkillData.Summoning.synergies,
		function(synergy)
			for i, summonID in ipairs(synergy.summonIDs) do
				if Shared.contains(familiarIDs, summonID) then
					return true
				end
			end
			return false
		end)
	table.sort(synergyList,
		function (a, b)
			local recA1, recB1 = recipesByID[a.summonIDs[1]], recipesByID[b.summonIDs[1]]

			if ((recA1.abyssalLevel or 0) == (recB1.abyssalLevel or 0)) and (recA1.level == recB1.level) then
				return (
					(a.summonIDs[1] == b.summonIDs[1] and a.summonIDs[2] < b.summonIDs[2])
					or a.summonIDs[1] < b.summonIDs[1]
				 )
			else
				return Skills.standardRecipeSort(skillID, recA1, recB1)
			end
		end
	)

	local rowArray = {}
	for i, syn in ipairs(synergyList) do
		local Fam1 = recipesByID[syn.summonIDs[1]]
		local Fam2 = recipesByID[syn.summonIDs[2]]
		if Fam1 ~= nil and Fam2 ~= nil then
			local FamName1 = famNames[Fam1.id] or 'Unknown'
			local FamName2 = famNames[Fam2.id] or 'Unknown'
			local synDesc = syn.customDescription
			if synDesc == nil then
				-- Generate description from modifiers
				synDesc = Modifiers.getModifiersText(syn.modifiers, false, false, 10) or ''
			end
			local rowText = '|-'
			rowText = rowText..'\r\n|data-sort-value="'..FamName1..'"|'..Icons.Icon({FamName1, type='item', notext=true})
			rowText = rowText..'||' .. Icons.getExpansionIcon(Fam1.id) .. Icons.Icon({FamName1, type='item', noicon=true})
			rowText = rowText..'||data-sort-value="'..FamName2..'"|'..Icons.Icon({FamName2, type='item', notext=true})
			rowText = rowText..'||' .. Icons.getExpansionIcon(Fam2.id) .. Icons.Icon({FamName2, type='item', noicon=true})
			rowText = rowText..'||'..synDesc

			local reqArray = {}
			local reqFam = (Skills.getRecipeLevel(skillID, Fam1) > Skills.getRecipeLevel(skillID, Fam2) and Fam1) or Fam2
			local reqLvl = Skills.getRecipeLevel(skillID, reqFam)
			table.insert(reqArray, Skills.getRecipeRequirementText(skillID, reqFam))
			table.insert(reqArray, FamName1..' Mark Level '..(Fam2.tier + 1))
			table.insert(reqArray, FamName2..' Mark Level '..(Fam1.tier + 1))

			rowText = rowText..'||data-sort-value="'..reqLvl..'"|'..table.concat(reqArray, '<br/>')

			table.insert(rowArray, rowText)
		end
	end

	result = result..'\r\n'..table.concat(rowArray, '\r\n')

	result = result..'\r\n|}'
	return result
end

function p.getSynergyTable(frame)
	local args = frame.args ~= nil and frame.args or frame
	local realmName = args.realm
	local realm = Skills.getRealmFromName(realmName)
	if realm == nil then
		return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
	end

	local familiarIDs = {}
	for i, recipe in ipairs(SkillData.Summoning.recipes) do
		if Skills.getRecipeRealm(recipe) == realm.id then
			table.insert(familiarIDs, recipe.id)
		end
	end

	return p._getSynergyTable(familiarIDs)
end

function p.getFamiliarSynergyTable(frame)
	local famName = frame.args ~= nil and frame.args[1] or frame
	local familiarID = nil
	local familiarItem = Items.getItem(famName)
	if familiarItem == nil then
		return Shared.printError('Not a valid familiar')
	else
		for i, recipe in ipairs(SkillData.Summoning.recipes) do
			if recipe.productID == familiarItem.id then
				familiarID = recipe.id
				break
			end
		end
		if familiarID == nil then
			return Shared.printError('Not a valid familiar')
		else
			return p._getSynergyTable({ familiarID })
		end
	end
end

function p._getSkillSummoningBonusTable(skill)
	local rowArray = {}
	local famNames = {}

	-- Familiars
	for i, recipe in ipairs(SkillData.Summoning.recipes) do
		local item = Items.getItemByID(recipe.productID)
		if item ~= nil then
			famNames[recipe.id] = item.name
			if item.modifiers ~= nil and not Shared.tableIsEmpty(item.modifiers) then
				local famSkills = Modifiers.getModifierSkills(item.modifiers)
				if Shared.contains(famSkills, skill) then
					table.insert(rowArray, {Fam1 = item.name, FamID1 = item.id, Fam2 = nil, FamID2 = nil, Descrip = Modifiers.getModifiersText(item.modifiers, false)})
				end
			end
		end
	end

	-- Synergies
	for i, syn in ipairs(SkillData.Summoning.synergies) do
		local synSkills = Modifiers.getModifierSkills(syn.modifiers)
		if Shared.contains(synSkills, skill) then
			local FamName1 = famNames[syn.summonIDs[1]] or 'Unknown'
			local FamName2 = famNames[syn.summonIDs[2]] or 'Unknown'
			local synDesc = syn.customDescription
			if synDesc == nil then
				-- Generate description from modifiers
				synDesc = Modifiers.getModifiersText(syn.modifiers, false) or ''
			end
			table.insert(rowArray, {Fam1 = FamName1, FamID1 = syn.summonIDs[1], Fam2 = FamName2, FamID2 = syn.summonIDs[2], Descrip = synDesc})
		end
	end

	if Shared.tableIsEmpty(rowArray) then
		return ''
	end

	local html = mw.html.create('table')
		:addClass('wikitable sortable stickyHeader col-1-img col-3-img')
		
	html:tag('tr'):addClass('headerRow-0')
			:tag('th'):wikitext('Familiar 1')
					  :attr('colspan', 2)
			:tag('th'):wikitext('Familiar 2')
					  :attr('colspan', 2)
			:tag('th'):wikitext('DLC')
			:tag('th'):wikitext('Effect')
	
	

	for i, rowItem in ipairs(rowArray) do
		local DLCIcon = Icons.getExpansionIcon(rowItem.FamID1)
		local row = html:tag('tr')
		row:tag('td'):wikitext(Icons.Icon({rowItem.Fam1, type='item', notext=true}))
					 :attr('data-sort-value', rowItem.Fam1)
		row:tag('td'):wikitext(Icons.Icon({rowItem.Fam1, type='item', noicon=true}))
		
		if rowItem.Fam2 ~= nil then
			-- If Fam1 has no DLC, try setting it for Fam2
			if DLCIcon == nil or DLCIcon == '' then
				DLCIcon = Icons.getExpansionIcon(rowItem.FamID2)
			end
			row:tag('td'):wikitext(Icons.Icon({rowItem.Fam2, type='item', notext=true}))
						 :attr('data-sort-value', rowItem.Fam2)
			row:tag('td'):wikitext(Icons.Icon({rowItem.Fam2, type='item', noicon=true}))
		else
			row:tag('td'):tag('td')
		end

		-- If both familiars have no DLC, default to melvor icon.
		if DLCIcon == nil or DLCIcon == '' then
			DLCIcon = Icons.Melvor()
		end
		-- As of 06/07/2024, no synergies exist that use two DLCs
		row:tag('td'):wikitext(DLCIcon)
					 :css('text-align', 'center')

		row:tag('td'):wikitext(rowItem.Descrip or ' ')
	end
	
	return tostring(html)

end

function p.getSkillSummoningBonusTable(frame)
	local skillName = frame.args ~= nil and frame.args[1] or frame
	return p._getSkillSummoningBonusTable(skillName)
end

return p