Difference between revisions of "Module:Skills"

From Melvor Idle
(Fixed sorting of farming seed/crop values)
(getSkillName)
(13 intermediate revisions by one other user not shown)
Line 1: Line 1:
 +
--Some skills have their own modules:
 +
--Module:Magic and Module:Prayer being the current two examples.
 +
 
local p = {}
 
local p = {}
  
 +
local ItemData = mw.loadData('Module:Items/data')
 
local SkillData = mw.loadData('Module:Skills/data')
 
local SkillData = mw.loadData('Module:Skills/data')
 
local Constants = mw.loadData('Module:Constants/data')
 
local Constants = mw.loadData('Module:Constants/data')
Line 6: Line 10:
 
local Shared = require('Module:Shared')
 
local Shared = require('Module:Shared')
 
local Items = require('Module:Items')
 
local Items = require('Module:Items')
 +
local ItemSourceTables = require('Module:Items/SourceTables')
 
local Icons = require('Module:Icons')
 
local Icons = require('Module:Icons')
  
Line 14: Line 19:
 
     if skName == skillName then
 
     if skName == skillName then
 
       return ID
 
       return ID
 +
    end
 +
  end
 +
  return nil
 +
end
 +
 +
function p.getSkillName(skillID)
 +
  for skName, ID in Shared.skpairs(Constants.skill) do
 +
    if ID == skillID then
 +
      return skName
 
     end
 
     end
 
   end
 
   end
Line 93: Line 107:
 
     result = result..'\r\n|'..Icons.Icon({seed.name, type='item', size='50', notext=true})..'||[['..seed.name..']]'
 
     result = result..'\r\n|'..Icons.Icon({seed.name, type='item', size='50', notext=true})..'||[['..seed.name..']]'
 
     result = result..'||'..seed.farmingLevel..'||'..Shared.formatnum(seed.farmingXP)
 
     result = result..'||'..seed.farmingLevel..'||'..Shared.formatnum(seed.farmingXP)
     result = result..'||'..Shared.timeString(seed.timeToGrow, true)
+
     result = result..'||data-sort-value="'..seed.timeToGrow..'"|'..Shared.timeString(seed.timeToGrow, true)
 
     result = result..'||data-sort-value="'..seed.sellsFor..'"|'..Icons.GP(seed.sellsFor)
 
     result = result..'||data-sort-value="'..seed.sellsFor..'"|'..Icons.GP(seed.sellsFor)
  
Line 102: Line 116:
 
     end
 
     end
 
     result = result..'||data-sort-value="'..crop.sellsFor..'"|'..Icons.GP(crop.sellsFor)
 
     result = result..'||data-sort-value="'..crop.sellsFor..'"|'..Icons.GP(crop.sellsFor)
     result = result..'||'..Items._getItemSources(seed)
+
     result = result..'||'..ItemSourceTables._getItemSources(seed)
 
   end
 
   end
  
Line 113: Line 127:
  
 
   return p._getFarmingTable(category)
 
   return p._getFarmingTable(category)
 +
end
 +
 +
function p.getMiningTable(frame)
 +
  local result = '{|class="wikitable sortable stickyHeader"'
 +
  result = result..'\r\n|- class="headerRow-0"'
 +
  result = result..'\r\n!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
 +
  result = result..'!!XP!!Respawn Time!!Ore Value'
 +
  local mineData = Shared.clone(SkillData.Mining)
 +
  table.sort(mineData, function(a, b) return a.level < b.level end)
 +
  for i, oreData in Shared.skpairs(mineData) do
 +
    local ore = Items.getItemByID(oreData.ore)
 +
    result = result..'\r\n|-\r\n|'..Icons.Icon({ore.name, type='item', size='50', notext=true})..'||'..ore.name
 +
    result = result..'||style="text-align:right"|'..oreData.level..'||style="text-align:right"|'..ore.miningXP
 +
    result = result..'||style="text-align:right" data-sort-value="'..oreData.respawnInterval..'"|'
 +
    result = result..Shared.timeString(oreData.respawnInterval / 1000, true)
 +
    result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor)
 +
  end
 +
 +
  result = result..'\r\n|}'
 +
  return result
 +
