Module:Monsters: Difference between revisions

Add column name for average healing
(Added DR to the full monster table because fuckit, never enough columns)
(Add column name for average healing)
(26 intermediate revisions by 5 users not shown)
Line 10: Line 10:


function p.getMonster(name)
function p.getMonster(name)
    return GameData.getEntityByName('monsters', name)
if name == 'Earth Golem (AoD)' then
-- Special case for ambiguous monster name
return p.getMonsterByID('melvorAoD:EarthGolem')
else
    return GameData.getEntityByName('monsters', name)
    end
end
end


function p.getMonsterByID(ID)
function p.getMonsterByID(ID)
     return GameData.getEntityByID('monsters', ID)
     return GameData.getEntityByID('monsters', ID)
end
function p.getMonsterName(monster)
if monster.id == 'melvorAoD:EarthGolem' then
-- Special case for ambiguous monster name
return 'Earth Golem (AoD)'
else
return monster.name
end
end
end


Line 39: 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 65: Line 81:
local monster = p.getMonster(MonsterName)
local monster = p.getMonster(MonsterName)
if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 85: Line 101:
iconText = Icons.Icon({'Magic', type='skill', notext=notext, nolink=nolink})
iconText = Icons.Icon({'Magic', type='skill', notext=notext, nolink=nolink})
elseif monster.attackType == 'random' then
elseif monster.attackType == 'random' then
iconText = Icons.Icon({monster.name, notext=notext, nolink=nolink, img='Question'})
iconText = Icons.Icon({p.getMonsterName(monster), notext=notext, nolink=nolink, img='Question'})
end
end


Line 97: Line 113:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 106: 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 114: Line 139:
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
else
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
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
return Shared.printError('No monster with that name found')
end
end
end
end
Line 124: Line 169:
return p._getMonsterHP(monster)
return p._getMonsterHP(monster)
else
else
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end
end
end
Line 142: Line 187:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 177: Line 222:
return p._getMonsterAttackSpeed(monster)
return p._getMonsterAttackSpeed(monster)
else
else
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end
end
end
Line 194: Line 239:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 217: Line 262:
bonus = p.getEquipmentStat(monster, 'stabAttackBonus')
bonus = p.getEquipmentStat(monster, 'stabAttackBonus')
else
else
return "ERROR: This monster has an invalid attack type somehow[[Category:Pages with script errors]]"
return Shared.printError('This monster has an invalid attack type somehow')
end
end


Line 228: Line 273:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 248: Line 293:
bonus = p.getEquipmentStat(monster, 'magicDefenceBonus')
bonus = p.getEquipmentStat(monster, 'magicDefenceBonus')
else
else
return "ERROR: Must choose Melee, Ranged, or Magic[[Category:Pages with script errors]]"
return Shared.printError('Must choose Melee, Ranged, or Magic')
end
end


Line 261: Line 306:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 303: Line 348:


if monster == nil then
if monster == nil then
return "ERROR: No monster with name "..monsterName.." found[[Category:Pages with script errors]]"
return Shared.printError('No monster with name ' .. monsterName .. ' found')
end
end


Line 337: Line 382:


if monster == nil then
if monster == nil then
return "ERROR: No monster with name "..monsterName.." found[[Category:Pages with script errors]]"
return Shared.printError('No monster with name ' .. monsterName .. ' found')
end
end


Line 343: Line 388:
end
end


function p.getSpecAttackMaxHit(specAttack, normalMaxHit, monsterDR)
function p.getSpecAttackMaxHit(specAttack, normalMaxHit, monster)
local result = 0
local bestHit = 0
for i, dmg in pairs(specAttack.damage) do
for i, dmg in pairs(specAttack.damage) do
local thisHit = 0
if dmg.damageType == 'Normal' then
if dmg.damageType == 'Normal' then
--Account for special attacks that include a normal attack hit
--Account for special attacks that include a normal attack hit
local result = normalMaxHit
thisHit = normalMaxHit
if dmg.amplitude ~= nil then
if dmg.amplitude ~= nil then
result = result * (dmg.amplitude / 100)
thisHit = thisHit * (dmg.amplitude / 100)
end
end
return result
elseif dmg.maxRoll == 'Fixed' then
elseif dmg.maxRoll == 'Fixed' then
result = dmg.maxPercent * 10
thisHit = dmg.maxPercent * 10
elseif dmg.maxRoll == 'MaxHit' then
elseif dmg.maxRoll == 'MaxHit' then
if dmg.character == 'Target' then
if dmg.character == 'Target' then
--Confusion applied damage based on the player's max hit. Gonna just ignore that one
--Confusion applied damage based on the player's max hit. Gonna just ignore that one
result = 0
thisHit = 0
else
else
result = dmg.maxPercent * normalMaxHit * 0.01
thisHit = dmg.maxPercent * normalMaxHit * 0.01
end
end
elseif Shared.contains(dmg.maxRoll, "Fixed100") then
elseif Shared.contains(dmg.maxRoll, "Fixed100") then
--Handles attacks that are doubled when conditions are met like Trogark's double damage if the player is burning
--Handles attacks that are doubled when conditions are met like Trogark's double damage if the player is burning
result = dmg.maxPercent * 20
thisHit = dmg.maxPercent * 20
elseif dmg.maxRoll == 'MaxHitScaledByHP2x' then
elseif dmg.maxRoll == 'MaxHitScaledByHP2x' then
result = normalMaxHit * 2
thisHit = normalMaxHit * 2
elseif dmg.maxRoll == 'PoisonMax35' then
elseif dmg.maxRoll == 'PoisonMax35' then
result = normalMaxHit * 1.35
thisHit = normalMaxHit * 1.35
elseif dmg.maxRoll == "MaxHitDR" then
elseif dmg.maxRoll == "MaxHitDR" then
result = normalMaxHit * dmg.maxPercent * 0.01 * (1 + monsterDR * 0.01)
local monsterDR = 0
if monster ~= nil then
monsterDR = p._getMonsterStat(monster, 'damageReduction')
end
thisHit = normalMaxHit * dmg.maxPercent * 0.01 * (1 + monsterDR * 0.01)
elseif Shared.contains({'Bleeding', 'Poisoned'}, dmg.maxRoll) then
elseif Shared.contains({'Bleeding', 'Poisoned'}, dmg.maxRoll) then
-- TODO: This is limited in that there is no verification that bleed/poison
-- TODO: This is limited in that there is no verification that bleed/poison
-- can be applied to the target, it is assumed that it can and so this applies
-- can be applied to the target, it is assumed that it can and so this applies
result = result + dmg.maxPercent * 10
thisHit = thisHit + dmg.maxPercent * 10
end
if thisHit > bestHit then
bestHit = thisHit
end
end
end
end
return result
return bestHit
end
end


