Module:Skills/Summoning: Difference between revisions
From Melvor Idle
Falterfire (talk | contribs) No edit summary |
m (Add function to get a familiar recipe by id) |
||
(47 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
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') | |||
local Num = require('Module:Number') | |||
function p. | |||
-- Gets a familiar recipe by ID, e.g. melvorF:GolbinThief | |||
function p.getFamiliarRecipeByID(id) | |||
return GameData.getEntityByID(SkillData.Summoning.recipes, id) | |||
end | |||
function p.getSynergies(checkFunc) | |||
local result = {} | |||
for i, synergy in pairs(SkillData.Summoning.synergies) do | |||
if checkFunc(synergy) then | |||
table.insert(result, synergy) | |||
end | |||
end | |||
return result | |||
end | |||
local function getSummonModifierText(summonItem, maxVisible) | |||
local playerMods = Modifiers.getModifiersText(summonItem.modifiers, false, false, maxVisible) or '' | |||
local enemyMods = Modifiers.getModifiersText(summonItem.enemyModifiers, false, false, maxVisible, function(text) return 'Gives the enemy: ' .. text end) or '' | |||
return playerMods .. (playerMods ~= '' and '<br>' or '') .. enemyMods | |||
end | end | ||
function p.getMarkTable(frame) | 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 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 | end | ||
function p. | 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 = getSummonModifierText(syn, 10) | |||
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 = getSummonModifierText(item)}) | |||
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 = getSummonModifierText(syn) | |||
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 | end | ||
return p | return p |
Latest revision as of 23:17, 7 October 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')
-- Gets a familiar recipe by ID, e.g. melvorF:GolbinThief
function p.getFamiliarRecipeByID(id)
return GameData.getEntityByID(SkillData.Summoning.recipes, id)
end
function p.getSynergies(checkFunc)
local result = {}
for i, synergy in pairs(SkillData.Summoning.synergies) do
if checkFunc(synergy) then
table.insert(result, synergy)
end
end
return result
end
local function getSummonModifierText(summonItem, maxVisible)
local playerMods = Modifiers.getModifiersText(summonItem.modifiers, false, false, maxVisible) or ''
local enemyMods = Modifiers.getModifiersText(summonItem.enemyModifiers, false, false, maxVisible, function(text) return 'Gives the enemy: ' .. text end) or ''
return playerMods .. (playerMods ~= '' and '<br>' or '') .. enemyMods
end
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 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 = getSummonModifierText(syn, 10)
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 = getSummonModifierText(item)})
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 = getSummonModifierText(syn)
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