Module:Monster: Difference between revisions
More actions
mNo edit summary |
No edit summary |
||
| Line 401: | Line 401: | ||
end | end | ||
local will_value = tonumber(will) | local will_value = tonumber(will) | ||
if not will_value then | |||
return will | |||
end | |||
if will_value == 0 then | if will_value == 0 then | ||
return frame:expandTemplate{title = "Will None"} | return frame:expandTemplate{title = "Will None"} | ||
Revision as of 06:34, 28 May 2026
local p = {} local data = mw.loadData('Module: Table of monsters')
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
local function tile_name_arg(frame)
local tile_name = frame.args[2] local monster_name = frame.args[1] if not tile_name or tile_name == "" then tile_name = monster_name end if not tile_name or tile_name == "" then tile_name = mw.title.getCurrentTitle().text end return tile_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.tile_name = tile_name_arg(frame)
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, "
")
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, "
")
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, "
")
end
args.xp = monster.XP
args.holiness = frame:expandTemplate{title = monster.Holiness}
args.willpower = monster.Willpower
local avg_hp = monster["Average HP 10x"] / 10
local hp_min = math.max(math.floor(0.66 * avg_hp), 1)
local hp_max = math.ceil(1.33 * avg_hp)
args.hp_range = ("%d-%d"):format(hp_min, hp_max)
args.avg_hp = avg_hp
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, "
")
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", "
")
end
flavour = frame:expandTemplate{title = "flavour", args = {flavour}}
return infobox .. "\n" .. flavour
end
local spell_disambig = {
["Phantom Mirror"] = "Phantom Mirror (spell)",
}
local function spell_link(name)
if spell_disambig[name] then
return '[[' .. spell_disambig[name] .. '|' .. name .. ']]'
else
return '' .. name .. ''
end
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" local cleared = false for i, spells in ipairs(monster.Spellsets) do
ret = ret .. [=[
Spell set ]=] .. roman[i] .. "\n"
for j, spell in ipairs(spells) do
ret = ret .. '|-\n! align="left" | Slot' .. j .. '\n| ' .. spell_link(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, ",
ret = ret .. "|-\n|}"
cleared = i % 2 == 0 if cleared thenret = ret .. '' end end if not cleared thenret = ret .. '' end return ret end local panlord_spells = { -- 3.4% chance to have a particular one of these spells -- (3/4 chance to be a spellcaster, then 9/10 chance to have either summon or aoe, -- then 1/2 chance to choose an aoe spell, then there are 10 possible spells) ["Symbol of Torment"] = 3.4, ["Fire Storm"] = 3.4, ["Glaciate"] = 3.4, ["Chain Lightning"] = 3.4, ["Freezing Cloud"] = 3.4, ["Poisonous Cloud"] = 3.4, ["Metal Splinters"] = 3.4, ["Bolt of Devastation"] = 3.4, ["Orb of Electricity"] = 3.4, ["Conjure Ball Lightning"] = 3.4, -- 5.2% chance to have a particular one of these spells -- (3/4 chance to be a spellcaster, then 9/10 chance to choose one of them, then there are 13 possible spells) ["Call Down Damnation"] = 5.2, ["Lehudib's Crystal Spear"] = 5.2, ["Corrosive Bolt"] = 5.2, ["Quicksilver Bolt"] = 5.2, ["Orb of Destruction"] = 5.2, ["Energy Bolt"] = 5.2, ["Mindburst"] = 5.2, ["Bolt of Fire"] = 5.2, ["Bolt of Cold"] = 5.2, ["Iron Shot"] = 5.2, ["Poison Arrow"] = 5.2, ["Bolt of Draining"] = 5.2, ["Lightning Bolt"] = 5.2, -- 7.5% chance to have a particular one of these spells -- (3/4 chance to be a spellcaster, then 1/2 chance to choose one of them, then there are 5 possible spells) ["Haste"] = 7.5, ["Silence"] = 7.5, ["Invisibility"] = 7.5, ["Blink"] = 7.5, ["Blink Range"] = 7.5, -- 2.8% chance to have a particular one of these spells -- (3/4 chance to be a spellcaster, then 9/10 to choose one of either summon or aoe, -- then 1/4 chance to choose one of either a summon spell or a summon (greater) demon spell, -- then there are 6 possible spells) ["Haunt"] = 2.8, ["Malign Gateway"] = 2.8, ["Summon Dragon"] = 2.8, ["Summon Horrible Things"] = 2.8, ["Summon Eyeballs"] = 2.8, ["Summon Vermin"] = 2.8, -- ... and then there's a 1/2 chance to get this spell ["Blink Allies Encircling"] = 1.4, -- 8.4% chance to have a particular one of these two spells -- (3/4 chance to be a spellcaster, then 9/10 to choose one of either summon or aoe, -- then 1/4 chance to choose one of either a summon spell or a summon (greater) demon spell, -- then there are 2 possible spells) ["Summon Demon"] = 8.4, ["Summon Greater Demon"] = 8.4, -- 3.4% chance to have a particular one of these spells -- (3/4 chance to be a spellcaster, then 1/2 chance to choose one of them, then there are 11 possible spells) ["Dispel Undead Range"] = 3.4, ["Paralyse"] = 3.4, ["Sleep"] = 3.4, ["Mass Confusion"] = 3.4, ["Drain Magic"] = 3.4, ["Petrify"] = 3.4, ["Polymorph"] = 3.4, ["Force Lance"] = 3.4, ["Slow"] = 3.4, ["Sentinel's Mark"] = 3.4, ["Dimension Anchor"] = 3.4, -- 2.8% chance to have one of these spells -- (1/4 chance to be a non-caster, then 1/3 chance to choose one of them, then there are 3 possible spells) ["Blinkbolt"] = 2.8, ["Blink Close"] = 2.8, ["Harpoon Shot"] = 2.8, } local function is_vault_only(monster) if monster.Name == "test spawner" then return true end -- not placed in game, nor in vaults
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.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 function p.categorize_will(frame) local will = frame.args[1] if not will then return end if will == 'Immune' then return frame:expandTemplate{title = "Will Immune"} end
local will_value = tonumber(will)
if not will_value then
return will
end
if will_value == 0 then
return frame:expandTemplate{title = "Will None"}
end
local threshold = 0
local step = 40
local title = 'Will'
while threshold < 200 do
threshold = threshold + step title = title..'+' if will_value <= threshold then return frame:expandTemplate{title = title, args={will}} end end
return frame:expandTemplate{title = 'Will+++++', args={will}}
end return p |
|---|