end
 +
 +
function p.getPotionNavbox(frame)
 +
  --•
 +
  local result = '{| class="wikitable" style="margin:auto; clear:both; width: 100%"'
 +
  result = result..'\r\n!colspan=2|'..Icons.Icon({'Herblore', 'Potions', type='skill'})
 +
 +
  local CombatPots = {}
 +
  local SkillPots = {}
 +
  for i, potData in Shared.skpairs(SkillData.Herblore.ItemData) do
 +
    if potData.category == 0 then
 +
      table.insert(CombatPots, Icons.Icon({potData.name, type='item', img=(potData.name..' I')}))
 +
    else
 +
      if potData.name == 'Bird Nests Potion' then
 +
        table.insert(SkillPots, Icons.Icon({"Bird Nest Potion", type='item', img="Bird Nest Potion I"}))
 +
      else
 +
        table.insert(SkillPots, Icons.Icon({potData.name, type='item', img=(potData.name..' I')}))
 +
      end
 +
    end
 +
  end
 +
 +
  result = result..'\r\n|-\r\n!Combat Potions\r\n|class="center" style="vertical-align:middle;"'
 +
  result = result..'|'..table.concat(CombatPots, ' • ')
 +
  result = result..'\r\n|-\r\n!Skill Potions\r\n|class="center" style="vertical-align:middle;"'
 +
  result = result..'|'..table.concat(SkillPots, ' • ')
 +
  result = result..'\r\n|}'
 +
  return result
 +
end
 +
 +
function p.getSpecialFishingTable(frame)
 +
  local lootValue = 0
 +
  local totalWt = Items.specialFishWt
 +
 +
  local result = ''
 +
  result = result..'\r\n{|class="wikitable sortable"'
 +
  result = result..'\r\n!Item'
 +
  result = result..'!!Price!!colspan="2"|Chance'
 +
 +
  --Sort the loot table by weight in descending order
 +
  table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
 +
  for i, row in pairs(Items.specialFishLoot) do
 +
    local thisItem = Items.getItemByID(row[1])
 +
    result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
 +
    result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
 +
    result = result..'|'..Icons.GP(thisItem.sellsFor)
 +
 +
    local dropChance = (row[2] / totalWt) * 100
 +
    result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
 +
    result = result..'|'..Shared.fraction(row[2], totalWt)
 +
    result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
 +
    lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
 +
  end
 +
  result = result..'\r\n|}'
 +
  result = result..'\r\nThe average value of a roll on the special fishing loot table is '..Icons.GP(Shared.round(lootValue, 2, 0))
 +
 +
  return result
 +
end
 +
 +
