3
edits
m (getMonsterName: Fix special case) |
(Add column name for average healing) |
||
(11 intermediate revisions by 5 users not shown) | |||
Line 53: | Line 53: | ||
function p._getMonsterStat(monster, statName) | function p._getMonsterStat(monster, statName) | ||
if statName == 'HP' then | if statName == 'Barrier' then | ||
return p._getMonsterBarrier(monster) | |||
elseif statName == 'HP' then | |||
return p._getMonsterHP(monster) | return p._getMonsterHP(monster) | ||
elseif statName == 'maxHit' then | elseif statName == 'maxHit' then | ||
Line 120: | Line 122: | ||
function p._getMonsterHP(monster) | function p._getMonsterHP(monster) | ||
return 10 * p._getMonsterLevel(monster, 'Hitpoints') | return 10 * p._getMonsterLevel(monster, 'Hitpoints') | ||
end | |||
function p._getMonsterBarrier(monster) | |||
--Monster Barrier is a percentage of its max health | |||
local barPercent = 0 | |||
if monster.barrierPercent ~= nil then | |||
barPercent = monster.barrierPercent | |||
end | |||
return p._getMonsterHP(monster) * barPercent * 0.01 | |||
end | end | ||
Line 127: | Line 138: | ||
if monster ~= nil then | if monster ~= nil then | ||
return math.floor((p._getMonsterHP(monster)/(1 - p._getMonsterStat(monster, 'damageReduction')/100)) + 0.5) | return math.floor((p._getMonsterHP(monster)/(1 - p._getMonsterStat(monster, 'damageReduction')/100)) + 0.5) | ||
else | |||
return Shared.printError('No monster with that name found') | |||
end | |||
end | |||
function p.getMonsterEffectiveBarrier(frame) | |||
local MonsterName = frame.args ~= nil and frame.args[1] or frame | |||
local monster = p.getMonster(MonsterName) | |||
if monster ~= nil then | |||
return math.floor((p._getMonsterBarrier(monster)/(1 - p._getMonsterStat(monster, 'damageReduction')/100)) + 0.5) | |||
else | |||
return Shared.printError('No monster with that name found') | |||
end | |||
end | |||
function p.getMonsterBarrier(frame) | |||
local MonsterName = frame.args ~= nil and frame.args[1] or frame | |||
local monster = p.getMonster(MonsterName) | |||
if monster ~= nil then | |||
return p._getMonsterBarrier(monster) | |||
else | else | ||
return Shared.printError('No monster with that name found') | return Shared.printError('No monster with that name found') | ||
Line 853: | Line 884: | ||
return result | return result | ||
end | |||
function p.getMonsterBoxBarrierText(frame) | |||
local MonsterName = frame.args ~= nil and frame.args[1] or frame | |||
local monster = p.getMonster(MonsterName) | |||
if monster == nil then | |||
return Shared.printError('No monster with that name found') | |||
end | |||
local barrier = p._getMonsterBarrier(monster) | |||
if barrier == 0 then | |||
return '' | |||
end | |||
local result = {} | |||
table.insert(result, '|-\r\n| style="font-weight: bold;" | [[Barrier]]:') | |||
table.insert(result, '\r\n| colspan=15 style="text-align: right" |') | |||
table.insert(result, Icons.Icon({"Barrier", notext="true"})) | |||
table.insert(result, ' '..barrier) | |||
return table.concat(result, '') | |||
end | end | ||
Line 908: | Line 960: | ||
local bones = p._getMonsterBones(monster) | local bones = p._getMonsterBones(monster) | ||
local boneVal = 0 | local boneVal = 0 | ||
local barrierDust = Items.getItemByID("melvorAoD:Barrier_Dust") | |||
local dustVal = 0 | |||
--Show the bones only if either the monster shows up outside of dungeons _or_ the monster drops shards | --Show the bones only if either the monster shows up outside of dungeons _or_ the monster drops shards | ||
if bones ~= nil then | if bones ~= nil then | ||
local boneQty = (bones.quantity ~= nil and bones.quantity or 1) | local boneQty = (bones.quantity ~= nil and bones.quantity or 1) | ||
local barrier = p._getMonsterBarrier(monster) | |||
result = result.."'''Always Drops:'''" | result = result.."'''Always Drops:'''" | ||
result = result..'\r\n{|class="wikitable" id="bonedrops"' | result = result..'\r\n{|class="wikitable" id="bonedrops"' | ||
result = result..'\r\n!Item !! Qty' | result = result..'\r\n!Item !! Qty' | ||
result = result..'\r\n|-\r\n|'..Icons.Icon({bones.item.name, type='item'}) | result = result..'\r\n|-\r\n|'..Icons.Icon({bones.item.name, type='item'}) | ||
result = result..'||'..boneQty..'\r\n'..'|}' | result = result..'||'..boneQty | ||
if barrier > 0 then | |||
local dustQty = math.max(math.floor(barrier / 10 / 20), 1) | |||
result = result..'\r\n|-\r\n|'..Icons.Icon({barrierDust.name, type='item'}) | |||
result = result..'||'..dustQty | |||
dustVal = dustQty * barrierDust.sellsFor | |||
end | |||
result = result..'\r\n'..'|}' | |||
boneVal = boneQty * bones.item.sellsFor | boneVal = boneQty * bones.item.sellsFor | ||
end | end | ||
Line 1,017: | Line 1,079: | ||
result = result..' and bones' | result = result..' and bones' | ||
end | end | ||
result = result..', the average kill is worth '..Icons.GP(Shared.round(avgGp + lootValue + boneVal, 2, 0))..'.' | if dustVal > 0 then | ||
result = result..' and barrier dust' | |||
end | |||
result = result..', the average kill is worth '..Icons.GP(Shared.round(avgGp + lootValue + boneVal + dustVal, 2, 0))..'.' | |||
end | end | ||
end | end | ||
Line 1,130: | Line 1,195: | ||
return Shared.printError(chestName .. ' does not have a drop table') | return Shared.printError(chestName .. ' does not have a drop table') | ||
else | else | ||
local lootValue = 0 | |||
local function formatNumRange(minValue, maxValue) | |||
if maxValue ~= nil and maxValue > minValue then | |||
return Shared.formatnum(minValue) .. ' - ' .. Shared.formatnum(maxValue) | |||
else | |||
return Shared.formatnum(minValue) | |||
end | |||
end | |||
local lootValue, foodValue = 0, 0 | |||
local totalWt = 0 | local totalWt = 0 | ||
for i, row in | local isAllFood = true | ||
for i, row in ipairs(chest.dropTable) do | |||
totalWt = totalWt + row.weight | totalWt = totalWt + row.weight | ||
if isAllFood then | |||
-- If the container's contents are entirely food then we add additional | |||
-- information to the output, so we determine this here | |||
local item = Items.getItemByID(row.itemID) | |||
if item ~= nil and item.healsFor == nil then | |||
isAllFood = false | |||
end | |||
end | |||
end | end | ||
result = result..'\r\n{|class="wikitable sortable"' | result = result..'\r\n{|class="wikitable sortable"' | ||
result = result..'\r\n!Item!!Qty' | result = result..'\r\n!Item!!Qty' | ||
result = result..'!!colspan="2"|Chance!!Price' | result = result..'!!colspan="2"|Chance!!Price' .. (isAllFood and '!!Healing!!Avg. Healing' or '') | ||
--Sort the loot table by weight in descending order | --Sort the loot table by weight in descending order | ||
local chestDrops = | local chestDrops = Shared.shallowClone(chest.dropTable) | ||
table.sort(chestDrops, function(a, b) return a.weight > b.weight end) | table.sort(chestDrops, function(a, b) return a.weight > b.weight end) | ||
for i, row in ipairs(chestDrops) do | for i, row in ipairs(chestDrops) do | ||
local thisItem = Items.getItemByID(row.itemID) | local thisItem = Items.getItemByID(row.itemID) | ||
result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}) | result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}) | ||
result = result..'||style="text-align:right" data-sort-value="'..(row.minQuantity + row.maxQuantity)..'"|' | result = result..'||style="text-align:right" data-sort-value="'..(row.minQuantity + row.maxQuantity)..'"| ' .. formatNumRange(row.minQuantity, row.maxQuantity) | ||
local dropChance = (row.weight / totalWt) * 100 | local dropChance = (row.weight / totalWt) * 100 | ||
Line 1,169: | Line 1,243: | ||
end | end | ||
lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor * ((row.minQuantity + row.maxQuantity)/ 2)) | lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor * ((row.minQuantity + row.maxQuantity)/ 2)) | ||
if isAllFood then | |||
local hp = thisItem.healsFor * 10 | |||
local minHeal, maxHeal = hp * row.minQuantity, hp * row.maxQuantity | |||
local avgHpPerLoot = (dropChance * 0.01 * (minHeal + maxHeal) / 2) | |||
foodValue = foodValue + avgHpPerLoot | |||
result = result .. '||data-sort-value="' .. thisItem.healsFor .. '"' | |||
result = result .. '|' .. Icons.Icon({'Hitpoints', type='skill', notext=true, nolink=true}) .. ' ' .. formatNumRange(minHeal, maxHeal) | |||
result = result .. '||data-sort-value="' .. avgHpPerLoot .. '"' | |||
result = result .. '|' .. Icons.Icon({'Hitpoints', type='skill', notext=true, nolink=true}) .. ' ' .. Shared.round(avgHpPerLoot, 2, 0) | |||
end | |||
end | end | ||
result = result..'\r\n|}' | result = result..'\r\n|}' | ||
result = result..'\r\nThe average value of the contents of one chest is '..Icons.GP(Shared.round(lootValue, 2, 0))..'.' | result = result..'\r\nThe average value of the contents of one chest is '..Icons.GP(Shared.round(lootValue, 2, 0))..'.' | ||
if isAllFood then | |||
result = result..'\r\n\r\nThe average healing of the contents of one chest is ' .. Icons.Icon({'Hitpoints', type='skill', notext=true, nolink=true}) .. ' ' .. Shared.round(foodValue, 2, 0) .. '.' | |||
end | |||
end | end | ||
Line 1,187: | Line 1,275: | ||
return p.getDungeonMonsterTable(frame) | return p.getDungeonMonsterTable(frame) | ||
end | end | ||
local | |||
local monsters = {} | |||
local hasBarrier = false | |||
for i, monsterID in ipairs(area.monsterIDs) do | for i, monsterID in ipairs(area.monsterIDs) do | ||
local monster = p.getMonsterByID(monsterID) | local monster = p.getMonsterByID(monsterID) | ||
if not hasBarrier and p._getMonsterBarrier(monster) > 0 then | |||
hasBarrier = true | |||
end | |||
table.insert(monsters, monster) | |||
end | |||
local tableBits = {} | |||
table.insert(tableBits, '{| class="wikitable sortable"') | |||
table.insert(tableBits, '\r\n! Name !! Combat Level ') | |||
if hasBarrier then | |||
table.insert(tableBits, '!! [[Barrier]] ') | |||
end | |||
table.insert(tableBits, '!! Hitpoints !! colspan=2| Max Hit !! [[Combat Triangle|Combat Style]]') | |||
for i, monster in ipairs(monsters) do | |||
local rowBits = {} | |||
table.insert(tableBits, '\r\n|-\r\n|'..Icons.Icon({p.getMonsterName(monster), type='monster'})) | |||
table.insert(tableBits, '||'..p._getMonsterCombatLevel(monster)) | |||
if hasBarrier then | |||
table.insert(tableBits, '||'..Shared.formatnum(p._getMonsterBarrier(monster))) | |||
end | |||
table.insert(tableBits, '||'..Shared.formatnum(p._getMonsterHP(monster))) | |||
local drReduction = p._getMonsterDrReduction(monster) | local drReduction = p._getMonsterDrReduction(monster) | ||
local maxHit = p._getMonsterMaxHit(monster) | local maxHit = p._getMonsterMaxHit(monster) | ||
if drReduction > 0 then | if drReduction > 0 then | ||
table.insert(tableBits, '||style="text-align:right" data-sort-value="'..maxHit..'"| -'..drReduction..'% DR') | |||
table.insert(tableBits, '||style="text-align:right"|'..Shared.formatnum(maxHit)) | |||
else | else | ||
table.insert(tableBits, '||style="text-align:right" colspan="2" data-sort-value="'..maxHit..'"|'..Shared.formatnum(maxHit)) | |||
end | end | ||
table.insert(tableBits, '||'..p._getMonsterStyleIcon({monster, nolink=true})) | |||
end | end | ||
table.insert(tableBits, '\r\n|}') | |||
return | |||
return table.concat(tableBits, '') | |||
end | end | ||
Line 1,218: | Line 1,327: | ||
--For Dungeons, go through and count how many of each monster are in the dungeon first | --For Dungeons, go through and count how many of each monster are in the dungeon first | ||
local monsterCounts = {} | local monsterCounts = {} | ||
local monsters = {} | |||
local hasBarrier = false | |||
for i, monsterID in ipairs(area.monsterIDs) do | for i, monsterID in ipairs(area.monsterIDs) do | ||
if monsterCounts[monsterID] == nil then | if monsterCounts[monsterID] == nil then | ||
Line 1,223: | Line 1,334: | ||
else | else | ||
monsterCounts[monsterID] = monsterCounts[monsterID] + 1 | monsterCounts[monsterID] = monsterCounts[monsterID] + 1 | ||
if monsterID ~= 'melvorF:RandomITM' and monsterID ~= 'melvorTotH:RandomSpiderLair' then | |||
monsters[monsterID] = p.getMonsterByID(monsterID) | |||
if not hasBarrier and p._getMonsterBarrier(monsters[monsterID]) > 0 then | |||
hasBarrier = true | |||
end | |||
end | |||
end | end | ||
end | end | ||
Line 1,230: | Line 1,347: | ||
-- Declare function for building table rows to avoid repeating code | -- Declare function for building table rows to avoid repeating code | ||
local buildRow = function(entityID, monsterCount, specialType) | local buildRow = function(entityID, monsterCount, specialType) | ||
local monIcon, monLevel, monHP, monMaxHit, monStyle, monCount, monDrReduce | local monIcon, monLevel, monHP, monMaxHit, monStyle, monCount, monDrReduce, monBarrier | ||
local monData = {} | local monData = {} | ||
if specialType ~= nil and Shared.contains({'Afflicted', 'Spider', 'SlayerArea'}, specialType) then | if specialType ~= nil and Shared.contains({'Afflicted', 'Spider', 'SlayerArea'}, specialType) then | ||
Line 1,237: | Line 1,354: | ||
local iconQ = Icons.Icon({'Into the Mist', notext=true, nolink=true, img='Question'}) | local iconQ = Icons.Icon({'Into the Mist', notext=true, nolink=true, img='Question'}) | ||
monIcon = Icons.Icon({'Into the Mist', 'Afflicted Monster', nolink=true, img='Question'}) | monIcon = Icons.Icon({'Into the Mist', 'Afflicted Monster', nolink=true, img='Question'}) | ||
monLevel, monHP, monMaxHit, monDrReduce, monStyle, monCount = iconQ, iconQ, iconQ, iconQ, iconQ, monsterCount | monLevel, monBarrier, monHP, monMaxHit, monDrReduce, monStyle, monCount = iconQ, iconQ, iconQ, iconQ, iconQ, iconQ, monsterCount | ||
elseif specialType == 'Spider' then | elseif specialType == 'Spider' then | ||
local iconQ = Icons.Icon({'', notext=true, nolink=true, img='Question'}) | local iconQ = Icons.Icon({'', notext=true, nolink=true, img='Question'}) | ||
Line 1,248: | Line 1,365: | ||
end | end | ||
monIcon = table.concat(monIconPart, '<br/>') | monIcon = table.concat(monIconPart, '<br/>') | ||
monLevel, monHP, monMaxHit, monDrReduce, monStyle, monCount = iconQ, iconQ, iconQ, iconQ, iconQ, monsterCount | monLevel, monBarrier, monHP, monMaxHit, monDrReduce, monStyle, monCount = iconQ, iconQ, iconQ, iconQ, iconQ, iconQ, monsterCount | ||
elseif specialType == 'SlayerArea' then | elseif specialType == 'SlayerArea' then | ||
-- entityID corresponds to a slayer area | -- entityID corresponds to a slayer area | ||
Line 1,254: | Line 1,371: | ||
monIcon = Icons.Icon({area.name, type='combatArea'}) .. ' Monsters' | monIcon = Icons.Icon({area.name, type='combatArea'}) .. ' Monsters' | ||
monLevel = {p.getLowHighStat(area.monsterIDs, function(monster) return p._getMonsterCombatLevel(monster) end)} | monLevel = {p.getLowHighStat(area.monsterIDs, function(monster) return p._getMonsterCombatLevel(monster) end)} | ||
if hasBarrier then | |||
monBarrier = {p.getLowHighStat(area.monsterIDs, function(monster) return p._getMonsterBarrier(monster) end)} | |||
end | |||
monHP = {p.getLowHighStat(area.monsterIDs, function(monster) return p._getMonsterHP(monster) end)} | monHP = {p.getLowHighStat(area.monsterIDs, function(monster) return p._getMonsterHP(monster) end)} | ||
local lowMaxHit, highMaxHit = p.getLowHighStat(area.monsterIDs, function(monster) return p._getMonsterMaxHit(monster) end) | local lowMaxHit, highMaxHit = p.getLowHighStat(area.monsterIDs, function(monster) return p._getMonsterMaxHit(monster) end) | ||
Line 1,267: | Line 1,387: | ||
monIcon = Icons.Icon({p.getMonsterName(monster), type='monster'}) | monIcon = Icons.Icon({p.getMonsterName(monster), type='monster'}) | ||
monLevel = p._getMonsterCombatLevel(monster) | monLevel = p._getMonsterCombatLevel(monster) | ||
if hasBarrier then | |||
monBarrier = p._getMonsterBarrier(monster) | |||
end | |||
monHP = p._getMonsterHP(monster) | monHP = p._getMonsterHP(monster) | ||
monDrReduce = p._getMonsterDrReduction(monster) | monDrReduce = p._getMonsterDrReduction(monster) | ||
Line 1,301: | Line 1,424: | ||
table.insert(resultPart, '\r\n|-\r\n| ' .. monIcon) | table.insert(resultPart, '\r\n|-\r\n| ' .. monIcon) | ||
table.insert(resultPart, '\r\n|style="text-align:right;" data-sort-value="' .. getValSort(monLevel) .. '"| ' .. getValText(monLevel)) | table.insert(resultPart, '\r\n|style="text-align:right;" data-sort-value="' .. getValSort(monLevel) .. '"| ' .. getValText(monLevel)) | ||
if hasBarrier then | |||
table.insert(resultPart, '\r\n|style="text-align:right;" data-sort-value="' .. getValSort(monBarrier) .. '"| ' .. getValText(monBarrier)) | |||
end | |||
table.insert(resultPart, '\r\n|style="text-align:right;" data-sort-value="' .. getValSort(monHP) .. '"| ' .. getValText(monHP)) | table.insert(resultPart, '\r\n|style="text-align:right;" data-sort-value="' .. getValSort(monHP) .. '"| ' .. getValText(monHP)) | ||
if type(monDrReduce) == 'number' and monDrReduce > 0 then | if type(monDrReduce) == 'number' and monDrReduce > 0 then | ||
Line 1,315: | Line 1,441: | ||
local returnPart = {} | local returnPart = {} | ||
table.insert(returnPart, '{| class="wikitable sortable"') | table.insert(returnPart, '{| class="wikitable sortable"') | ||
table.insert(returnPart, '\r\n! Name !! Combat Level !! Hitpoints !! colspan="2" | Max Hit !! [[Combat Triangle|Combat Style]] !! Count') | table.insert(returnPart, '\r\n! Name !! Combat Level ') | ||
if hasBarrier then | |||
table.insert(returnPart, '!! [[Barrier]] ') | |||
end | |||
table.insert(returnPart, '!! Hitpoints !! colspan="2" | Max Hit !! [[Combat Triangle|Combat Style]] !! Count') | |||
-- Special handing for Impending Darkness event | -- Special handing for Impending Darkness event | ||
-- TODO needs to be revised once there is a better understanding of how the event works | -- TODO needs to be revised once there is a better understanding of how the event works | ||
for i, monsterID in ipairs(area.monsterIDs) do | for i, monsterID in ipairs(area.monsterIDs) do | ||
if not Shared.contains(usedMonsters, monsterID) then | if not Shared.contains(usedMonsters, monsterID) then | ||
Line 1,333: | Line 1,456: | ||
table.insert(returnPart, buildRow(monsterID, monsterCounts[monsterID], 'Spider')) | table.insert(returnPart, buildRow(monsterID, monsterCounts[monsterID], 'Spider')) | ||
else | else | ||
table.insert(returnPart, buildRow(monsterID, monsterCounts[monsterID])) | table.insert(returnPart, buildRow(monsterID, monsterCounts[monsterID], hasBarrier)) | ||
end | end | ||
table.insert(usedMonsters, monsterID) | table.insert(usedMonsters, monsterID) | ||
Line 1,370: | Line 1,493: | ||
local lastID = '' | local lastID = '' | ||
local count = 0 | local count = 0 | ||
local monsterCounts = {} | local monsterCounts = {} |
edits