Module:CombatAreas/AreaTables

From Melvor Idle
< Module:CombatAreas
Revision as of 23:43, 3 July 2021 by Falterfire (talk | contribs) (Some initial prepwork on setting up dungeon DR tables. Too lazy to finish it right now)

Documentation for this module may be created at Module:CombatAreas/AreaTables/doc

--Splitting this out so I can make Module:Monsters calls (Monsters calls CombatAreas, so this prevents a loop)

local p = {}

local AreaData = mw.loadData('Module:CombatAreas/data')

local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Monsters = require('Module:Monsters')
local CombatAreas = require('Module:CombatAreas')
local Pets = require('Module:Pets')

function p.getLowHighLevels(idList)
  local lowLevel = 1000000
  local highLevel = 0
  for i, monID in Shared.skpairs(idList) do
    local monster = Monsters.getMonsterByID(monID)
    local cmbLevel = Monsters._getMonsterCombatLevel(monster)
    if cmbLevel < lowLevel then lowLevel = cmbLevel end
    if cmbLevel > highLevel then highLevel = cmbLevel end
  end
  return lowLevel, highLevel
end

function p.getCombatAreaTable()
  local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!colspan="2"|Zone!!Difficulty!!Lowest Monster Level!!Highest Monster Level'

  local combatAreas = Shared.clone(AreaData.combatAreas)
  table.sort(combatAreas, function(a, b) 
               if a.difficulty[1] ~= b.difficulty[1] then
                 return a.difficulty[1] < b.difficulty[1]
               elseif a.difficulty[2] == nil then
                 return true
               elseif b.difficulty[2] == nil then
                 return false
               elseif a.difficulty[2] ~= b.difficulty[2] then
                 return a.difficulty[2] < b.difficulty[2]
               else
                 return a.areaName < b.areaName
               end
             end)

  for i, area in Shared.skpairs(combatAreas) do
    result = result..'\r\n|-'
    result = result..'\r\n|'..Icons.Icon({area.areaName, type='combat', size='50', notext=true})..'||[['..area.areaName..']]'
    local diff1 = Constants.getDifficultyString(area.difficulty[1])
    local diff2 = Constants.getDifficultyString(area.difficulty[2])
    result = result..'||data-sort-value="'..area.difficulty[1]..'"|'..diff1
    if diff1 ~= diff2 and diff2 ~= nil then result = result..' - '..diff2 end
    local lowLvl, highLvl = p.getLowHighLevels(area.monsters)
    result = result..'||'..lowLvl..'||'..highLvl
  end

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

  return result
end

function p.getSlayerAreaTable()
  local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!colspan="2"|Zone!!Difficulty!!Highest Monster Level!!Requirements!!Area Effect'

  local slayerAreas = Shared.clone(AreaData.slayerAreas)
  table.sort(slayerAreas, function(a, b) 
               if a.slayerLevel ~= b.slayerLevel then
                 return a.slayerLevel < b.slayerLevel
               elseif a.difficulty[1] ~= b.difficulty[1] then
                 return a.difficulty[1] < b.difficulty[1]
               elseif a.difficulty[2] == nil then
                 return true
               elseif b.difficulty[2] == nil then
                 return false
               elseif a.difficulty[2] ~= b.difficulty[2] then
                 return a.difficulty[2] < b.difficulty[2]
               else
                 return a.areaName < b.areaName
               end
             end)

  for i, area in Shared.skpairs(slayerAreas) do
    result = result..'\r\n|-'
    result = result..'\r\n|'..Icons.Icon({area.areaName, type='slayer', size='50', notext=true})..'||[['..area.areaName..']]'
    local diff1 = Constants.getDifficultyString(area.difficulty[1])
    local diff2 = Constants.getDifficultyString(area.difficulty[2])
    result = result..'||data-sort-value="'..area.difficulty[1]..'"|'..diff1
    if diff1 ~= diff2 and diff2 ~= nil then result = result..' - '..diff2 end
    local lowLvl, highLvl = p.getLowHighLevels(area.monsters)
    result = result..'||'..highLvl

    local reqArray = {}
    if area.slayerLevel ~= nil and area.slayerLevel > 1 then
      table.insert(reqArray, Icons._SkillReq('Slayer', area.slayerLevel))
    end
    if area.slayerItem ~= nil and area.slayerItem > 0 then
      local item = Items.getItemByID(area.slayerItem)
      table.insert(reqArray, Icons.Icon({item.name, type='item'})..' Equipped')
    end
    if area.dungeonCompleted ~= nil and area.dungeonCompleted > 0 then
      local dung = CombatAreas.getAreaByID('dungeon', area.dungeonCompleted)
      table.insert(reqArray, Icons.Icon({dung.name, type='dungeon'})..' Completed')
    end
    result = result..'||data-sort-value="'..area.slayerLevel..'"| '..table.concat(reqArray, '<br/>')
    result = result..'||'
    if area.areaEffectDescription ~= nil then
       result = result..area.areaEffectDescription
    end
  end

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

  return result