function p.canSpecAttackApplyEffect(specAttack, effectType)
function p.canSpecAttackApplyEffect(specAttack, effectType)
for i, effect in pairs(specAttack.prehitEffects) do
local effectKeys = { 'prehitEffects', 'onhitEffects' }
if effect.type == effectType then
for i, effectKey in ipairs(effectKeys) do
            return true
if type(specAttack[effectKey]) == 'table' then
for j, effect in pairs(specAttack[effectKey]) do
if effect.type == effectType or p.canModifiersApplyEffect(effect.modifiers, effectType) then
return true
end
end
end
end
end
end
return false
end


for i, effect in pairs(specAttack.onhitEffects) do
function p.canModifiersApplyEffect(modifiers, effectType)
if effect.type == effectType then
-- List of modifiers which can result in the application of status effects
return true
local statusModsAll = {
["Stun"] = { 'increasedGlobalStunChance', 'increasedMeleeStunChance' },
["Sleep"] = { 'increasedGlobalSleepChance' },
["Poison"] = { 'increasedChanceToApplyPoison' },
["Slow"] = { 'increased15SlowStunChance2Turns', 'increased30Slow5TurnsChance' }
}
 
local statusMods = statusModsAll[effectType]
if statusMods ~= nil and type(modifiers) == 'table' then
for modName, modMagnitude in pairs(modifiers) do
if Shared.contains(statusMods, modName) then
return true
end
end
end
end
end
Line 404: Line 476:
end
end


local normalChance = 100
-- Damage adjustments are defined as follows:
local specialMaxHit = 0
-- multiplier - Damage from modifier 'increasedDamageTaken' & additional damage while
local normalMaxHit = p._getMonsterBaseMaxHit(monster)
-- stunned, asleep, or poisoned. Defined by in-game function
local hasActiveBuffSpec = false
-- getDamageModifiers(). Applies after other percentage of flat adjustments.
local damageMultiplier = 1
-- percent - Percentage adjustments to the max hit. Applies before flat & multiplier
if monster.specialAttacks ~= nil then
-- adjustments.
local canStun, canSleep = false, false
-- flat - Flat adjustments to the max hit. Applies after percent adjustments, and
for i, specAttackID in pairs(monster.specialAttacks) do
-- after multiplier adjustments.
            local specAttack = GameData.getEntityByID('attacks', specAttackID)
local dmgAdjust = { ["percent"] = 100, ["flat"] = 0, ["multiplier"] = 100 }
if monster.overrideSpecialChances ~= nil then
-- Check passives & effects that apply pre or on hit for damage modifiers
normalChance = normalChance - monster.overrideSpecialChances[i]
local dmgMods = {
else
-- List of modifiers which affect damage dealt, and whether they are percentage or flat adjustments
normalChance = normalChance - specAttack.defaultChance
["increasedDamageTaken"] = { type = 'multiplier', mult = 1 },
["increasedMaxHitPercent"] = { type = 'percent', mult = 1 },
["increasedMeleeMaxHit"] = { type = 'percent', mult = 1 },
["increasedRangedMaxHit"] = { type = 'percent', mult = 1 },
["increasedMagicMaxHit"] = { type = 'percent', mult = 1 },
["increasedMaxHitFlat"] = { type = 'flat', mult = 10 },
["increasedMeleeMaxHitFlat"] = { type = 'flat', mult = 10 },
["increasedRangedMaxHitFlat"] = { type = 'flat', mult = 10 },
["increasedMagicMaxHitFlat"] = { type = 'flat', mult = 10 },
-- Rage: +2% max hit per stack, maximum of 10 stacks
["increasedRage"] = { type = 'percent', mult = 1, magnitude = 2, maxStacks = 10 },
-- Dark Blade: +1% max hit per successful hit, maximum of 30 stacks
["increasedChanceDarkBlade"] = { type = 'percent', mult = 1, magnitude = 1, maxStacks = 30 },
-- Growing Madness/Moment in Time/Reign Over Time: +2% max hit per stack, maximum of 25 stacks
["growingMadnessPassive"] = { type = 'percent', mult = 1, magnitude = 2, maxStacks = 25 },
["momentInTimePassive"] = { type = 'percent', mult = 1, magnitude = 2, maxStacks = 25 },
["reignOverTimePassive"] = { type = 'percent', mult = 1, magnitude = 2, maxStacks = 25 }
}
local effectKeys = { 'prehitEffects', 'onhitEffects' }
local dmgStatuses = {
-- List of status effects which can affect damage dealt
["Stun"] = { type = 'multiplier', magnitude = 30 },
["Sleep"] = { type = 'multiplier', magnitude = 20 }
}
local canApplyStatus = {}
-- Initialize table
for statusName, def in pairs(dmgStatuses) do
canApplyStatus[statusName] = false
end
 
