Difference between revisions of "Module:Monster"
(Include a set of monsters that need to be disambiguated) |
(Improve monsters_with_spell) |
||
| Line 164: | Line 164: | ||
end | end | ||
| + | -- returns: | ||
| + | -- * 0 if monster doesn't have spell | ||
| + | -- * 1 if monster might have spell, depending on spell set | ||
| + | -- * 2 if monster always has spell | ||
local function monster_has_spell(monster, spell_name) | local function monster_has_spell(monster, spell_name) | ||
if not monster.Spellsets then | if not monster.Spellsets then | ||
| − | return | + | return 0 |
end | end | ||
| + | local has = false | ||
| + | local all = true | ||
for _, spellset in ipairs(monster.Spellsets) do | for _, spellset in ipairs(monster.Spellsets) do | ||
| + | local contains = false | ||
for _, spell in ipairs(spellset) do | for _, spell in ipairs(spellset) do | ||
if spell.Spell == spell_name then | if spell.Spell == spell_name then | ||
| − | + | contains = true | |
| + | break | ||
end | end | ||
end | end | ||
| + | has = has or contains | ||
| + | all = all and contains | ||
end | end | ||
| − | return | + | if has then |
| + | if all then | ||
| + | return 2 | ||
| + | end | ||
| + | return 1 | ||
| + | end | ||
| + | return 0 | ||
end | end | ||
| Line 212: | Line 228: | ||
function p.monsters_with_spell(frame) | function p.monsters_with_spell(frame) | ||
local spell_name = name_arg(frame) | local spell_name = name_arg(frame) | ||
| − | local | + | local always = {} |
| + | local sometimes = {} | ||
for monster, monsterdata in pairs(data) do | for monster, monsterdata in pairs(data) do | ||
| − | + | local has_spell = monster_has_spell(monsterdata, spell_name) | |
| − | + | if has_spell > 0 then | |
| + | monster = disambig[monster] or (monster:gsub("^%l", string.upper)) | ||
| + | if has_spell == 1 then | ||
| + | table.insert(sometimes, monster) | ||
| + | elseif has_spell == 2 then | ||
| + | table.insert(always, monster) | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | table.sort(always, string_icmp) | ||
| + | table.sort(sometimes, string_icmp) | ||
| + | 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 | ||
end | end | ||
| − | table. | + | 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 | |
| − | + | table.insert(ret, "* " .. frame:expandTemplate{title = "monsterlink", args = {monster}}) | |
| + | end | ||
end | end | ||
return table.concat(ret, "\n") | return table.concat(ret, "\n") | ||
Revision as of 03:23, 15 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
-- returns:
-- * 0 if monster doesn't have spell
-- * 1 if monster might have spell, depending on spell set
-- * 2 if monster always has spell
local function monster_has_spell(monster, spell_name)
if not monster.Spellsets then
return 0
end
local has = false
local all = true
for _, spellset in ipairs(monster.Spellsets) do
local contains = false
for _, spell in ipairs(spellset) do
if spell.Spell == spell_name then
contains = true
break
end
end
has = has or contains
all = all and contains
end
if has then
if all then
return 2
end
return 1
end
return 0
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 has_spell = monster_has_spell(monsterdata, spell_name)
if has_spell > 0 then
monster = disambig[monster] or (monster:gsub("^%l", string.upper))
if has_spell == 1 then
table.insert(sometimes, monster)
elseif has_spell == 2 then
table.insert(always, monster)
end
end
end
table.sort(always, string_icmp)
table.sort(sometimes, string_icmp)
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
table.insert(ret, "* " .. frame:expandTemplate{title = "monsterlink", args = {monster}})
end
end
return table.concat(ret, "\n")
end
return p