end

function p._getDungeonRewards(area, asList)
  if asList == nil then
    asList = true
  elseif type(asList) == 'string' then
    asList = asList.upper ~= 'FALSE'
  end

  local bossMonster = Monsters.getMonsterByID(area.monsters[Shared.tableCount(area.monsters)])
  local gpMin = bossMonster.dropCoins[1]
  local gpMax = bossMonster.dropCoins[2] - 1
  local chestID = bossMonster.lootTable[1][1]
  local chestQty = bossMonster.lootTable[1][3]
  local theChest = Items.getItemByID(chestID)
  local chr = asList and '* ' or ''
  local rewardList = {}

  if gpMin > 0 and gpMax > 0 then
    table.insert(rewardList, chr..Icons.GP(gpMin, gpMax))
  end
  table.insert(rewardList, chr..Icons.Icon({theChest.name, type='item', qty=chestQty}))
  if area.name == 'Volcanic Cave' then
    table.insert(rewardList, chr..Icons.Icon({'Fire Cape', type='item', qty=1}))
  elseif area.name == 'Infernal Stronghold' then
    table.insert(rewardList, chr..Icons.Icon({'Infernal Cape', type='item', qty=1}))
  end

  if asList then
    return table.concat(rewardList, '\r\n')
  else
    return table.concat(rewardList, '<br/>')
  end
end

function p.getDungeonRewards(frame)
  local areaName = frame.args ~= nil and frame.args[1] or frame
  local asList = frame.args ~= nil and frame.args[2] or true
  local area = CombatAreas.getArea(areaName)
  if area == nil then
    return "ERROR: Could not find an area named "..areaName..'[[Category:Pages with script errors]]'
  end

  if area.type == 'dungeon' then
    return p._getDungeonRewards(area, asList)
  else
    return "ERROR: "..areaName.." is not a dungeon[[Category:Pages with script errors]]"
  end
end

function p.getDungeonTable(frame)
  local dungeons = CombatAreas.getAreas(function(area) return area.type == 'dungeon' end)

  local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|-class="headerRow-0"'
  result = result..'\r\n!colspan="2"|Dungeon!!Difficulty!!Monsters!!Boss Level!!Reward(s)!!Boss Pet'
  
  table.sort(dungeons, function(a, b) 
                       if a.difficulty[1] ~= b.difficulty[1] then
                         return a.difficulty[1] < b.difficulty[1]
                       else
                         return a.id < b.id
                       end end)
  for i, dung in Shared.skpairs(dungeons) do
    result = result..'\r\n|-'
    result = result..'\r\n|data-sort-value="'..dung.name..'"|'..Icons.Icon({dung.name, type='dungeon', size='50', notext=true})
    result = result..'||[['..dung.name..']]'
    result = result..'||data-sort-value="'..dung.difficulty[1]..'"|'..CombatAreas._getAreaStat(dung, 'difficulty')
    result = result..'||'..Shared.tableCount(dung.monsters)

    local boss = Monsters.getMonsterByID(dung.monsters[Shared.tableCount(dung.monsters)])
    result = result..'||'..Monsters._getMonsterCombatLevel(boss)
    
    result = result..'||'..p._getDungeonRewards(dung, false)
    if dung.petID ~= nil then
      local pet = Pets.getPetByID(dung.petID)
      result = result..'||data-sort-value="'..pet.name..'"|'..Icons.Icon({pet.name, type='pet'})
    else
      result = result..'|| '
    end
  end

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