local adjustForMod = function(mod, modMagnitude, effect)
local magnitude = mod.magnitude or modMagnitude
local maxStacks = mod.maxStacks or (effect ~= nil and effect.maxStacks) or 1
dmgAdjust[mod.type] = dmgAdjust[mod.type] + magnitude * mod.mult * maxStacks
end
 
local adjustForCurse = function(curseID, effect)
local curse = Magic.getSpellByID(curseID, 'curse')
if type(curse) == 'table' and type(curse.targetModifiers) == 'table' then
for modName, modMagnitude in pairs(curse.targetModifiers) do
local mod = dmgMods[modName]
if mod ~= nil then
-- The modifier is one which affects damage dealt
adjustForMod(mod, modMagnitude, effect)
end
end
end
local thisMax = p.getSpecAttackMaxHit(specAttack, normalMaxHit, p._getMonsterStat(monster, 'damageReduction'))
end
if not canStun and p.canSpecAttackApplyEffect(specAttack, 'Stun') then canStun = true end
end
if not canSleep and p.canSpecAttackApplyEffect(specAttack, 'Sleep') then canSleep = true end


if thisMax > specialMaxHit then specialMaxHit = thisMax end
-- Check monster passives for modifiers which affect damage dealt, and alo if any modifiers
if Shared.contains(string.upper(specAttack.description), 'NORMAL ATTACK INSTEAD') then  
-- present can apply stun or sleep
hasActiveBuffSpec = true  
if monster ~= nil and type(monster.passives) ~= nil then
for i, passiveID in ipairs(monster.passives) do
local passive = p.getPassiveByID(passiveID)
if passive ~= nil and type(passive.modifiers) == 'table' then
for modName, modMagnitude in pairs(passive.modifiers) do
local mod = dmgMods[modName]
if modName == 'applyRandomCurseOnSpawn' then
-- Special case in which the enemy can apply a random curse. Currently
-- Anguish III is the curse with the highest +% damage taken, so use this.
adjustForCurse('melvorF:AnguishIII')
elseif mod ~= nil then
-- The modifier is one which affects damage dealt
adjustForMod(mod, modMagnitude)
end
end
-- Check for application of relevant status effects
if doStuns then
for statusName, statusDef in pairs(dmgStatuses) do
if not canApplyStatus[statusName] and p.canModifiersApplyEffect(passive.modifiers, statusName) then
canApplyStatus[statusName] = true
end
end
end
end
end
end
end
if canSleep and doStuns then damageMultiplier = damageMultiplier * 1.2 end
if canStun and doStuns then damageMultiplier = damageMultiplier * 1.3 end
end
end
--Ensure that if the monster never does a normal attack, the normal max hit is irrelevant
if normalChance == 0 and not hasActiveBuffSpec then normalMaxHit = 0 end
return math.floor(math.max(specialMaxHit, normalMaxHit) * damageMultiplier)
end


function p.getMonsterMaxHit(frame)
local normalChance = 100
local MonsterName = frame.args ~= nil and frame.args[1] or frame
local specialMaxHit = 0
local doStuns = frame.args ~= nil and frame.args[2] or true
local normalMaxHit = p._getMonsterBaseMaxHit(monster)
local monster = p.getMonster(MonsterName)
local hasActiveBuffSpec = false
 
if monster.specialAttacks ~= nil then
if monster == nil then
for i, specAttackID in pairs(monster.specialAttacks) do
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
            local specAttack = GameData.getEntityByID('attacks', specAttackID)
end
for i, effectKey in ipairs(effectKeys) do
if type(specAttack[effectKey]) == 'table' then
for j, effect in ipairs(specAttack[effectKey]) do
local countsOnPlayer = (effect.countsOn == nil or effect.countsOn == 'Attacker')
if countsOnPlayer then
-- Check for pre or on hit effects for modifiers which affect damage dealt
if type(effect.modifiers) == 'table' then
for modName, modMagnitude in pairs(effect.modifiers) do
local mod = dmgMods[modName]
if mod ~= nil then
-- The modifier is one which affects damage dealt
adjustForMod(mod, modMagnitude, effect)
end
end
end
-- Check for curses which may cause the player to incur additional damage
if effect.effectType == 'Curse' then
-- If isRandom is true then a random curse is selected. Currently
-- Anguish III is the curse with the highest +% damage taken, so
-- use this.
local curseID = (effect.isRandom and 'melvorF:AnguishIII') or effect.curse
if curseID ~= nil then
adjustForCurse(curseID, effect)
end
end
end
end
end
end
 