function p.getSmithingTable(frame)
 +
  local tableType = frame.args ~= nil and frame.args[1] or frame
 +
  local bar = nil
 +
  if tableType ~= 'Smelting' then
 +
    bar = Items.getItem(tableType)
 +
    if bar == nil then
 +
      return 'ERROR: Could not find an item named '..tableType..' to build a smithing table with'
 +
    elseif bar.type ~= 'Bar' then
 +
      return 'ERROR: '..tableType.." is not a bar and thus can't be used for smithing"
 +
    end
 +
  end
 +
 +
  local smithList = {}
 +
  for i, item in pairs(ItemData.Items) do
 +
    if item.smithingLevel ~= nil then
 +
      if tableType == 'Smelting' then
 +
        if item.type == 'Bar' then
 +
          table.insert(smithList, item)
 +
        end
 +
      else
 +
        for j, req in pairs(item.smithReq) do
 +
          if req.id == bar.id then
 +
            table.insert(smithList, item)
 +
          end
 +
        end
 +
      end
 +
    end
 +
  end
 +
 +
  local result = '{|class="wikitable sortable stickyHeader"'
 +
  result = result..'\r\n|-class="headerRow-0"'
 +
  result = result..'\r\n!Item!!Name!!'..Icons.Icon({'Smithing', type='skill', notext=true})..' Level!!XP!!Value!!Ingredients'
 +
 +
  table.sort(smithList, function(a, b)
 +
                          if a.smithingLevel ~= b.smithingLevel then
 +
                            return a.smithingLevel < b.smithingLevel
 +
                          else
 +
                            return a.name < b.name
 +
                          end end)
 +
  for i, item in Shared.skpairs(smithList) do
 +
    result = result..'\r\n|-'
 +
    result = result..'\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true})..'||'
 +
    local qty = item.smithingQty ~= nil and item.smithingQty or 1
 +
    if qty > 1 then
 +
      result = result..item.smithingQty..'x '
 +
    end
 +
    result = result..'[['..item.name..']]'
 +
    result = result..'||data-sort-value="'..item.smithingLevel..'"|'..Icons._SkillReq('Smithing', item.smithingLevel)
 +
    result = result..'||'..item.smithingXP
 +
    result = result..'||data-sort-value="'..item.sellsFor * qty..'"|'..Icons.GP(item.sellsFor)
 +
    if qty > 1 then
 +
      result = result..' (x'..qty..')'
 +
    end
 +
    result = result..'||'
 +
    for i, mat in Shared.skpairs(item.smithReq) do
 +
      matItem = Items.getItemByID(mat.id)
 +
      if i > 1 then result = result..', ' end
 +
      result = result..Icons.Icon({matItem.name, type='item', qty=mat.qty, notext=true})
 +
    end
 +
  end
 +
 +
  result = result..'\r\n|}'
 +
  return result
 
end
 
end
  
 
return p
 
return p

Revision as of 01:07, 14 January 2021

Data pulled from Module:Skills/data


--Some skills have their own modules:
--Module:Magic and Module:Prayer being the current two examples.

local p = {}

local ItemData = mw.loadData('Module:Items/data')
local SkillData = mw.loadData('Module:Skills/data')
local Constants = mw.loadData('Module:Constants/data')

local Shared = require('Module:Shared')
local Items = require('Module:Items')
local ItemSourceTables = require('Module:Items/SourceTables')
local Icons = require('Module:Icons')

local MasteryCheckpoints = {.1, .25, .5, .95}

function p.getSkillID(skillName)
  for skName, ID in Shared.skpairs(Constants.skill) do
    if skName == skillName then
      return ID
    end
  end
  return nil
end

function p.getSkillName(skillID)
  for skName, ID in Shared.skpairs(Constants.skill) do
    if ID == skillID then
      return skName
    end
  end
  return nil
end

function p.getMasteryUnlockTable(frame)
  local skillName = frame.args ~= nil and frame.args[1] or frame
  local skillID = p.getSkillID(skillName)
  if skillID == nil then
    return "ERROR: Failed to find a skill ID for "..skillName
  end

  local unlockTable = SkillData.MasteryUnlocks[skillID]
  if unlockTable == nil then
    return 'ERROR: Failed to find Mastery Unlock data for '..skillName
  end

  local result = '{|class="wikitable"\r\n!Level!!Unlock'
  for i, unlock in Shared.skpairs(unlockTable) do
    result = result..'\r\n|-'
    result = result..'\r\n|'..unlock.level..'||'..unlock.unlock
  end
  result = result..'\r\n|}'
  return result
end

function p.getMasteryCheckpointTable(frame)
  local skillName = frame.args ~= nil and frame.args[1] or frame
  local skillID = p.getSkillID(skillName)
  if skillID == nil then
    return "ERROR: Failed to find a skill ID for "..skillName
  end

  if SkillData.MasteryCheckpoints[skillID] == nil then
    return 'ERROR: Failed to find Mastery Unlock data for '..skillName
  end

  local bonuses = SkillData.MasteryCheckpoints[skillID].bonuses
  local totalPoolXP = SkillData.MasteryPoolXP[skillID + 1]

  local result = '{|class="wikitable"\r\n!Pool %!!style="width:100px"|Pool XP!!Bonus'
  for i, bonus in Shared.skpairs(bonuses) do
    result = result..'\r\n|-'
    result = result..'\r\n|'..(MasteryCheckpoints[i] * 100)..'%||'
    result = result..Shared.formatnum(totalPoolXP * MasteryCheckpoints[i])..' xp||'..bonus
  end
  result = result..'\r\n|-\r\n!colspan="2"|Total Mastery Pool XP'
  result = result..'\r\n|'..Shared.formatnum(totalPoolXP)
  result = result..'\r\n|}'
  return result
