17,101
edits
Falterfire (talk | contribs) (added functions to get average GP for all monster drops and then made a table of that) |
(Implement getSlayerTierMonsterTable; Improve performance of getMonster, getSpecialAttack, getPassive) |
||
Line 23: | Line 23: | ||
--Make sure every monster has an ID, and account for the 1-based indexing of Lua | --Make sure every monster has an ID, and account for the 1-based indexing of Lua | ||
result.id = i - 1 | result.id = i - 1 | ||
break | |||
end | end | ||
end | end | ||
Line 42: | Line 43: | ||
--Make sure every attack has an ID, and account for the 1-based indexing of Lua | --Make sure every attack has an ID, and account for the 1-based indexing of Lua | ||
result.id = i - 1 | result.id = i - 1 | ||
break | |||
end | end | ||
end | end | ||
Line 59: | Line 61: | ||
--Make sure every passive has an ID, and account for the 1-based indexing of Lua | --Make sure every passive has an ID, and account for the 1-based indexing of Lua | ||
result.id = i - 1 | result.id = i - 1 | ||
break | |||
end | end | ||
end | end | ||
Line 77: | Line 80: | ||
if StatName == 'HP' then | if StatName == 'HP' then | ||
return p. | return p._getMonsterHP(monster) | ||
elseif StatName == 'maxHit' then | elseif StatName == 'maxHit' then | ||
return p. | return p._getMonsterMaxHit(monster) | ||
elseif StatName == 'accuracyRating' then | elseif StatName == 'accuracyRating' then | ||
return p. | return p._getMonsterAR(monster) | ||
elseif StatName == 'meleeEvasionRating' then | elseif StatName == 'meleeEvasionRating' then | ||
return p. | return p._getMonsterER({monster, 'Melee'}) | ||
elseif StatName == 'rangedEvasionRating' then | elseif StatName == 'rangedEvasionRating' then | ||
return p. | return p._getMonsterER({monster, 'Ranged'}) | ||
elseif StatName == 'magicEvasionRating' then | elseif StatName == 'magicEvasionRating' then | ||
return p. | return p._getMonsterER({monster, 'Magic'}) | ||
end | end | ||
Line 93: | Line 96: | ||
end | end | ||
function p. | function p._getMonsterStyleIcon(frame) | ||
local args = frame.args ~= nil and frame.args or frame | local args = frame.args ~= nil and frame.args or frame | ||
local | local monster = args[1] | ||
local notext = args.notext | local notext = args.notext | ||
local nolink = args.nolink | local nolink = args.nolink | ||
local iconText = '' | local iconText = '' | ||
Line 114: | Line 112: | ||
return iconText | return iconText | ||
end | |||
function p.getMonsterStyleIcon(frame) | |||
local args = frame.args ~= nil and frame.args or frame | |||
local MonsterName = args[1] | |||
local monster = p.getMonster(MonsterName) | |||
if monster == nil then | |||
return "ERROR: No monster with that name found" | |||
end | |||
args[1] = monster | |||
return p._getMonsterStyleIcon(args) | |||
end | |||
function p._getMonsterHP(monster) | |||
return monster.hitpoints * 10 | |||
end | end | ||
Line 120: | Line 135: | ||
local monster = p.getMonster(MonsterName) | local monster = p.getMonster(MonsterName) | ||
if monster ~= nil then | if monster ~= nil then | ||
return monster | return p._getMonsterHP(monster) | ||
else | else | ||
return "ERROR: No monster with that name found" | return "ERROR: No monster with that name found" | ||
end | end | ||
end | |||
function p._getMonsterAttackSpeed(monster) | |||
return monster.attackSpeed / 1000 | |||
end | end | ||
Line 130: | Line 149: | ||
local monster = p.getMonster(MonsterName) | local monster = p.getMonster(MonsterName) | ||
if monster ~= nil then | if monster ~= nil then | ||
return monster | return p._getMonsterAttackSpeed(monster) | ||
else | else | ||
return "ERROR: No monster with that name found" | return "ERROR: No monster with that name found" | ||
Line 161: | Line 180: | ||
end | end | ||
function p. | function p._getMonsterAR(monster) | ||
local effAttLvl = 0 | local effAttLvl = 0 | ||
local attBonus = 0 | local attBonus = 0 | ||
Line 187: | Line 199: | ||
end | end | ||
function p. | function p.getMonsterAR(frame) | ||
local | local MonsterName = frame.args ~= nil and frame.args[1] or frame | ||
local monster = p.getMonster(MonsterName) | local monster = p.getMonster(MonsterName) | ||
Line 196: | Line 206: | ||
return "ERROR: No monster with that name found" | return "ERROR: No monster with that name found" | ||
end | end | ||
return p._getMonsterAR(monster) | |||
end | |||
function p._getMonsterER(frame) | |||
local args = frame.args ~= nil and frame.args or frame | |||
local monster = args[1] | |||
local style = args[2] | |||
local effDefLvl = 0 | local effDefLvl = 0 | ||
local defBonus = 0 | local defBonus = 0 | ||
Line 212: | Line 230: | ||
end | end | ||
return effDefLvl * defBonus | return effDefLvl * defBonus | ||
end | |||
function p.getMonsterER(frame) | |||
local args = frame.args ~= nil and frame.args or frame | |||
local MonsterName = args[1] | |||
local style = args[2] | |||
local monster = p.getMonster(MonsterName) | |||
if monster == nil then | |||
return "ERROR: No monster with that name found" | |||
end | |||
return p._getMonsterER({monster, style}) | |||
end | end | ||
Line 220: | Line 251: | ||
for i, area in Shared.skpairs(areaList) do | for i, area in Shared.skpairs(areaList) do | ||
if area.type == 'dungeon' then | if area.type == 'dungeon' then | ||
dunCount = dunCount + 1 | dunCount = dunCount + 1 | ||
Line 262: | Line 292: | ||
end | end | ||
function p. | function p._getMonsterMaxHit(monster) | ||
local normalChance = 100 | local normalChance = 100 | ||
local specialMaxHit = 0 | local specialMaxHit = 0 | ||
local normalMaxHit = p. | local normalMaxHit = p._getMonsterBaseMaxHit(monster) | ||
if monster.hasSpecialAttack then | if monster.hasSpecialAttack then | ||
for i, specID in pairs(monster.specialAttackID) do | for i, specID in pairs(monster.specialAttackID) do | ||
Line 301: | Line 324: | ||
end | end | ||
function p. | function p.getMonsterMaxHit(frame) | ||
local MonsterName = frame.args ~= nil and frame.args[1] or frame | local MonsterName = frame.args ~= nil and frame.args[1] or frame | ||
local monster = p.getMonster(MonsterName) | local monster = p.getMonster(MonsterName) | ||
Line 308: | Line 331: | ||
return "ERROR: No monster with that name found" | return "ERROR: No monster with that name found" | ||
end | end | ||
return p._getMonsterMaxHit(monster) | |||
end | |||
function p._getMonsterBaseMaxHit(monster) | |||
local effStrLvl = 0 | local effStrLvl = 0 | ||
local strBonus = 0 | local strBonus = 0 | ||
Line 331: | Line 358: | ||
--Should only get here for Melee/Ranged, which use functionally the same damage formula | --Should only get here for Melee/Ranged, which use functionally the same damage formula | ||
return math.floor(10 * (1.3 + (effStrLvl/10) + (strBonus / 80) + ((effStrLvl * strBonus) / 640))) | return math.floor(10 * (1.3 + (effStrLvl/10) + (strBonus / 80) + ((effStrLvl * strBonus) / 640))) | ||
end | |||
function p.getMonsterBaseMaxHit(frame) | |||
local MonsterName = frame.args ~= nil and frame.args[1] or frame | |||
local monster = p.getMonster(MonsterName) | |||
if monster == nil then | |||
return "ERROR: No monster with that name found" | |||
end | |||
return p._getMonsterBaseMaxHit(monster) | |||
end | end | ||
Line 413: | Line 451: | ||
local result = '[[Category:Monsters]]' | local result = '[[Category:Monsters]]' | ||
if monster.attackType == Constants.attackType.Melee then | if monster.attackType == Constants.attackType.Melee then | ||
result = result..'[[Category:Melee Monsters]]' | result = result..'[[Category:Melee Monsters]]' | ||
Line 425: | Line 463: | ||
result = result..'[[Category:Monsters with Special Attacks]]' | result = result..'[[Category:Monsters with Special Attacks]]' | ||
end | end | ||
if monster.isBoss then | if monster.isBoss then | ||
result = result..'[[Category:Bosses]]' | result = result..'[[Category:Bosses]]' | ||
Line 486: | Line 524: | ||
result = result..'||style="text-align:right" data-sort-value="'..maxQty..'"|' | result = result..'||style="text-align:right" data-sort-value="'..maxQty..'"|' | ||
if maxQty > 1 then | if maxQty > 1 then | ||
result = result.. '1 - ' | result = result.. '1 - ' | ||
end | end | ||
result = result..Shared.formatnum(row[3]) | result = result..Shared.formatnum(row[3]) | ||
Line 573: | Line 611: | ||
result = result..'||style="text-align:right" data-sort-value="'..qty..'"|' | result = result..'||style="text-align:right" data-sort-value="'..qty..'"|' | ||
if qty > 1 then | if qty > 1 then | ||
result = result.. '1 - ' | result = result.. '1 - ' | ||
end | end | ||
result = result..Shared.formatnum(qty) | result = result..Shared.formatnum(qty) | ||
Line 634: | Line 672: | ||
local monsterCounts = {} | local monsterCounts = {} | ||
for i, monsterID in pairs(area.monsters) do | for i, monsterID in pairs(area.monsters) do | ||
if monsterCounts[monsterID] == nil then | if monsterCounts[monsterID] == nil then | ||
monsterCounts[monsterID] = 1 | monsterCounts[monsterID] = 1 | ||
else | else | ||
Line 816: | Line 854: | ||
lootValue = lootValue + (dropChance * 0.01 * itemPrice * ((1 + maxQty) / 2)) | lootValue = lootValue + (dropChance * 0.01 * itemPrice * ((1 + maxQty) / 2)) | ||
end | end | ||
totalGP = totalGP + lootValue | totalGP = totalGP + lootValue | ||
end | end | ||
Line 830: | Line 868: | ||
return "ERROR: No monster with that name found" | return "ERROR: No monster with that name found" | ||
end | end | ||
return p._getMonsterAverageGP(monster) | return p._getMonsterAverageGP(monster) | ||
end | end | ||
Line 848: | Line 886: | ||
result = result..'\r\n|}' | result = result..'\r\n|}' | ||
return result | return result | ||
end | |||
function p.getSlayerTierMonsterTable(tier) | |||
-- Input validation | |||
local SlayerTiers = Constants.Slayer.Tiers | |||
if tier == nil then | |||
return "ERROR: No tier specified" | |||
elseif type(tier) ~= "number" then | |||
return "ERROR: Tier must be an integer" | |||
else | |||
tier = math.floor(tier) | |||
local tierCount = Shared.tableCount(SlayerTiers) - 1 | |||
if tier < 0 or tier > tierCount then | |||
return "ERROR: Tier must be between 0 and " .. tierCount | |||
end | |||
tier = tier + 1 | |||
end | |||
-- Obtain required tier details | |||
local minLevel, maxLevel = SlayerTiers[tier].minLevel, SlayerTiers[tier].maxLevel | |||
if maxLevel < 0 then | |||
maxLevel = nil | |||
end | |||
-- Build list of monster IDs | |||
-- hiddenMonsterIDs below should be changed to {} when 0.19 releases | |||
local hiddenMonsterIDs = {139} | |||
local monsterIDs = {} | |||
for i, monster in Shared.skpairs(MonsterData.Monsters) do | |||
if monster.canSlayer and not Shared.contains(hiddenMonsterIDs, i - 1) then | |||
local cmbLevel = p._getMonsterCombatLevel(monster) | |||
if cmbLevel >= minLevel and (maxLevel == nil or cmbLevel <= maxLevel) then | |||
table.insert(monsterIDs, i - 1) | |||
end | |||
end | |||
end | |||
if Shared.tableCount(monsterIDs) == 0 then | |||
-- Somehow no monsters are in the tier, return nothing | |||
return '' | |||
else | |||
local tableTxt = '{| class="wikitable sortable stickyHeader"' | |||
-- First header row | |||
tableTxt = tableTxt .. '\r\n|- class="headerRow-0"\r\n! colspan="5" | !! colspan="4" |Offensive Stats !! colspan="3" |Evasion Rating !! colspan="4" |' | |||
-- Second header row | |||
tableTxt = tableTxt .. '\r\n|- class="headerRow-1"\r\n!Monster !!Name !!ID !!Combat Level ' | |||
tableTxt = tableTxt .. '!!style="padding:0 1em 0 0"|' .. Icons.Icon({'Hitpoints', type='skill'}) | |||
tableTxt = tableTxt .. '!!Attack Type !!Attack Speed (s) !!Max Hit !!Accuracy ' | |||
tableTxt = tableTxt .. '!!style="padding:0 1em 0 0"|' .. Icons.Icon({'Defence', type='skill', notext=true}) | |||
tableTxt = tableTxt .. '!!style="padding:0 1em 0 0"|' .. Icons.Icon({'Ranged', type='skill', notext=true}) | |||
tableTxt = tableTxt .. '!!style="padding:0 1em 0 0"|' .. Icons.Icon({'Magic', type='skill', notext=true}) | |||
tableTxt = tableTxt .. '!!Drop Chance !!Coins !!Bones !!Locations' | |||
-- Generate row per monster | |||
for i, monsterID in Shared.skpairs(monsterIDs) do | |||
local monster = p.getMonsterByID(monsterID) | |||
local cmbLevel = p._getMonsterCombatLevel(monster) | |||
local atkSpeed = p._getMonsterAttackSpeed(monster) | |||
local maxHit = p._getMonsterMaxHit(monster) | |||
local accR = p._getMonsterAR(monster) | |||
local evaR = {p._getMonsterER({monster, "Melee"}), p._getMonsterER({monster, "Ranged"}), p._getMonsterER({monster, "Magic"})} | |||
local lootChance = monster.lootChance ~= nil and monster.lootChance or 100 | |||
local gpRange = {0, 0} | |||
if monster.dropCoins ~= nil and monster.dropCoins[2] > 1 then | |||
gpRange = {monster.dropCoins[1], monster.dropCoins[2] - 1} | |||
end | |||
local gpTxt = nil | |||
if gpRange[1] == gpRange[2] then | |||
gpTxt = gpRange[1] | |||
else | |||
gpTxt = gpRange[1] .. ' - ' .. gpRange[2] | |||
end | |||
local boneTxt = 'None' | |||
if monster.bones ~= nil then | |||
local bones = Items.getItemByID(monster.bones) | |||
boneTxt = Icons.Icon({bones.name, type='item', notext=true}) | |||
end | |||
tableTxt = tableTxt .. '\r\n|-\r\n|style="text-align: left;" |' .. Icons.Icon({monster.name, type='monster', size=50, notext=true}) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:left" |[[' .. monster.name .. ']]' | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" |' .. monsterID | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. cmbLevel .. '" |' .. Shared.formatnum(cmbLevel) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. monster.hitpoints .. '" |' .. Shared.formatnum(p._getMonsterHP(monster)) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right;white-space:nowrap" |' .. p._getMonsterStyleIcon({monster, nolink='true'}) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. atkSpeed .. '" |' .. Shared.round(atkSpeed, 1, 1) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. maxHit .. '" |' .. Shared.formatnum(maxHit) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. accR .. '" |' .. Shared.formatnum(accR) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. evaR[1] .. '" |' .. Shared.formatnum(evaR[1]) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. evaR[2] .. '" |' .. Shared.formatnum(evaR[2]) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. evaR[3] .. '" |' .. Shared.formatnum(evaR[3]) | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. lootChance .. '" |' .. lootChance .. '%' | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right" data-sort-value="' .. (gpRange[1] + gpRange[2]) / 2 .. '" |' .. gpTxt | |||
tableTxt = tableTxt .. '\r\n|style="text-align:center" |' .. boneTxt | |||
tableTxt = tableTxt .. '\r\n|style="text-align:right;white-space:nowrap" |' .. p._getMonsterAreas(monster) | |||
end | |||
tableTxt = tableTxt .. "\r\n|}" | |||
return tableTxt | |||
end | |||
end | end | ||
return p | return p |