if monster.overrideSpecialChances ~= nil then
normalChance = normalChance - monster.overrideSpecialChances[i]
else
normalChance = normalChance - specAttack.defaultChance
end
-- Check for application of relevant status effects
if doStuns then
for statusName, statusDef in pairs(dmgStatuses) do
if not canApplyStatus[statusName] and p.canSpecAttackApplyEffect(specAttack, statusName) then
canApplyStatus[statusName] = true
end
end
end


return p._getMonsterMaxHit(monster, doStuns)
local thisMax = p.getSpecAttackMaxHit(specAttack, normalMaxHit, monster)
end
if thisMax > specialMaxHit then specialMaxHit = thisMax end
if Shared.contains(string.upper(specAttack.description), 'NORMAL ATTACK INSTEAD') then
hasActiveBuffSpec = true
end
end


function p._getMonsterBaseMaxHit(monster)
if doStuns then
--8/27/21 - Now references p.calculateStandardMaxHit for Melee & Ranged
for statusName, statusDef in pairs(dmgStatuses) do
local result = 0
if canApplyStatus[statusName] then
local baseLevel = 0
local adjType = statusDef.type
local bonus = 0
dmgAdjust[adjType] = dmgAdjust[adjType] + statusDef.magnitude
end
end
end
end
--Ensure that if the monster never does a normal attack, the normal max hit is irrelevant
if normalChance == 0 and not hasActiveBuffSpec then normalMaxHit = 0 end
local maxHit = math.floor(math.max(specialMaxHit, normalMaxHit) * dmgAdjust.percent / 100) + dmgAdjust.flat
return math.floor(maxHit * dmgAdjust.multiplier / 100)
end
 
function p.getMonsterMaxHit(frame)
local MonsterName = frame.args ~= nil and frame.args[1] or frame
local doStuns = frame.args ~= nil and frame.args[2] or true
local monster = p.getMonster(MonsterName)
 
if monster == nil then
return Shared.printError('No monster with that name found')
end
 
return p._getMonsterMaxHit(monster, doStuns)
end
 
function p._getMonsterBaseMaxHit(monster)
--8/27/21 - Now references p.calculateStandardMaxHit for Melee & Ranged
local result = 0
local baseLevel = 0
local bonus = 0
if monster.attackType == 'melee' then
if monster.attackType == 'melee' then
baseLevel = p._getMonsterLevel(monster, 'Strength')
baseLevel = p._getMonsterLevel(monster, 'Strength')
Line 504: Line 708:
result = max
result = max
else
else
return "ERROR: This monster has an invalid attack type somehow[[Category:Pages with script errors]]"
return Shared.printError('This monster has an invalid attack type somehow')
end
end


Line 515: Line 719:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 526: Line 730:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 544: Line 748:
local buffAttacks = {}
local buffAttacks = {}
local hasActiveBuffSpec = false
local hasActiveBuffSpec = false
local isNormalAttackRelevant = false


local normalAttackChance = 100
local normalAttackChance = 100
Line 571: Line 776:
if Shared.contains(string.upper(specAttack.description), 'NORMAL ATTACK INSTEAD') then
if Shared.contains(string.upper(specAttack.description), 'NORMAL ATTACK INSTEAD') then
table.insert(buffAttacks, specAttack.name)
table.insert(buffAttacks, specAttack.name)
hasActiveBuffSpec = true  
hasActiveBuffSpec = true
isNormalAttackRelevant = true
end
if not isNormalAttackRelevant and type(specAttack.damage) == 'table' then
-- Determine if the special attack uses normal damage in some form
for j, dmgData in ipairs(specAttack.damage) do
if dmgData.damageType == 'Normal' then
isNormalAttackRelevant = true
break
end
end
end
end
end
end
end
end
if normalAttackChance == 100 then
result = iconText..' 1 - '..p._getMonsterBaseMaxHit(monster)..' '..typeText..' Damage'
if isNormalAttackRelevant or normalAttackChance > 0 then
elseif normalAttackChance > 0 then
--Reformatting slightly - If there are any special attacks, specifically label the Normal Attack
result = '* '..normalAttackChance..'% '..iconText..' 1 - '..p.getMonsterBaseMaxHit(frame)..' '..typeText..' Damage'..result
elseif hasActiveBuffSpec then
local normalDmgText = ' 1 - '..Shared.formatnum(p._getMonsterBaseMaxHit(monster))..' '..typeText..' Damage'
--If the monster normally has a 0% chance of doing a normal attack but some special attacks can't be repeated, include it
if normalAttackChance > 0 and normalAttackChance < 100 then
--(With a note about when it does it)
normalDmgText = normalAttackChance .. '% ' ..iconText..' Normal Attack\r\n** '..normalDmgText
result = '* '..iconText..' 1 - '..p._getMonsterBaseMaxHit(monster)..' '..typeText..' Damage (Instead of repeating '..table.concat(buffAttacks, ' or ')..' while the effect is already active)'..result
elseif hasActiveBuffSpec and normalAttackChance == 0 then
--If the monster normally has a 0% chance of doing a normal attack but some special attacks can't be repeated, include it
--(With a note about when it does it)
normalDmgText = iconText..' Normal Attack\r\n** '..normalDmgText .. ' (Instead of repeating '..table.concat(buffAttacks, ' or ')..' while the effect is already active)'
end
result = '* ' .. normalDmgText .. result
end
end