end

function p._getFarmingTable(category)
  local seedList = {}
  if category == 'Allotment' or category == 'Herb' or category == 'Tree' then
    seedList = Items.getItems(function(item) return item.tier == category end)
  else
    return 'ERROR: Invalid farming category. Please choose Allotment, Herb, or Tree'
  end

  local result = '{|class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!colspan=2|Seeds!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level'
  result = result..'!!XP!!Growth Time!!Seed Value'
  if category == 'Allotment' then
    result = result..'!!colspan="2"|Crop!!Crop Healing!!Crop Value'
  elseif category == 'Herb' then
    result = result..'!!colspan="2"|Herb!!Herb Value'
  elseif category == 'Tree' then
    result = result..'!!colspan="2"|Logs!!Log Value'
  end
  result = result..'!!Seed Sources'
  
  table.sort(seedList, function(a, b) return a.farmingLevel < b.farmingLevel end)

  for i, seed in pairs(seedList) do
    result = result..'\r\n|-'
    result = result..'\r\n|'..Icons.Icon({seed.name, type='item', size='50', notext=true})..'||[['..seed.name..']]'
    result = result..'||'..seed.farmingLevel..'||'..Shared.formatnum(seed.farmingXP)
    result = result..'||data-sort-value="'..seed.timeToGrow..'"|'..Shared.timeString(seed.timeToGrow, true)
    result = result..'||data-sort-value="'..seed.sellsFor..'"|'..Icons.GP(seed.sellsFor)

    local crop = Items.getItemByID(seed.grownItemID)
    result = result..'||'..Icons.Icon({crop.name, type='item', size='50', notext=true})..'||[['..crop.name..']]'
    if category == 'Allotment' then
      result = result..'||'..Icons.Icon({'Hitpoints', type='skill', notext=true})..' '..(crop.healsFor * 10)
    end
    result = result..'||data-sort-value="'..crop.sellsFor..'"|'..Icons.GP(crop.sellsFor)
    result = result..'||'..ItemSourceTables._getItemSources(seed)
  end

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

function p.getFarmingTable(frame)
  local category = frame.args ~= nil and frame.args[1] or frame

  return p._getFarmingTable(category)
end

function p.getMiningTable(frame)
  local result = '{|class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
  result = result..'!!XP!!Respawn Time!!Ore Value'
  local mineData = Shared.clone(SkillData.Mining)
  table.sort(mineData, function(a, b) return a.level < b.level end)
  for i, oreData in Shared.skpairs(mineData) do
    local ore = Items.getItemByID(oreData.ore)
    result = result..'\r\n|-\r\n|'..Icons.Icon({ore.name, type='item', size='50', notext=true})..'||'..ore.name
    result = result..'||style="text-align:right"|'..oreData.level..'||style="text-align:right"|'..ore.miningXP
    result = result..'||style="text-align:right" data-sort-value="'..oreData.respawnInterval..'"|'
    result = result..Shared.timeString(oreData.respawnInterval / 1000, true)
    result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor)
  end

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

