Difference between revisions of "Module:Monster"
(Don't include vault-only monsters in monsters_with_spell) |
(Add possible panlord spells) |
||
| Line 194: | Line 194: | ||
["Summon Horrible Things"] = 101, | ["Summon Horrible Things"] = 101, | ||
["Throw Icicle"] = 101, | ["Throw Icicle"] = 101, | ||
| + | } | ||
| + | |||
| + | local panlord_spells = { | ||
| + | ["Fire Storm"] = 101, | ||
| + | Glaciate = 101, | ||
| + | ["Lehudib's Crystal Spear"] = 101, | ||
| + | ["Chain Lightning"] = 101, | ||
| + | ["Orb of Destruction"] = 101, | ||
| + | ["Corrosive Bolt"] = 101, | ||
| + | ["Disintegrate"] = 101, | ||
| + | ["Bolt of Fire"] = 101, | ||
| + | ["Bolt of Cold"] = 101, | ||
| + | ["Iron Shot"] = 101, | ||
| + | ["Poison Arrow"] = 101, | ||
| + | ["Bolt of Draining"] = 101, | ||
| + | ["Quicksilver Bolt"] = 101, | ||
| + | ["Force Lance"] = 101, | ||
| + | Fireball = 101, | ||
| + | ["Bolt of Magma"] = 101, | ||
| + | ["Lee's Rapid Deconstruction"] = 101, | ||
| + | ["Lightning Bolt"] = 101, | ||
| + | Blinkbolt = 101, | ||
| + | ["Venom Bolt"] = 101, | ||
| + | Agony = 101, | ||
| + | Sleep = 101, | ||
| + | ["Iskendrun's Mystic Blast"] = 101, | ||
| + | ["Sticky Flame Range"] = 101, | ||
| + | ["Steam Ball"] = 101, | ||
| + | ["Throw Icicle"] = 101, | ||
| + | Airstrike = 101, | ||
| + | Smiting = 101, | ||
| + | ["Dazzling Spray"] = 101, | ||
| + | ["Stone Arrow"] = 101, | ||
| + | ["Static Discharge"] = 101, | ||
| + | ["Vampiric Draining"] = 101, | ||
| + | ["Throw Flame"] = 101, | ||
| + | ["Throw Frost"] = 101, | ||
| + | ["Summon Demon"] = 101, | ||
| + | ["Summon Dragon"] = 101, | ||
| + | ["Summon Horrible Things"] = 101, | ||
| + | ["Summon Greater Demon"] = 101, | ||
| + | Haunt = 101, | ||
| + | ["Summon Hydra"] = 101, | ||
| + | ["Malign Gateway"] = 101, | ||
| + | Haste = 101, | ||
| + | Invisibility = 101, | ||
| + | ["Symbol of Torment"] = 101, | ||
| + | ["Monstrous Menagerie"] = 101, | ||
| + | Silence = 101, | ||
| + | ["Shadow Creatures"] = 101, | ||
| + | ["Summon Vermin"] = 101, | ||
| + | ["Summon Swarm"] = 101, | ||
| + | ["Iskenderun's Battlesphere"] = 101, | ||
| + | ["Fulminant Prism"] = 101, | ||
| + | ["Summon Ice Beast"] = 101, | ||
| + | Swiftness = 101, | ||
| + | Blink = 101, | ||
| + | ["Summon Butterflies"] = 101, | ||
| + | Shatter = 101, | ||
| + | Banishment = 101, | ||
| + | ["Freezing Cloud"] = 101, | ||
| + | ["Poisonous Cloud"] = 101, | ||
| + | ["Mass Confusion"] = 101, | ||
| + | ["Metabolic Englaciation"] = 101, | ||
| + | ["Dispel Undead"] = 101, | ||
| + | Dig = 101, | ||
| + | Petrify = 101, | ||
| + | ["Olgreb's Toxic Radiance"] = 101, | ||
| + | Paralyse = 101, | ||
| + | Polymorph = 101, | ||
| + | ["Mephitic Cloud"] = 101, | ||
| + | Confuse = 101, | ||
| + | ["Teleport Other"] = 101, | ||
| + | Slow = 101, | ||
| + | ["Hellfire Burst"] = 101, | ||
| + | ["Metal Splinters"] = 101, | ||
| + | ["Energy Bolt"] = 101, | ||
| + | ["Orb of Electricity"] = 101, | ||
| + | ["Hellfire"] = 101, | ||
| + | ["Summon Eyeballs"] = 10, | ||
| + | ["Summon Greater Demon"] = 101 | ||
} | } | ||
| Line 212: | Line 293: | ||
if monster.Species == "lich" and monster.Name ~= "Boris" then | if monster.Species == "lich" and monster.Name ~= "Boris" then | ||
return lich_spells[spell_name] or 0 | return lich_spells[spell_name] or 0 | ||
| + | end | ||
| + | |||
| + | if monster.Name == "pandemonium lord" then | ||
| + | return panlord_spells[spell_name] or 0 | ||
end | end | ||
Revision as of 02:19, 29 December 2015
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"] = 101,
["Lehudib's Crystal Spear"] = 101,
["Orb of Destruction"] = 101,
["Summon Greater Demon"] = 50,
["Banishment"] = 33.3,
["Haste"] = 33.3,
["Invisibility"] = 33.3,
["Agony"] = 101,
["Bolt of Cold"] = 101,
["Bolt of Draining"] = 101,
["Bolt of Fire"] = 101,
["Confuse"] = 101,
["Fireball"] = 101,
["Haunt"] = 101,
["Iron Shot"] = 101,
["Iskenderun's Mystic Blast"] = 101,
["Iskenderun's Battlesphere"] = 101,
["Lee's Rapid Deconstruction"] = 101,
["Lightning Bolt"] = 101,
["Malign Gateway"] = 101,
["Paralyse"] = 101,
["Petrify"] = 101,
["Poison Arrow"] = 101,
["Spellforged Servitor"] = 101,
["Simulacrum"] = 101,
["Sleep"] = 101,
["Slow"] = 101,
["Summon Horrible Things"] = 101,
["Throw Icicle"] = 101,
}
local panlord_spells = {
["Fire Storm"] = 101,
Glaciate = 101,
["Lehudib's Crystal Spear"] = 101,
["Chain Lightning"] = 101,
["Orb of Destruction"] = 101,
["Corrosive Bolt"] = 101,
["Disintegrate"] = 101,
["Bolt of Fire"] = 101,
["Bolt of Cold"] = 101,
["Iron Shot"] = 101,
["Poison Arrow"] = 101,
["Bolt of Draining"] = 101,
["Quicksilver Bolt"] = 101,
["Force Lance"] = 101,
Fireball = 101,
["Bolt of Magma"] = 101,
["Lee's Rapid Deconstruction"] = 101,
["Lightning Bolt"] = 101,
Blinkbolt = 101,
["Venom Bolt"] = 101,
Agony = 101,
Sleep = 101,
["Iskendrun's Mystic Blast"] = 101,
["Sticky Flame Range"] = 101,
["Steam Ball"] = 101,
["Throw Icicle"] = 101,
Airstrike = 101,
Smiting = 101,
["Dazzling Spray"] = 101,
["Stone Arrow"] = 101,
["Static Discharge"] = 101,
["Vampiric Draining"] = 101,
["Throw Flame"] = 101,
["Throw Frost"] = 101,
["Summon Demon"] = 101,
["Summon Dragon"] = 101,
["Summon Horrible Things"] = 101,
["Summon Greater Demon"] = 101,
Haunt = 101,
["Summon Hydra"] = 101,
["Malign Gateway"] = 101,
Haste = 101,
Invisibility = 101,
["Symbol of Torment"] = 101,
["Monstrous Menagerie"] = 101,
Silence = 101,
["Shadow Creatures"] = 101,
["Summon Vermin"] = 101,
["Summon Swarm"] = 101,
["Iskenderun's Battlesphere"] = 101,
["Fulminant Prism"] = 101,
["Summon Ice Beast"] = 101,
Swiftness = 101,
Blink = 101,
["Summon Butterflies"] = 101,
Shatter = 101,
Banishment = 101,
["Freezing Cloud"] = 101,
["Poisonous Cloud"] = 101,
["Mass Confusion"] = 101,
["Metabolic Englaciation"] = 101,
["Dispel Undead"] = 101,
Dig = 101,
Petrify = 101,
["Olgreb's Toxic Radiance"] = 101,
Paralyse = 101,
Polymorph = 101,
["Mephitic Cloud"] = 101,
Confuse = 101,
["Teleport Other"] = 101,
Slow = 101,
["Hellfire Burst"] = 101,
["Metal Splinters"] = 101,
["Energy Bolt"] = 101,
["Orb of Electricity"] = 101,
["Hellfire"] = 101,
["Summon Eyeballs"] = 10,
["Summon Greater Demon"] = 101
}
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
-- returns a number between 0 and 100: the percentage chance that a monster will have the spell
-- Monsters with no spellsets that contain the spell return 0.
-- Monsters with all spellsets containing the spell return 100.
-- Monsters that might have the spell, but it's too hard to calculate the percentage chance, return 101.
local function monster_has_spell(monster, spell_name)
if monster.Species == "lich" and monster.Name ~= "Boris" then
return lich_spells[spell_name] or 0
end
if monster.Name == "pandemonium lord" then
return panlord_spells[spell_name] or 0
end
if not monster.Spellsets then
return 0
end
local has = 0
local total = 0
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
break
end
end
end
return has * 100 / total
end
local function string_icmp(s1, s2)
return s1:lower() < s2:lower()
end
local disambig = {
centaur = "Centaur (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)",
}
function p.monsters_with_spell(frame)
local spell_name = name_arg(frame)
local always = {}
local sometimes = {}
for monster, monsterdata in pairs(data) do
local spell_chance = monster_has_spell(monsterdata, spell_name)
if spell_chance > 0 and not is_vault_only(monsterdata) then
monster = disambig[monster] or (monster:gsub("^%l", string.upper))
if spell_chance == 100 then
table.insert(always, monster)
else
table.insert(sometimes, {monster, spell_chance})
end
end
end
table.sort(always, string_icmp)
table.sort(sometimes, function(m1, m2) return string_icmp(m1[1], m2[1]) end)
local ret = {}
if #always > 0 then
table.insert(ret, "The following enemies cast " .. spell_name .. ":")
for _, monster in ipairs(always) do
table.insert(ret, "* " .. frame:expandTemplate{title = "monsterlink", args = {monster}})
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 _, monster in ipairs(sometimes) do
local name = monster[1]
local chance = monster[2]
local bullet = "* " .. frame:expandTemplate{title = "monsterlink", args = {name}}
if chance ~= 101 then
bullet = bullet .. (" (%.1f%% chance)"):format(monster[2])
end
table.insert(ret, bullet)
end
end
return table.concat(ret, "\n")
end
return p