Line 612: Line 832:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end
Line 623: Line 843:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 642: Line 862:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 664: 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 671: Line 912:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 712: Line 953:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 719: 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 754: Line 1,005:


--Sort the loot table by weight in descending order
--Sort the loot table by weight in descending order
table.sort(monster.lootTable, function(a, b) return a.weight > b.weight end)
local lootTable = Shared.shallowClone(monster.lootTable)
for i, row in ipairs(monster.lootTable) do
table.sort(lootTable, function(a, b)
if a.weight == b.weight then
local aItem, bItem = Items.getItemByID(a.itemID), Items.getItemByID(b.itemID)
if aItem ~= nil and bItem ~= nil then
return aItem.name < bItem.name
else
return a.itemID < b.itemID
end
else
return a.weight > b.weight
end
end)
for i, row in ipairs(lootTable) do
local thisItem = Items.getItemByID(row.itemID)
local thisItem = Items.getItemByID(row.itemID)
Line 816: 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 827: Line 1,093:
function p._getMonsterLootValue(monster)
function p._getMonsterLootValue(monster)
if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 890: Line 1,156:
if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end
if item == nil then
if item == nil then
return "ERROR: No item with that name found[[Category:Pages with script errors]]"
return Shared.printError('No item with that name found')
end
end
Line 922: Line 1,188:


if chest == nil then
if chest == nil then
return "ERROR: No item named "..chestName..' found[[Category:Pages with script errors]]'
return Shared.printError('No item named ' .. chestName .. ' found')
end
end
local result = ''
local result = ''


if chest.dropTable == nil then
if chest.dropTable == nil then
return "ERROR: "..chestName.." does not have a drop table[[Category:Pages with script errors]]"
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 pairs(chest.dropTable) do
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)
for i, row in ipairs(chest.dropTable) do
table.insert(chestDrops, row)
end
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)
 
if row.minQuantity < row.maxQuantity then
result = result .. Shared.formatnum(row.minQuantity) .. ' - ' .. Shared.formatnum(row.maxQuantity)
else
result = result .. Shared.formatnum(row.minQuantity)
end


local dropChance = (row.weight / totalWt) * 100
local dropChance = (row.weight / totalWt) * 100
Line 968: 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 980: Line 1,269:
local area = Areas.getArea(areaName)
local area = Areas.getArea(areaName)
if area == nil then
if area == nil then
return "ERROR: Could not find an area named "..areaName..'[[Category:Pages with script errors]]'
return Shared.printError('Could not find an area named ' .. areaName)
end
end


Line 986: Line 1,275:
return p.getDungeonMonsterTable(frame)
return p.getDungeonMonsterTable(frame)
end
end
 
local tableTxt = '{| class="wikitable sortable"'
tableTxt = tableTxt..'\r\n! Name !! Combat Level !! Hitpoints !! colspan=2| Max Hit !! [[Combat Triangle|Combat Style]]'
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)
tableTxt = tableTxt..'\r\n|-\r\n|'..Icons.Icon({monster.name, type='monster'})
if not hasBarrier and p._getMonsterBarrier(monster) > 0 then
tableTxt = tableTxt..'||'..p._getMonsterCombatLevel(monster)
hasBarrier = true
tableTxt = tableTxt..'||'..Shared.formatnum(p._getMonsterHP(monster))
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
tableTxt = tableTxt..'||style="text-align:right" data-sort-value="'..maxHit..'"| -'..drReduction..'% DR'
table.insert(tableBits, '||style="text-align:right" data-sort-value="'..maxHit..'"| -'..drReduction..'% DR')
tableTxt = tableTxt..'||style="text-align:right"|'..Shared.formatnum(maxHit)
table.insert(tableBits, '||style="text-align:right"|'..Shared.formatnum(maxHit))
else
else
tableTxt = tableTxt..'||style="text-align:right" colspan="2" data-sort-value="'..maxHit..'"|'..Shared.formatnum(maxHit)
table.insert(tableBits, '||style="text-align:right" colspan="2" data-sort-value="'..maxHit..'"|'..Shared.formatnum(maxHit))
end
end
tableTxt = tableTxt..'||'..p._getMonsterStyleIcon({monster, nolink=true})
table.insert(tableBits, '||'..p._getMonsterStyleIcon({monster, nolink=true}))
end
end
tableTxt = tableTxt..'\r\n|}'
table.insert(tableBits, '\r\n|}')
return tableTxt
return table.concat(tableBits, '')
end
end


Line 1,012: Line 1,322:
local area = Areas.getArea(areaName)
local area = Areas.getArea(areaName)
if area == nil then
if area == nil then
return "ERROR: Could not find a dungeon named "..areaName..'[[Category:Pages with script errors]]'
return Shared.printError('Could not find a dungeon named ' .. areaName)
end
end