function p.getPotionNavbox(frame)
  --•
  local result = '{| class="wikitable" style="margin:auto; clear:both; width: 100%"'
  result = result..'\r\n!colspan=2|'..Icons.Icon({'Herblore', 'Potions', type='skill'})

  local CombatPots = {}
  local SkillPots = {}
  for i, potData in Shared.skpairs(SkillData.Herblore.ItemData) do
    if potData.category == 0 then
      table.insert(CombatPots, Icons.Icon({potData.name, type='item', img=(potData.name..' I')}))
    else
      if potData.name == 'Bird Nests Potion' then
        table.insert(SkillPots, Icons.Icon({"Bird Nest Potion", type='item', img="Bird Nest Potion I"}))
      else
        table.insert(SkillPots, Icons.Icon({potData.name, type='item', img=(potData.name..' I')}))
      end
    end
  end

  result = result..'\r\n|-\r\n!Combat Potions\r\n|class="center" style="vertical-align:middle;"'
  result = result..'|'..table.concat(CombatPots, ' • ')
  result = result..'\r\n|-\r\n!Skill Potions\r\n|class="center" style="vertical-align:middle;"'
  result = result..'|'..table.concat(SkillPots, ' • ')
  result = result..'\r\n|}'
  return result
end

function p.getSpecialFishingTable(frame)
  local lootValue = 0
  local totalWt = Items.specialFishWt

  local result = ''
  result = result..'\r\n{|class="wikitable sortable"'
  result = result..'\r\n!Item'
  result = result..'!!Price!!colspan="2"|Chance'

  --Sort the loot table by weight in descending order
  table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
  for i, row in pairs(Items.specialFishLoot) do
    local thisItem = Items.getItemByID(row[1])
    result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
    result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
    result = result..'|'..Icons.GP(thisItem.sellsFor)

    local dropChance = (row[2] / totalWt) * 100
    result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
    result = result..'|'..Shared.fraction(row[2], totalWt)
    result = result..'||style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
    lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
  end
  result = result..'\r\n|}'
  result = result..'\r\nThe average value of a roll on the special fishing loot table is '..Icons.GP(Shared.round(lootValue, 2, 0))

  return result
end

function p.getSmithingTable(frame)
  local tableType = frame.args ~= nil and frame.args[1] or frame
  local bar = nil
  if tableType ~= 'Smelting' then
    bar = Items.getItem(tableType)
    if bar == nil then
      return 'ERROR: Could not find an item named '..tableType..' to build a smithing table with'
    elseif bar.type ~= 'Bar' then
      return 'ERROR: '..tableType.." is not a bar and thus can't be used for smithing"
    end
  end

  local smithList = {}
  for i, item in pairs(ItemData.Items) do
    if item.smithingLevel ~= nil then
      if tableType == 'Smelting' then
        if item.type == 'Bar' then
          table.insert(smithList, item)
        end
      else
        for j, req in pairs(item.smithReq) do
          if req.id == bar.id then
            table.insert(smithList, item)
          end
        end
      end
    end
  end

  local result = '{|class="wikitable sortable stickyHeader"'
  result = result..'\r\n|-class="headerRow-0"'
  result = result..'\r\n!Item!!Name!!'..Icons.Icon({'Smithing', type='skill', notext=true})..' Level!!XP!!Value!!Ingredients'

  table.sort(smithList, function(a, b)
                          if a.smithingLevel ~= b.smithingLevel then
                            return a.smithingLevel < b.smithingLevel
                          else
                            return a.name < b.name
                          end end)
  for i, item in Shared.skpairs(smithList) do
    result = result..'\r\n|-'
    result = result..'\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true})..'||'
    local qty = item.smithingQty ~= nil and item.smithingQty or 1
    if qty > 1 then
      result = result..item.smithingQty..'x '
    end
    result = result..'[['..item.name..']]'
    result = result..'||data-sort-value="'..item.smithingLevel..'"|'..Icons._SkillReq('Smithing', item.smithingLevel)
    result = result..'||'..item.smithingXP
    result = result..'||data-sort-value="'..item.sellsFor * qty..'"|'..Icons.GP(item.sellsFor)
    if qty > 1 then
      result = result..' (x'..qty..')'
    end
    result = result..'||'
    for i, mat in Shared.skpairs(item.smithReq) do
      matItem = Items.getItemByID(mat.id)
      if i > 1 then result = result..', ' end
      result = result..Icons.Icon({matItem.name, type='item', qty=mat.qty, notext=true})
    end
  end

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

return p