function p._getDungeonDRTable(dung, mode, doStuns)
  local AutoEatVals = {T1 = 0.2, T2 = 0.3, T3 = 0.4, T3W = 0.45} 
  --NOTE: Due to Agility Obstacles, this value is a tad arbitrary
  local MaxViableHP = 1120
  --This is the highest DR to list as possible. Value might be slightly off right now, currently just setting up value for testing
  local MaxViableDR = 81
  --This is the highest DR row shown. This should be higher than MaxViableDR
  local MaxVisibleDR = 85

  if doStuns == nil then
    doStuns = true
  elseif type(doStuns) == 'string' then
    doStuns = string.upper(doStuns) == 'TRUE'
  end

  --First, figure out what our max hit for each style is
  local MaxHits = { Melee = 0, Ranged = 0, Magic = 0 }
  for i, monsterID in Shared.skpairs(dung.monsters) do
    local monster = Monsters.getMonsterByID(monsterID)
    local styleName = Constants.getCombatStyleName(monster.attackType)
    local maxHit = Monsters._getMonsterMaxHit(monster, doStuns)
    if maxHit > MaxHits[styleName] then
      MaxHits[styleName] = maxHit
    end
  end

  --Then, figure out the DR row to start with
  --This is the DR that is one lower than the lowest possible DR for the best style
  local StyleArray = {"Melee", "Ranged", "Magic"}
  local EatThreshold = math.floor(MaxViableHP * AutoEatVals.T3W)
  local minDR = 100
  for i, playerStyle in Shared.skpairs(StyleArray) do
    local maxStyleDR = 0
    for enemyStyle, styleHit in Shared.skpairs(MaxHits) do
      if styleHit > 0 and styleHit > EatThreshold then
        local styleDR = math.ceil((1 - (EatThreshold / styleHit)) * 100)
        styleDR = math.ceil(styleDR / Constants.getTriangleDRModifier(playerStyle, enemyStyle, mode))
        maxStyleDR = math.max(maxStyleDR, styleDR)
      end
    end
    minDR = math.min(minDR, maxStyleDR)
  end

  minDR = minDR - 1

  --Finally, build the table using those starting points
  local StyleHeader = "Melee!!Ranged!!Magic"
  StyleHeader = StyleHeader..'!!'..StyleHeader..'!!'..StyleHeader..'!!'..StyleHeader

  local result = '{| class="wikitable stickyHeader"'
  result = result..'\r\n|-class="headerRow-0"'
  result = result..'\r\n!Pre-Triangle DR!!colspan=3|AE T3 + Wasteful!!colspan=3|Auto Eat Tier 3!!colspan=3|Auto Eat Tier 2!!colspan=3|Auto Eat Tier 1'
  result = result..'\r\n|-class="headerRow-0"'
  result = result..'\r\n!DR %!!'..StyleHeader

  local getHpForStyle = function(playerStyle, autoEat, playerDR) 
                          
                        end

  for dr = minDR, MaxVisibleDR, 1 do
    
  end

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

  return result
end

function p.getDungeonDRTable(frame)
  local dungName = frame.args ~= nil and frame.args[1] or frame[1]
  local mode = frame.args ~= nil and frame.args[2] or frame[2]
  local doStuns = frame.args ~= nil and frame.args[3] or frame[3]
  local dung = CombatAreas.getArea(dungName)

  if dung == nil then
    return 'ERROR: Invalid dungeon name'
  end

  return p._getDungeonDRTable(dung, mode, doStuns)
end

return p