--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,022: 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,029: 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,036: 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,043: Line 1,361:
local monster = p.getMonsterByID(monsterID)
local monster = p.getMonsterByID(monsterID)
if monster ~= nil then
if monster ~= nil then
table.insert(monIconPart, Icons.Icon({monster.name, type='monster'}))
table.insert(monIconPart, Icons.Icon({p.getMonsterName(monster), type='monster'}))
end
end
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,053: 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,064: Line 1,385:
-- entityID corresponds to a monster
-- entityID corresponds to a monster
local monster = p.getMonsterByID(entityID)
local monster = p.getMonsterByID(entityID)
monIcon = Icons.Icon({monster.name, 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,100: 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,114: 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
--if area.isEvent ~= nil and area.isEvent then
-- for i, eventAreaID in ipairs(Areas.eventData.slayerAreas) do
-- table.insert(returnPart, buildRow(eventAreaID, {5, 8}, 'SlayerArea'))
-- end
--  -- Add Bane * 4
--  table.insert(returnPart, buildRow(152, 4))
--end
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,132: 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,145: Line 1,469:
local area = Areas.getArea(areaName)
local area = Areas.getArea(areaName)
if area == nil then
if area == nil then
return "ERROR: Could not find a dungeon named "..areaName..'[[Category:Pages with script errors]]'
return Shared.printError('Could not find a dungeon named ' .. areaName)
end
end
local totalHP = 0
local totalHP = 0
Line 1,160: Line 1,484:
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)
table.insert(monsterList, Icons.Icon({monster.name, type='monster'}))
table.insert(monsterList, Icons.Icon({p.getMonsterName(monster), type='monster'}))
end
end
return table.concat(monsterList, '<br/>')
return table.concat(monsterList, '<br/>')
Line 1,169: Line 1,493:
local lastID = ''
local lastID = ''
local count = 0
local count = 0
-- Special handing for Impending Darkness event
-- TODO needs to be revised once there is a better understanding of how the event works
--if area.isEvent ~= nil and area.isEvent then
-- for i, eventAreaID in ipairs(Areas.eventData.slayerAreas) do
-- local eventArea = Areas.getAreaByID('slayer', eventAreaID)
-- table.insert(monsterList, '5-8 ' .. Icons.Icon({eventArea.name, type='combatArea'}) .. ' Monsters')
-- end
-- table.insert(monsterList, '4 ' .. Icons.Icon({'Bane', type='monster'}))
--end
local monsterCounts = {}
local monsterCounts = {}
Line 1,203: Line 1,518:
local monster = p.getMonsterByID(monsterID)
local monster = p.getMonsterByID(monsterID)
if monster ~= nil then
if monster ~= nil then
table.insert(monIconPart, '&nbsp;&nbsp;&nbsp;' .. Icons.Icon({monster.name, type='monster'}))
table.insert(monIconPart, '&nbsp;&nbsp;&nbsp;' .. Icons.Icon({p.getMonsterName(monster), type='monster'}))
end
end
end
end
Line 1,209: Line 1,524:
else
else
local monsterObj = p.getMonsterByID(monster.id)
local monsterObj = p.getMonsterByID(monster.id)
table.insert(monsterList, Icons.Icon({monsterObj.name, type='monster', qty=monster.count}))
table.insert(monsterList, Icons.Icon({p.getMonsterName(monsterObj), type='monster', qty=monster.count}))
end
end
end
end
Line 1,220: Line 1,535:
local area = Areas.getArea(areaName)
local area = Areas.getArea(areaName)
if area == nil then
if area == nil then
return "ERROR: Could not find an area named "..areaName..'[[Category:Pages with script errors]]'
return Shared.printError('Could not find an area named ' .. areaName)
end
end


Line 1,236: Line 1,551:
if monster.gpDrops ~= nil and monster.gpDrops.max > 0 then
if monster.gpDrops ~= nil and monster.gpDrops.max > 0 then
local avgGp = (monster.gpDrops.min + monster.gpDrops.max) / 2
local avgGp = (monster.gpDrops.min + monster.gpDrops.max) / 2
result = result .. '<br/>' .. monster.name .. ',' .. monster.gpDrops.min .. ',' .. monster.gpDrops.max .. ',' .. avgGp
result = result .. '<br/>' .. p.getMonsterName(monster) .. ',' .. monster.gpDrops.min .. ',' .. monster.gpDrops.max .. ',' .. avgGp
end
end
end
end
Line 1,293: Line 1,608:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end


Line 1,306: Line 1,621:
local monsterGP = p._getMonsterAverageGP(monster)
local monsterGP = p._getMonsterAverageGP(monster)
local combatLevel = p._getMonsterCombatLevel(monster)
local combatLevel = p._getMonsterCombatLevel(monster)
result = result..'\r\n|-\r\n|'..Icons.Icon({monster.name, type='monster', noicon=true})..'||'..combatLevel..'||'..monsterGP
result = result..'\r\n|-\r\n|'..Icons.Icon({p.getMonsterName(monster), type='monster', noicon=true})..'||'..combatLevel..'||'..monsterGP
end
end
end
end
Line 1,319: Line 1,634:


if tier == nil then
if tier == nil then
return "ERROR: No tier specified[[Category:Pages with script errors]]"
return Shared.printError('No tier specified')
end
end


Line 1,329: Line 1,644:


if slayerTier == nil then
if slayerTier == nil then
return "ERROR: Invalid slayer tier[[Category:Pages with script errors]]"
return Shared.printError('Invalid slayer tier')
end
end


Line 1,399: Line 1,714:
local boneTxt = (bones ~= nil and Icons.Icon({bones.item.name, type='item', notext=true})) or 'None'
local boneTxt = (bones ~= nil and Icons.Icon({bones.item.name, type='item', notext=true})) or 'None'
table.insert(tableParts, '\r\n|-\r\n|style="text-align: center;" |' .. Icons.Icon({monster.name, type='monster', size=50, notext=true}))
table.insert(tableParts, '\r\n|-\r\n|style="text-align: center;" |' .. Icons.Icon({p.getMonsterName(monster), type='monster', size=50, notext=true}))
table.insert(tableParts, '\r\n|style="text-align:left" |' .. Icons.Icon({monster.name, type='monster', noicon=true}))
table.insert(tableParts, '\r\n|style="text-align:left" |' .. Icons.Icon({p.getMonsterName(monster), type='monster', noicon=true}))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. cmbLevel .. '" |' .. Shared.formatnum(cmbLevel))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. cmbLevel .. '" |' .. Shared.formatnum(cmbLevel))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. p._getMonsterHP(monster) .. '" |' .. Shared.formatnum(p._getMonsterHP(monster)))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. p._getMonsterHP(monster) .. '" |' .. Shared.formatnum(p._getMonsterHP(monster)))
Line 1,438: Line 1,753:
-- Generate row per monster
-- Generate row per monster
for i, monster in ipairs(GameData.rawData.monsters) do
for i, monster in ipairs(GameData.rawData.monsters) do
local cmbLevel = p._getMonsterCombatLevel(monster)
if p.getMonsterName(monster) ~= nil then
 
