Difference between revisions of "Module:Monster"
(Death knight needs disambiguation) |
(Monsters with spell includes damage. Also some refactoring) |
||
Line 165: | Line 165: | ||
local lich_spells = { | local lich_spells = { | ||
− | ["Corrosive Bolt"] = | + | ["Corrosive Bolt"] = true, |
− | ["Lehudib's Crystal Spear"] = | + | ["Lehudib's Crystal Spear"] = true, |
− | ["Orb of Destruction"] = | + | ["Orb of Destruction"] = true, |
["Summon Greater Demon"] = 50, | ["Summon Greater Demon"] = 50, | ||
["Banishment"] = 33.3, | ["Banishment"] = 33.3, | ||
["Haste"] = 33.3, | ["Haste"] = 33.3, | ||
["Invisibility"] = 33.3, | ["Invisibility"] = 33.3, | ||
− | ["Agony"] = | + | ["Agony"] = true, |
− | ["Bolt of Cold"] = | + | ["Bolt of Cold"] = true, |
− | ["Bolt of Draining"] = | + | ["Bolt of Draining"] = true, |
− | ["Bolt of Fire"] = | + | ["Bolt of Fire"] = true, |
− | ["Confuse"] = | + | ["Confuse"] = true, |
− | ["Fireball"] = | + | ["Fireball"] = true, |
− | ["Haunt"] = | + | ["Haunt"] = true, |
− | ["Iron Shot"] = | + | ["Iron Shot"] = true, |
− | ["Iskenderun's Mystic Blast"] = | + | ["Iskenderun's Mystic Blast"] = true, |
− | ["Iskenderun's Battlesphere"] = | + | ["Iskenderun's Battlesphere"] = true, |
− | ["Lee's Rapid Deconstruction"] = | + | ["Lee's Rapid Deconstruction"] = true, |
− | ["Lightning Bolt"] = | + | ["Lightning Bolt"] = true, |
− | ["Malign Gateway"] = | + | ["Malign Gateway"] = true, |
− | ["Paralyse"] = | + | ["Paralyse"] = true, |
− | ["Petrify"] = | + | ["Petrify"] = true, |
− | ["Poison Arrow"] = | + | ["Poison Arrow"] = true, |
− | ["Spellforged Servitor"] = | + | ["Spellforged Servitor"] = true, |
− | ["Simulacrum"] = | + | ["Simulacrum"] = true, |
− | ["Sleep"] = | + | ["Sleep"] = true, |
− | ["Slow"] = | + | ["Slow"] = true, |
− | ["Summon Horrible Things"] = | + | ["Summon Horrible Things"] = true, |
− | ["Throw Icicle"] = | + | ["Throw Icicle"] = true, |
} | } | ||
local panlord_spells = { | local panlord_spells = { | ||
− | ["Fire Storm"] = | + | ["Fire Storm"] = true, |
− | Glaciate = | + | Glaciate = true, |
− | ["Lehudib's Crystal Spear"] = | + | ["Lehudib's Crystal Spear"] = true, |
− | ["Chain Lightning"] = | + | ["Chain Lightning"] = true, |
− | ["Orb of Destruction"] = | + | ["Orb of Destruction"] = true, |
− | ["Corrosive Bolt"] = | + | ["Corrosive Bolt"] = true, |
− | ["Disintegrate"] = | + | ["Disintegrate"] = true, |
− | ["Bolt of Fire"] = | + | ["Bolt of Fire"] = true, |
− | ["Bolt of Cold"] = | + | ["Bolt of Cold"] = true, |
− | ["Iron Shot"] = | + | ["Iron Shot"] = true, |
− | ["Poison Arrow"] = | + | ["Poison Arrow"] = true, |
− | ["Bolt of Draining"] = | + | ["Bolt of Draining"] = true, |
− | ["Quicksilver Bolt"] = | + | ["Quicksilver Bolt"] = true, |
− | ["Force Lance"] = | + | ["Force Lance"] = true, |
− | Fireball = | + | Fireball = true, |
− | ["Bolt of Magma"] = | + | ["Bolt of Magma"] = true, |
− | ["Lee's Rapid Deconstruction"] = | + | ["Lee's Rapid Deconstruction"] = true, |
− | ["Lightning Bolt"] = | + | ["Lightning Bolt"] = true, |
− | Blinkbolt = | + | Blinkbolt = true, |
− | ["Venom Bolt"] = | + | ["Venom Bolt"] = true, |
− | Agony = | + | Agony = true, |
− | Sleep = | + | Sleep = true, |
− | ["Iskendrun's Mystic Blast"] = | + | ["Iskendrun's Mystic Blast"] = true, |
− | ["Sticky Flame Range"] = | + | ["Sticky Flame Range"] = true, |
− | ["Steam Ball"] = | + | ["Steam Ball"] = true, |
− | ["Throw Icicle"] = | + | ["Throw Icicle"] = true, |
− | Airstrike = | + | Airstrike = true, |
− | Smiting = | + | Smiting = true, |
− | ["Dazzling Spray"] = | + | ["Dazzling Spray"] = true, |
− | ["Stone Arrow"] = | + | ["Stone Arrow"] = true, |
− | ["Static Discharge"] = | + | ["Static Discharge"] = true, |
− | ["Vampiric Draining"] = | + | ["Vampiric Draining"] = true, |
− | ["Throw Flame"] = | + | ["Throw Flame"] = true, |
− | ["Throw Frost"] = | + | ["Throw Frost"] = true, |
− | ["Summon Demon"] = | + | ["Summon Demon"] = true, |
− | ["Summon Dragon"] = | + | ["Summon Dragon"] = true, |
− | ["Summon Horrible Things"] = | + | ["Summon Horrible Things"] = true, |
− | ["Summon Greater Demon"] = | + | ["Summon Greater Demon"] = true, |
− | Haunt = | + | Haunt = true, |
− | ["Summon Hydra"] = | + | ["Summon Hydra"] = true, |
− | ["Malign Gateway"] = | + | ["Malign Gateway"] = true, |
− | Haste = | + | Haste = true, |
− | Invisibility = | + | Invisibility = true, |
− | ["Symbol of Torment"] = | + | ["Symbol of Torment"] = true, |
− | ["Monstrous Menagerie"] = | + | ["Monstrous Menagerie"] = true, |
− | Silence = | + | Silence = true, |
− | ["Shadow Creatures"] = | + | ["Shadow Creatures"] = true, |
− | ["Summon Vermin"] = | + | ["Summon Vermin"] = true, |
− | ["Summon Swarm"] = | + | ["Summon Swarm"] = true, |
− | ["Iskenderun's Battlesphere"] = | + | ["Iskenderun's Battlesphere"] = true, |
− | ["Fulminant Prism"] = | + | ["Fulminant Prism"] = true, |
− | ["Summon Ice Beast"] = | + | ["Summon Ice Beast"] = true, |
− | Swiftness = | + | Swiftness = true, |
− | Blink = | + | Blink = true, |
− | ["Summon Butterflies"] = | + | ["Summon Butterflies"] = true, |
− | Shatter = | + | Shatter = true, |
− | Banishment = | + | Banishment = true, |
− | ["Freezing Cloud"] = | + | ["Freezing Cloud"] = true, |
− | ["Poisonous Cloud"] = | + | ["Poisonous Cloud"] = true, |
− | ["Mass Confusion"] = | + | ["Mass Confusion"] = true, |
− | ["Metabolic Englaciation"] = | + | ["Metabolic Englaciation"] = true, |
− | ["Dispel Undead"] = | + | ["Dispel Undead"] = true, |
− | Dig = | + | Dig = true, |
− | Petrify = | + | Petrify = true, |
− | ["Olgreb's Toxic Radiance"] = | + | ["Olgreb's Toxic Radiance"] = true, |
− | Paralyse = | + | Paralyse = true, |
− | Polymorph = | + | Polymorph = true, |
− | ["Mephitic Cloud"] = | + | ["Mephitic Cloud"] = true, |
− | Confuse = | + | Confuse = true, |
− | ["Teleport Other"] = | + | ["Teleport Other"] = true, |
− | Slow = | + | Slow = true, |
− | ["Hellfire Burst"] = | + | ["Hellfire Burst"] = true, |
− | ["Metal Splinters"] = | + | ["Metal Splinters"] = true, |
− | ["Energy Bolt"] = | + | ["Energy Bolt"] = true, |
− | ["Orb of Electricity"] = | + | ["Orb of Electricity"] = true, |
− | ["Hellfire"] = | + | ["Hellfire"] = true, |
["Summon Eyeballs"] = 10, | ["Summon Eyeballs"] = 10, | ||
− | ["Summon Greater Demon"] = | + | ["Summon Greater Demon"] = true, |
} | } | ||
Line 286: | Line 286: | ||
end | end | ||
− | -- returns a | + | -- If a monster may be able to cast the spell, returns a table. Otherwise returns nil. |
− | + | -- The table contains the following keys: | |
− | -- | + | -- * chance - the chance that the monster will have the spell. May be missing if the chance is too hard to calculate. |
− | + | -- * damage - the damage that the spell does when the monster casts it (if any) | |
− | local function | + | local function monster_spell_data(monster, spell_name) |
+ | local special_table = nil | ||
if monster.Species == "lich" and monster.Name ~= "Boris" then | if monster.Species == "lich" and monster.Name ~= "Boris" then | ||
− | + | special_table = lich_spells | |
+ | elseif monster.Name == "pandemonium lord" then | ||
+ | special_table = panlord_spells | ||
end | end | ||
− | if | + | if special_table then |
− | return | + | local chance = special_table[spell_name] |
+ | if chance then | ||
+ | if type(chance) == "number" then | ||
+ | return {chance = chance} | ||
+ | else | ||
+ | return {} | ||
+ | end | ||
+ | else | ||
+ | return nil | ||
+ | end | ||
end | end | ||
if not monster.Spellsets then | if not monster.Spellsets then | ||
− | return | + | return nil |
end | end | ||
local has = 0 | local has = 0 | ||
local total = 0 | local total = 0 | ||
+ | local damage = nil | ||
for _, spellset in ipairs(monster.Spellsets) do | for _, spellset in ipairs(monster.Spellsets) do | ||
total = total + 1 | total = total + 1 | ||
Line 310: | Line 323: | ||
if spell.Spell == spell_name then | if spell.Spell == spell_name then | ||
has = has + 1 | has = has + 1 | ||
+ | if not damage then | ||
+ | damage = spell.Damage | ||
+ | end | ||
break | break | ||
end | end | ||
end | end | ||
end | end | ||
− | return has * 100 / total | + | if has > 0 then |
+ | return {chance = has * 100 / total, damage = damage} | ||
+ | else | ||
+ | return nil | ||
+ | end | ||
end | end | ||
Line 348: | Line 368: | ||
wizard = "Wizard (monster)", | wizard = "Wizard (monster)", | ||
} | } | ||
+ | |||
+ | local function monsterlink(frame, data) | ||
+ | local ret = "* " .. frame:expandTemplate{title = "monsterlink", args = {data.monster}} | ||
+ | if data.damage or data.chance and data.chance ~= 100 then | ||
+ | ret = ret .. " (" | ||
+ | if data.damage then | ||
+ | ret = ret .. data.damage .. " damage" | ||
+ | if data.chance and data.chance ~= 100 then | ||
+ | ret = ret .. ", " | ||
+ | end | ||
+ | end | ||
+ | if data.chance and data.chance ~= 100 then | ||
+ | ret = ret .. ("%.1f%% chance"):format(data.chance) | ||
+ | end | ||
+ | ret = ret .. ")" | ||
+ | end | ||
+ | return ret | ||
+ | end | ||
function p.monsters_with_spell(frame) | function p.monsters_with_spell(frame) | ||
Line 354: | Line 392: | ||
local sometimes = {} | local sometimes = {} | ||
for monster, monsterdata in pairs(data) do | for monster, monsterdata in pairs(data) do | ||
− | local | + | local data = not is_vault_only(monsterdata) and monster_spell_data(monsterdata, spell_name) |
− | if | + | if data then |
− | monster = disambig[monster] or (monster:gsub("^%l", string.upper)) | + | data.monster = disambig[monster] or (monster:gsub("^%l", string.upper)) |
− | if | + | if data.chance and data.chance == 100 then |
− | table.insert(always, | + | table.insert(always, data) |
else | else | ||
− | table.insert(sometimes, | + | table.insert(sometimes, data) |
end | end | ||
end | end | ||
end | end | ||
− | table.sort(always, string_icmp) | + | table.sort(always, function (m1, m2) return string_icmp(m1.monster, m2.monster) end) |
− | table.sort(sometimes, function(m1, m2) return string_icmp(m1 | + | table.sort(sometimes, function(m1, m2) return string_icmp(m1.monster, m2.monster) end) |
local ret = {} | local ret = {} | ||
if #always > 0 then | if #always > 0 then | ||
table.insert(ret, "The following enemies cast " .. spell_name .. ":") | table.insert(ret, "The following enemies cast " .. spell_name .. ":") | ||
− | for _, | + | for _, data in ipairs(always) do |
− | table.insert(ret, | + | table.insert(ret, monsterlink(frame, data)) |
end | end | ||
end | end | ||
if #sometimes > 0 then | if #sometimes > 0 then | ||
table.insert(ret, "The following enemies may be able to cast " .. spell_name .. ", depending on their spell set:") | table.insert(ret, "The following enemies may be able to cast " .. spell_name .. ", depending on their spell set:") | ||
− | for _, | + | for _, data in ipairs(sometimes) do |
− | + | table.insert(ret, monsterlink(frame, data)) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | table.insert(ret, | ||
end | end | ||
end | end |
Revision as of 22:54, 6 January 2016
Documentation for this module may be created at Module:Monster/doc
local p = {} local data = mw.loadData('Module: Table of monsters') -- Taken from max_corpse_chunks in mon-util.cc local max_corpse_chunks = { Tiny = 1, Little = 2, Small = 3, Medium = 4, Large = 9, Big = 10, Giant = 12, } local function name_arg(frame) local name = frame.args[1] if not name or name == "" then name = mw.title.getCurrentTitle().text end return name end function p.glyph(frame) local name = name_arg(frame) local monster = data[name] or data[name:lower()] if monster then return frame:expandTemplate{title = monster.Colour, args = {monster.Glyph}} else -- probably obsolete monster; fall back to old templates return frame:expandTemplate{title = "glyph/" .. name} end end function p.monster_info(frame) local name = name_arg(frame) local monster = data[name] or data[name:lower()] if not monster then return name end local args = {} args.name = monster.Name args.glyph = frame:expandTemplate{title = monster.Colour, args = {monster.Glyph}} local flags = {} for i, v in ipairs(monster.Flags) do flags[i] = frame:expandTemplate{title = v} end args.flags = table.concat(flags, "<br>") local resistances = {} for i, v in ipairs(monster.Resistances) do resistances[i] = frame:expandTemplate{title = v} end if #resistances == 0 then args.resistances = "None" else args.resistances = table.concat(resistances, "<br>") end local vulnerabilities = {} for i, v in ipairs(monster.Vulnerabilities) do vulnerabilities[i] = frame:expandTemplate{title = v} end if #vulnerabilities == 0 then args.vulnerabilities = "None" else args.vulnerabilities = table.concat(vulnerabilities, "<br>") end if monster.Corpse ~= "No" then args.max_chunks = max_corpse_chunks[monster.Size] or 0 else args.max_chunks = 0 end args.meat = frame:expandTemplate{title = monster.Corpse .. " corpse"} args.xp = monster.XP args.holiness = frame:expandTemplate{title = monster.Holiness} args.magic_resistance = monster.MR local hp_min = monster.HD*monster["Base HP"] + monster["Fixed HP"] local hp_max = monster.HD*(monster["Base HP"]+monster["Rand HP"])+monster["Fixed HP"] if hp_min == hp_max then args.hp_range = hp_min else args.hp_range = ("%d-%d"):format(hp_min, hp_max) end args.avg_hp = (hp_min + hp_max)/2 args.armour_class = monster.AC args.evasion = monster.EV args.habitat = monster.Habitat args.speed = monster.Speed args.size = frame:expandTemplate{title = monster.Size} local item_use = {} for i, v in ipairs(monster["Item Use"]) do item_use[i] = frame:expandTemplate{title = v} end args.item_use = table.concat(item_use, "<br>") for i = 1, 4 do local attack = monster.Attacks[i] if attack then local typ = frame:expandTemplate{title = attack.Type .. " type"} local flavour = frame:expandTemplate{title = attack.Flavour .. " flavour"} args["attack" .. i] = ("%d (%s: %s)"):format(attack.Damage, typ, flavour) else args["attack" .. i] = "" end end args.hit_dice = monster.HD args.base_hp = monster["Base HP"] args.extra_hp = monster["Rand HP"] args.fixed_hp = monster["Fixed HP"] args.intelligence = frame:expandTemplate{title = monster.Intelligence .. " intelligence"} args.genus = monster.Genus args.species = monster.Species local infobox = frame:expandTemplate{title = "monster", args = args} local flavour = monster.Description if monster.Quote then flavour = flavour .. "\n----\n" .. monster.Quote:gsub("\n", "<br>") end flavour = frame:expandTemplate{title = "flavour", args = {flavour}} return infobox .. "\n" .. flavour end local roman = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"} function p.monster_spells(frame) local name = name_arg(frame) local monster = data[name] or data[name:lower()] local ret = "==Spells==\n[[Category:Spellcaster]]" local cleared = false for i, spells in ipairs(monster.Spellsets) do ret = ret .. [=[<div style="margin: 1.0em; margin-top:0; margin-right:0; padding: 0px; float: left; border:none;"> {| class="prettytable" style="border:none; margin:0; padding:0; width:16em;" ! colspan="3" style="font-size:larger;" | Spell set ]=] .. roman[i] .. "\n" for j, spell in ipairs(spells) do ret = ret .. '|-\n! align="left" | Slot<sup>' .. j .. '</sup>\n| [[' .. spell.Spell .. ']]' if spell.Damage then ret = ret .. " (" .. spell.Damage .. ")" end ret = ret .. '\n| ' local flags = {} for i, v in ipairs(spell.Flags) do flags[i] = frame:expandTemplate{title = v .. " slot flag"} end ret = ret .. table.concat(flags, ", ") .. "\n" end ret = ret .. "|-\n|}</div>" cleared = i % 2 == 0 if cleared then ret = ret .. '<div style="clear:both;"></div>' end end if not cleared then ret = ret .. '<div style="clear:both;"></div>' end return ret end local lich_spells = { ["Corrosive Bolt"] = true, ["Lehudib's Crystal Spear"] = true, ["Orb of Destruction"] = true, ["Summon Greater Demon"] = 50, ["Banishment"] = 33.3, ["Haste"] = 33.3, ["Invisibility"] = 33.3, ["Agony"] = true, ["Bolt of Cold"] = true, ["Bolt of Draining"] = true, ["Bolt of Fire"] = true, ["Confuse"] = true, ["Fireball"] = true, ["Haunt"] = true, ["Iron Shot"] = true, ["Iskenderun's Mystic Blast"] = true, ["Iskenderun's Battlesphere"] = true, ["Lee's Rapid Deconstruction"] = true, ["Lightning Bolt"] = true, ["Malign Gateway"] = true, ["Paralyse"] = true, ["Petrify"] = true, ["Poison Arrow"] = true, ["Spellforged Servitor"] = true, ["Simulacrum"] = true, ["Sleep"] = true, ["Slow"] = true, ["Summon Horrible Things"] = true, ["Throw Icicle"] = true, } local panlord_spells = { ["Fire Storm"] = true, Glaciate = true, ["Lehudib's Crystal Spear"] = true, ["Chain Lightning"] = true, ["Orb of Destruction"] = true, ["Corrosive Bolt"] = true, ["Disintegrate"] = true, ["Bolt of Fire"] = true, ["Bolt of Cold"] = true, ["Iron Shot"] = true, ["Poison Arrow"] = true, ["Bolt of Draining"] = true, ["Quicksilver Bolt"] = true, ["Force Lance"] = true, Fireball = true, ["Bolt of Magma"] = true, ["Lee's Rapid Deconstruction"] = true, ["Lightning Bolt"] = true, Blinkbolt = true, ["Venom Bolt"] = true, Agony = true, Sleep = true, ["Iskendrun's Mystic Blast"] = true, ["Sticky Flame Range"] = true, ["Steam Ball"] = true, ["Throw Icicle"] = true, Airstrike = true, Smiting = true, ["Dazzling Spray"] = true, ["Stone Arrow"] = true, ["Static Discharge"] = true, ["Vampiric Draining"] = true, ["Throw Flame"] = true, ["Throw Frost"] = true, ["Summon Demon"] = true, ["Summon Dragon"] = true, ["Summon Horrible Things"] = true, ["Summon Greater Demon"] = true, Haunt = true, ["Summon Hydra"] = true, ["Malign Gateway"] = true, Haste = true, Invisibility = true, ["Symbol of Torment"] = true, ["Monstrous Menagerie"] = true, Silence = true, ["Shadow Creatures"] = true, ["Summon Vermin"] = true, ["Summon Swarm"] = true, ["Iskenderun's Battlesphere"] = true, ["Fulminant Prism"] = true, ["Summon Ice Beast"] = true, Swiftness = true, Blink = true, ["Summon Butterflies"] = true, Shatter = true, Banishment = true, ["Freezing Cloud"] = true, ["Poisonous Cloud"] = true, ["Mass Confusion"] = true, ["Metabolic Englaciation"] = true, ["Dispel Undead"] = true, Dig = true, Petrify = true, ["Olgreb's Toxic Radiance"] = true, Paralyse = true, Polymorph = true, ["Mephitic Cloud"] = true, Confuse = true, ["Teleport Other"] = true, Slow = true, ["Hellfire Burst"] = true, ["Metal Splinters"] = true, ["Energy Bolt"] = true, ["Orb of Electricity"] = true, ["Hellfire"] = true, ["Summon Eyeballs"] = 10, ["Summon Greater Demon"] = true, } local function is_vault_only(monster) for _, flag in ipairs(monster.Flags) do if flag == "Vault flag" then return true end end return false end -- If a monster may be able to cast the spell, returns a table. Otherwise returns nil. -- The table contains the following keys: -- * chance - the chance that the monster will have the spell. May be missing if the chance is too hard to calculate. -- * damage - the damage that the spell does when the monster casts it (if any) local function monster_spell_data(monster, spell_name) local special_table = nil if monster.Species == "lich" and monster.Name ~= "Boris" then special_table = lich_spells elseif monster.Name == "pandemonium lord" then special_table = panlord_spells end if special_table then local chance = special_table[spell_name] if chance then if type(chance) == "number" then return {chance = chance} else return {} end else return nil end end if not monster.Spellsets then return nil end local has = 0 local total = 0 local damage = nil for _, spellset in ipairs(monster.Spellsets) do total = total + 1 for _, spell in ipairs(spellset) do if spell.Spell == spell_name then has = has + 1 if not damage then damage = spell.Damage end break end end end if has > 0 then return {chance = has * 100 / total, damage = damage} else return nil end end local function string_icmp(s1, s2) return s1:lower() < s2:lower() end local disambig = { centaur = "Centaur (monster)", ["death knight"] = "Death knight (monster)", ["deep dwarf"] = "Deep dwarf (monster)", demigod = "Demigod (monster)", demonspawn = "Demonspawn (monster)", draconian = "Draconian (monster)", felid = "Felid (monster)", formicid = "Formicid (monster)", gargoyle = "Gargoyle (monster)", ghoul = "Ghoul (monster)", halfling = "Halfling (monster)", human = "Human (monster)", kobold = "Kobold (monster)", merfolk = "Merfolk (monster)", minotaur = "Minotaur (monster)", mummy = "Mummy (monster)", naga = "Naga (monster)", necromancer = "Necromancer (monster)", octopode = "Octopode (monster)", ogre = "Ogre (monster)", spriggan = "Spriggan (monster)", troll = "Troll (monster)", tengu = "Tengu (monster)", vampire = "Vampire (monster)", wizard = "Wizard (monster)", } local function monsterlink(frame, data) local ret = "* " .. frame:expandTemplate{title = "monsterlink", args = {data.monster}} if data.damage or data.chance and data.chance ~= 100 then ret = ret .. " (" if data.damage then ret = ret .. data.damage .. " damage" if data.chance and data.chance ~= 100 then ret = ret .. ", " end end if data.chance and data.chance ~= 100 then ret = ret .. ("%.1f%% chance"):format(data.chance) end ret = ret .. ")" end return ret end function p.monsters_with_spell(frame) local spell_name = name_arg(frame) local always = {} local sometimes = {} for monster, monsterdata in pairs(data) do local data = not is_vault_only(monsterdata) and monster_spell_data(monsterdata, spell_name) if data then data.monster = disambig[monster] or (monster:gsub("^%l", string.upper)) if data.chance and data.chance == 100 then table.insert(always, data) else table.insert(sometimes, data) end end end table.sort(always, function (m1, m2) return string_icmp(m1.monster, m2.monster) end) table.sort(sometimes, function(m1, m2) return string_icmp(m1.monster, m2.monster) end) local ret = {} if #always > 0 then table.insert(ret, "The following enemies cast " .. spell_name .. ":") for _, data in ipairs(always) do table.insert(ret, monsterlink(frame, data)) end end if #sometimes > 0 then table.insert(ret, "The following enemies may be able to cast " .. spell_name .. ", depending on their spell set:") for _, data in ipairs(sometimes) do table.insert(ret, monsterlink(frame, data)) end end return table.concat(ret, "\n") end return p