local cmbLevel = p._getMonsterCombatLevel(monster)
local gpTxt = nil
if monster.gpDrops.min >= monster.gpDrops.max then
local gpTxt = nil
gpTxt = Shared.formatnum(monster.gpDrops.min)
if monster.gpDrops.min >= monster.gpDrops.max then
else
gpTxt = Shared.formatnum(monster.gpDrops.min)
gpTxt = Shared.formatnum(monster.gpDrops.min) .. ' - ' .. Shared.formatnum(monster.gpDrops.max)
else
end
gpTxt = Shared.formatnum(monster.gpDrops.min) .. ' - ' .. Shared.formatnum(monster.gpDrops.max)
end
local lootVal = p._getMonsterLootValue(monster)
local lootTxt = '0'
local lootVal = p._getMonsterLootValue(monster)
if lootVal ~= 0 then
local lootTxt = '0'
lootTxt = Shared.formatnum(Shared.round(lootVal, 2, 2))
if lootVal ~= 0 then
end
lootTxt = Shared.formatnum(Shared.round(lootVal, 2, 2))
end
 
table.insert(tableParts, '\r\n|-\r\n|style="text-align: center;" |' .. Icons.Icon({monster.name, type='monster', size=50, notext=true}))
table.insert(tableParts, '\r\n|style="text-align:left" |' .. Icons.Icon({monster.name, type='monster', noicon=true}))
table.insert(tableParts, '\r\n|-\r\n|style="text-align: center;" |' .. Icons.Icon({p.getMonsterName(monster), type='monster', size=50, notext=true}))
table.insert(tableParts, '\r\n|style="text-align:right" |' .. monster.id)
table.insert(tableParts, '\r\n|style="text-align:left" |' .. Icons.Icon({p.getMonsterName(monster), type='monster', noicon=true}))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. cmbLevel .. '" |' .. Shared.formatnum(cmbLevel))
table.insert(tableParts, '\r\n|style="text-align:right" |' .. monster.id)
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. p._getMonsterHP(monster) .. '" |' .. Shared.formatnum(p._getMonsterHP(monster)))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. cmbLevel .. '" |' .. Shared.formatnum(cmbLevel))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. (monster.gpDrops.min + monster.gpDrops.max) / 2 .. '" |' .. gpTxt)
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. p._getMonsterHP(monster) .. '" |' .. Shared.formatnum(p._getMonsterHP(monster)))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. lootVal .. '" |' .. lootTxt)
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. (monster.gpDrops.min + monster.gpDrops.max) / 2 .. '" |' .. gpTxt)
table.insert(tableParts, '\r\n|style="text-align:right;width:190px" |' .. p._getMonsterAreas(monster, false))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. lootVal .. '" |' .. lootTxt)
end
table.insert(tableParts, '\r\n|style="text-align:right;width:190px" |' .. p._getMonsterAreas(monster, false))
end
end


table.insert(tableParts, '\r\n|}')
table.insert(tableParts, '\r\n|}')
Line 1,484: Line 1,801:
-- Generate row per monster
-- Generate row per monster
for i, monster in ipairs(GameData.rawData.monsters) do
for i, monster in ipairs(GameData.rawData.monsters) do
local cmbLevel = p._getMonsterCombatLevel(monster)
if p.getMonsterName(monster) ~= nil then
 
local cmbLevel = p._getMonsterCombatLevel(monster)
local gpTxt = nil
if monster.gpDrops.min >= monster.gpDrops.max then
local gpTxt = nil
gpTxt = Shared.formatnum(monster.gpDrops.min)
if monster.gpDrops.min >= monster.gpDrops.max then
else
gpTxt = Shared.formatnum(monster.gpDrops.min)
gpTxt = Shared.formatnum(monster.gpDrops.min) .. ' - ' .. Shared.formatnum(monster.gpDrops.max)
else
end
gpTxt = Shared.formatnum(monster.gpDrops.min) .. ' - ' .. Shared.formatnum(monster.gpDrops.max)
end
local lootVal = p._getMonsterLootValue(monster)
local lootTxt = '0'
local lootVal = p._getMonsterLootValue(monster)
if lootVal ~= 0 then
local lootTxt = '0'
lootTxt = Shared.formatnum(Shared.round(lootVal, 2, 2))
if lootVal ~= 0 then
lootTxt = Shared.formatnum(Shared.round(lootVal, 2, 2))
end
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 bones = p._getMonsterBones(monster)
local boneTxt = (bones ~= nil and Icons.Icon({bones.item.name, type='item', notext=true})) or 'None'
table.insert(tableParts, '\r\n|-\r\n|style="text-align: center;" |' .. Icons.Icon({p.getMonsterName(monster), type='monster', size=50, notext=true}))
table.insert(tableParts, '\r\n|style="text-align:left" |' .. Icons.Icon({p.getMonsterName(monster), type='monster', noicon=true}))
-- table.insert(tableParts, '\r\n|style="text-align:right" |' .. monster.id)
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. cmbLevel .. '" |' .. Shared.formatnum(cmbLevel))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. p._getMonsterHP(monster) .. '" |' .. Shared.formatnum(p._getMonsterHP(monster)))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. evaR[1] .. '" |' .. Shared.formatnum(evaR[1]))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. atkSpeed .. '" |' .. Shared.round(atkSpeed, 1, 1))
table.insert(tableParts, '\r\n|style="text-align:center;border-right:hidden" |' .. p._getMonsterStyleIcon({monster, notext=true}))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. maxHit .. '" |' .. Shared.formatnum(maxHit))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. accR .. '" |' .. Shared.formatnum(accR))
--table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. evaR[2] .. '" |' .. Shared.formatnum(evaR[2]))
--table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. evaR[3] .. '" |' .. Shared.formatnum(evaR[3]))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. (monster.gpDrops.min + monster.gpDrops.max) / 2 .. '" |' .. gpTxt)
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. lootVal .. '" |' .. lootTxt)
table.insert(tableParts, '\r\n|style="text-align:center" |' .. boneTxt)
-- table.insert(tableParts, '\r\n|style="text-align:right;width:190px" |' .. p._getMonsterAreas(monster, hideDungeons))
end
end
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 bones = p._getMonsterBones(monster)
local boneTxt = (bones ~= nil and Icons.Icon({bones.item.name, type='item', notext=true})) or 'None'
table.insert(tableParts, '\r\n|-\r\n|style="text-align: center;" |' .. Icons.Icon({monster.name, type='monster', size=50, notext=true}))
table.insert(tableParts, '\r\n|style="text-align:left" |' .. Icons.Icon({monster.name, type='monster', noicon=true}))
-- table.insert(tableParts, '\r\n|style="text-align:right" |' .. monster.id)
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. cmbLevel .. '" |' .. Shared.formatnum(cmbLevel))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. p._getMonsterHP(monster) .. '" |' .. Shared.formatnum(p._getMonsterHP(monster)))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. evaR[1] .. '" |' .. Shared.formatnum(evaR[1]))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. atkSpeed .. '" |' .. Shared.round(atkSpeed, 1, 1))
table.insert(tableParts, '\r\n|style="text-align:center;border-right:hidden" |' .. p._getMonsterStyleIcon({monster, notext=true}))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. maxHit .. '" |' .. Shared.formatnum(maxHit))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. accR .. '" |' .. Shared.formatnum(accR))
--table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. evaR[2] .. '" |' .. Shared.formatnum(evaR[2]))
--table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. evaR[3] .. '" |' .. Shared.formatnum(evaR[3]))
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. (monster.gpDrops.min + monster.gpDrops.max) / 2 .. '" |' .. gpTxt)
table.insert(tableParts, '\r\n|style="text-align:right" data-sort-value="' .. lootVal .. '" |' .. lootTxt)
table.insert(tableParts, '\r\n|style="text-align:center" |' .. boneTxt)
-- table.insert(tableParts, '\r\n|style="text-align:right;width:190px" |' .. p._getMonsterAreas(monster, hideDungeons))
end
end


Line 1,545: Line 1,864:
spAttTable[spAtt.id]['icons'][attChance] = {}
spAttTable[spAtt.id]['icons'][attChance] = {}
end
end
table.insert(spAttTable[spAtt.id]['icons'][attChance], Icons.Icon({ monster.name, type = 'monster' }))
table.insert(spAttTable[spAtt.id]['icons'][attChance], Icons.Icon({ p.getMonsterName(monster), type = 'monster' }))
end
end
end
end
Line 1,590: Line 1,909:
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)
table.insert(outArray, "===={{MonsterIcon|"..monster.name.."|size=40}}====")
table.insert(outArray, "===={{MonsterIcon|"..p.getMonsterName(monster).."|size=40}}====")
table.insert(outArray, "{{MonsterDrops|"..monster.name.."|size=40}}")
table.insert(outArray, "{{MonsterDrops|"..p.getMonsterName(monster).."|size=40}}")
end
end
return table.concat(outArray, "\r\n")
return table.concat(outArray, "\r\n")
Line 1,628: Line 1,947:


if monster == nil then
if monster == nil then
return "ERROR: No monster with that name found[[Category:Pages with script errors]]"
return Shared.printError('No monster with that name found')
end
end
3

edits