Module:Monster

local Monster = {}

-- Libraries of functions --

-- Loads high frequency functions local HF = require('Module:HF') -- Parses invocation and template parameters, trims whitespace, and removes blanks. local getArgs = require('Dev:Arguments').getArgs -- Language functions for the default language local lang = mw.language.getContentLanguage

-- local yesno = require( 'Module:Yesno' ) local yesno = require( 'Dev:Yesno' ) --- -- Libraries of data -- ---

-- Local functions (used only in this Module) --

local pagetitle, namespace, content = mw.title.getCurrentTitle.fullText, mw.title.getCurrentTitle.namespace, mw.title.getCurrentTitle.isContentPage local testcase = (mw.title.getCurrentTitle:inNamespace( 10 )    and mw.title.getCurrentTitle.text:lower:match('testcases$')) and true local documentation = (mw.title.getCurrentTitle:inNamespace( 10 )    and mw.title.getCurrentTitle.text:lower:match('doc$')) and true

local backrefcheck = require('Module:HF')._backrefargs --[==[	ret:defineParams{ { name = 'poisonous', func = { name = poisonarg, params = { 'poisonous' }, flag = 'p' } }, { name = 'level', func = combatarg }, { name = 'style', func = stylearg }, { name = 'lifepoints', func = numargcommas }, { name = 'experience', func = numargcommas }, { name = 'xpraw', func = { name = numargraw, params = { 'experience' }, flag = 'p' } }, { name = 'hpxp', func = { name = hpxparg, params = { 'experience' } } }, { name = 'wepxp2h', func = numargcommas }, { name = 'wepxpmhandarmour', func = numargcommas }, { name = 'wepxpoh', func = numargcommas }, { name = 'wepxp', func = { name = wepxparg, params = { 'experience', 'wepxp2h', 'wepxpmhandarmour', 'wepxpoh' } } }, { name = 'weakness', func = weaknessarg }, { name = 'explicit_weakness', func = { name = explicitwkarg, params = { 'weakness' }, flag = 'p' } }, { name = 'aff_weakness', func = { name = aff_weakness_arg, params = { 'aff_weakness', 'aff_weakness', 'weakness' }, flag = { 'p', 'r', 'p' } } }, { name = 'abilities', func = abilarg }, { name = 'assigned_by', func = slayerarg }, { name = 'not_assigned', func = { name = notassarg, params = { 'assigned_by' }, flag = 'p' } }, { name = 'release', func = 'release' }, { name = 'removal', func = 'removal' }, { name = 'members', func = 'has_content' }, { name = 'examine', func = 'has_content' }, { name = 'speed', func = speedarg }, { name = 'name', func = 'name' }, { name = 'aka', func = 'has_content' }, { name = 'image', func = 'image' }, { name = 'chathead', func = 'image' }, { name = 'slayercat', func = 'has_content' }, -- not used; only for categories { name = 'id', func = 'numbers' }, { name = 'rscid', func = 'numbers' } } --]==] local function rawnumber(value) if value then -- in case it's a number in comma-grouped form, -- or (more likely) a string -- make it a pure number value = lang:parseFormattedNumber( value ) value = tonumber( value ) and tonumber( value ) or value return value else return nil end end local function groupednumber(value) if value then -- in case it's a number in comma-grouped form, -- or (more likely) a string -- make it a pure number value = rawnumber(value) -- if it's a pure number, -- put it in comma-grouped form value = type(value) == 'number' and lang:formatNum( value ) or value return value else return nil end end --[===[ local function poisonous(arg) arg = string.gsub(arg or ,',',) arg = string.lower(arg) if arg == 'yes' then arg = 'Yes check.svg ??? '	elseif tonumber(arg) then arg = ' Yes check.svg '..tonumber(arg)..' ' else arg = ' X mark.svg ' end return arg end -- For numerical args local function numberargs(arg,v) local arg_v = (arg or ):find('%S') and string.gsub(arg,',',) or -1 local arg_i if arg_v == -1 then arg_i = nil elseif string.lower(arg_v) == 'n/a' then arg_i = 'N/A' elseif string.lower(arg_v) == 'varies' then arg_i = 'Varies' else arg_i = tonumber(arg_v:gsub(',',''),10) if not arg_i then arg_i = badarg(v,'should be a single numerical value.') end end return arg_i end -- For combat level local function combatarg(arg) local arg_v = (arg or ):find('%S') and string.gsub(arg,',',) or -1 local arg_i if arg_v == -1 then arg_i = nil elseif string.lower(arg_v) == 'n/a' or tonumber(arg_v) == 0 then arg_i = 'N/A' else arg_i = tonumber(arg_v:gsub(',',''),10) if not arg_i then arg_i = badarg('level','should be a single numerical value.') end end return arg_i end -- For numbers (adds commas) local function numargcommas(arg) local ret = numberargs(arg) if type(ret) == 'number' then return commas(ret) else return ret end end -- For numbers local function numargraw(arg) return tonumber(arg) or 0 end -- For hp xp local function hpxparg(arg) local xp = string.gsub(arg or ,',',) if string.lower(xp) == 'n/a' then return 'N/A' else xp = tonumber(xp) end if type(xp) == 'number' then return commas(math.floor(xp*3.3)/10) else return nil end end -- weapon xp local function wepxparg(combatxp, wepxp2h, wepxpmhandarmour, wepxpoh) local xp = string.gsub(combatxp or ,',',) local xp2h = string.gsub(wepxp2h or ,',',) local xpmh = string.gsub(wepxpmhandarmour or ,',',) local xpoh = string.gsub(wepxpoh or ,',',) if string.lower(xp) == 'n/a' then return 'N/A' else xp = tonumber(xp) end if string.lower(xp2h) ~= '' then xp2h = tonumber(xp2h) end if string.lower(xpmh) ~= '' then xpmh = tonumber(xpmh) end if string.lower(xpoh) ~= '' then xpoh = tonumber(xpoh) end if type(xp) == 'number' then local th,mh,oh if type(xp2h) == 'number' then th = xp2h else th = math.floor(xp * .06) end if type(xpmh) == 'number' then mh = xpmh else mh = math.floor(xp * .04) end if type(xpoh) == 'number' then oh = xpoh else oh = math.floor(xp * .02) end return string.format('%s / %s / %s',th,mh,oh) else return nil end end -- For true/false local function boolargs(arg,argr) local arg_v = (arg or ''):find('%S') and arg or -1 local arg_ret if arg_v == -1 then arg_ret = nil else arg_v = yesno(arg_v) local argf = ' %s ' local f1,f3 local f2 = string.gsub(argr,'_',' ') if arg_v then f1 = '' f3 = 'Yes check.svg' else f1 = ' not' f3 = 'X mark.svg' end arg_ret = string.format(argf,f1,f2,f3) end return arg_ret end -- Poison local function poisonarg(arg) arg = string.gsub(arg or ,',',) arg = string.lower(arg) if arg == 'yes' then arg = 'Yes check.svg ??? '	elseif tonumber(arg) then arg = ' Yes check.svg '..tonumber(arg)..' ' else arg = ' X mark.svg ' end return arg end -- style local function stylearg(arg) -- split by commas local atts = mw.text.split(string.lower(arg or ''),'%s*,%s*') local _atts = {} -- remake the list as a table, remove anything that's blank/doesn't exist for _, v in ipairs(atts) do		local att_x = styles_map[v] if att_x then table.insert(_atts,attack_styles[att_x]) end end local p_att if #_atts == 0 then p_att = nil else p_att = {} for _, v in ipairs(_atts) do			table.insert(p_att,string.format('%s.png',v.image,v.link)) end p_att = table.concat(p_att,' ') end return p_att end -- weakness local function weaknessarg(arg) -- split by commas local wk = mw.text.split(string.lower(arg or ''),'%s*,%s*') local _wk = {} -- remake the list as a table, remove anything that's blank/doesn't exist for _, v in ipairs(wk) do		local wk_x = weaknesses[v] if wk_x then table.insert(_wk,wk_x) end end local p_wk if #_wk == 0 then p_wk = nil else p_wk = {} for _, v in ipairs(_wk) do			table.insert(p_wk,string.format('%s',v.image,v.link)) end p_wk = table.concat(p_wk,' ') end return p_wk end -- Explicit weakness -- weakness / explicit weakness local function explicitwkarg(arg) -- split by commas local wk = mw.text.split(string.lower(arg or ''),'%s*,%s*') local _wk = weaknesses[wk[1]] if _wk then return string.format('%s',_wk.image) else return nil end end local function aff_weakness_arg(aff,aff_r,weakness) -- split by commas local wk = mw.text.split(string.lower(weakness or ''),'%s*,%s*') local _wk = weaknesses[wk[1]] if _wk and _wk.text == 'Nothing' then return '-' else return numberargs(aff,aff_r) end end -- Slayer assigners local function slayerarg(_arg) local arg = _arg if arg then arg = mw.text.split(string.lower(arg),'%s*,%s*') local slayer_master_list = { turael = false, spria = false, mazchna = false, achtryn = false, vannaka = false, chaeldar = false, sumona = false, duradel = false, lapalok = false, kuradal = false, morvran = false, none = false }		for _, v in ipairs(arg) do			if slay_masters_map[v] then slayer_master_list[slay_masters_map[v]] = true end end if slayer_master_list.none then arg = 'Not assigned' else arg = {} for n, v in pairs(slayer_master_list) do				if v then --table.insert(params.assigned_by, slay_masters[n].text) table.insert(arg, 1, string.format('%s.png',slay_masters[n].chathead,n)) end end arg = table.concat(arg,' ') end else arg = nil end return arg end -- Not assigned local function notassarg(arg) if arg then if arg:find('%?action=edit') or not arg:find('%S') then return true end arg = mw.text.split(string.lower(arg),'%s*,%s*') for _, v in ipairs(arg) do			if slay_masters_map[v] == 'none' then return true end end end return false end -- Abilities used local function abilarg(arg) arg = paramtest.default_to(arg,false) if not arg then arg = 'None' elseif arg:find('clickpic') then arg = mw.getCurrentFrame:preprocess(arg) else arg = 'None' end return arg end -- Attacks speed local function speedarg(arg) if paramtest.is_empty(arg) then return nil end _,_,arg = string.find(arg:lower..' ','^(.-)%s') arg = tostring(attack_speed_bar(speed_map[arg])) or badarg('speed',' is not a valid attack speed') return arg end -- red ERR span with title hover for explanation local function badarg(argname, argmessage) return ''.. 'ERR ' end

--- in Infobox -- map of names to pre-defined functions, used by Infobox:defineParams local func_map = { name = subjectName, release = { name = releaseUpdate, params = { 'release', 'update' }, flag = 'p' }, removal = { name = releaseUpdate, params = { 'removal', 'removalupdate' }, flag = 'p' }, has_content = hasContent, hasContent = hasContent, image = image, numbers = numbers, } --]===] -- -- Public functions (called from a Template or article) -- -- function Monster.name(frame) local name = getArgs(frame, { parentOnly = true })['name'] or pagetitle local version = getArgs(frame, { frameOnly = true })[1] local name = content and ('%s%s'):format(name, HF.Category('Bestiary')) or name local id = backrefcheck('id', version, args) local version_name = version and (backrefcheck('version', version, args) or 'Version'..version) or nil if id and content then mw.smw.set{ ['NPC ID' .. (version or '')] = id } if version then mw.smw.subobject( { ['NPC ID'] = id }, version_name ) end end return name or nil end function Monster.image(frame) local invargs = getArgs(frame, { frameOnly = true }) local args = getArgs(frame, { parentOnly = true }) local galleryitems = {} -- function is either versioned or consolidated local image = args['image'] -- for a parameter, identify multiple entries -- specialStripFileWrapper(args['image']) -- placing local chatheads = args['chathead'] -- parse each parameter end function Monster.HeadGalleryItems(frame) local args = getArgs(frame, { parentOnly = true }) local out = {} local name = args['name'] or pagetitle local image = args['image'] local imagecount = 0 for file in string.gmatch(image or '','%b[]') do       imagecount=imagecount+1 end if image then for i in image:gmatch('%b[]') do       local caption = nil local filename, meta = i:match('%[%[(File:[^%|]*)%|?([^%]]*)') for fileparam in mw.text.gsplit(meta, '|') do           fileparam = mw.text.trim(fileparam) for filter in mw.text.gsplit('border,frameless,frame,thumb,thumbnail,%d+px,x%d+px,%d+x%d+px,upright,left,right,center,none,baseline,sub,super,top,text-top,middle,bottom,text-bottom,link=.*,alt=',',') do               fileparam = fileparam:gsub('^'..filter..'$','') end caption = (fileparam ~= '' and not caption) and fileparam end if imagecount > 1 then table.insert(out, ('%s|%s'):format(filename, caption or tostring(imagecount))) else table.insert(out, ('%s|%s'):format(filename, 'Detail')) end end end table.insert(out,        (args['chathead'] or name..' chathead.png') ..        '|Chathead'    ) for i=1,100,1 do       if args['chathead'..i] then table.insert(out,            HF._stripFileWrapper(args['chathead'..i]) .. '|Chathead'        ) end end return table.concat(out,'\n') end function Monster.release(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local release = backrefcheck('release', version, args) local update = backrefcheck('update', version, args) local version_name = version and (backrefcheck('version', version, args) or 'Version'..version) or nil local default = content and ('%s%s'):format('Unknown', HF.Category('Needs release date')) or 'Unknown' default = (content or testcase or documentation) and default or nil default = (version == nil and not backrefcheck('release', 1, args)) and default or nil if release and content then release_plaintext = release:gsub('%[%[',):gsub('%]%]',) mw.smw.set{ ['Release date' .. (version or '')] = release_plaintext } if version then mw.smw.subobject( { ['Release date'] = release_plaintext }, version_name ) end end --    -- Dont show default for version = nil if backrefcheck('release', 1, args) return release and ('%s (%s)'):format(        release,        update             and HF.Link('Update:'..update, 'Update')            or 'Update unknown'        ) or default end function Monster.removal(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local removal = backrefcheck('removal', version, args) local removalupdate = backrefcheck('removalupdate', version, args) return removal and ('%s (%s)'):format(        removal,        removalupdate             and HF.Link('Update:'..removalupdate, 'Update')            or 'Update unknown'        ) or nil end function Monster.aka(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local aka = backrefcheck('aka', version, args) local aka = (aka and content) and ('%s%s'):format(aka, HF.Category('Pages with AKA')) or aka return aka or nil end function Monster.members(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local members = backrefcheck('members', version, args) local categories = {} local version_name = version and (backrefcheck('version', version, args) or 'Version'..version) or nil local default = content and ('%s%s'):format('Unknown', HF.Category('Needs members status')) or 'Unknown' default = (content or testcase or documentation) and default or nil default = (version == nil and not backrefcheck('members', 1, args)) and default or nil if members and content then if yesno(members) == true then mw.smw.set{ ['Is members only' .. (version or '')] = yesno(members) }           if version then mw.smw.subobject( { ['Is members only'] = yesno(members) }, version_name ) end table.insert(categories, HF.Category('P2P bestiary')) end if yesno(members) == false then mw.smw.set{ ['Is members only' .. (version or '')] = yesno(members) }           if version then mw.smw.subobject( { ['Is members only'] = yesno(members) }, version_name ) end table.insert(categories, HF.Category('F2P bestiary')) end members = members .. table.concat(categories) end return members or default end function Monster.examine(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local examine = backrefcheck('examine', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Needs examine added')) or 'Unknown' default = (content or testcase or documentation) and default or nil default = (version == nil and not backrefcheck('examine', 1, args)) and default or nil return examine or default end function Monster.numberofkills(frame) end function Monster.level(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local level = backrefcheck('level', version, args) local version_name = version and (backrefcheck('version', version, args) or 'Version'..version) or nil local default = content and ('%s%s'):format('Unknown', HF.Category('Needs combat level')) or 'Unknown' default = (content or testcase or documentation) and default or nil default = (version == nil and not backrefcheck('level', 1, args)) and default or nil if level and content then level = tonumber(rawnumber(level)) and rawnumber(level) or level if type(level) == 'number' then mw.smw.set{ ['Combat level' .. (version or '')] = level } if version then mw.smw.subobject( { ['Combat level'] = level }, version_name ) end elseif level:lower:match('n/a') or level == 0 then level = 'N/A' else level = level .. mw.smw.info( 'Should be a single numerical value.', 'warning' ) end end return level or default end function Monster.lifepoints(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local lifepoints = backrefcheck('lifepoints', version, args) lifepoints = groupednumber(lifepoints) local version_name = version and (backrefcheck('version', version, args) or 'Version'..version) or nil if content and lifepoints then mw.smw.set{ ['Life points' .. (version or '')] = lifepoints } if version then mw.smw.subobject( { ['Life points'] = lifepoints }, version_name ) end end return lifepoints or nil end function Monster.experience(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local experience = backrefcheck('experience', version, args) if experience then experience = content and (' %s ') :format(groupednumber(experience)) or groupednumber(experience) end local default = content and (' %s%s ') :format('Unknown', HF.Category('Needs XP per kill')) or 'Unknown' default = (content or testcase or documentation) and default or nil default = (version == nil and not backrefcheck('experience', 1, args)) and default or nil return experience or default end function Monster.hpxp(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local hpxp = backrefcheck('hpxp', version, args) if hpxp then hpxp = tonumber(rawnumber(hpxp)) and rawnumber(hpxp) or hpxp hpxp = type(hpxp) == 'number' and (math.floor(hpxp*3.3)/10) hpxp = tostring(hpxp):lower == 'n/a' and tostring(hpxp):upper hpxp = content and (' %s ') :format(groupednumber(hpxp)) or groupednumber(hpxp) end --[==[   	local xp = string.gsub(arg or ,',',) if string.lower(xp) == 'n/a' then return 'N/A' else xp = tonumber(xp) end if type(xp) == 'number' then return commas(math.floor(xp*3.3)/10) else return nil end --]==]   return hpxp or nil end function Monster.wepxp(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local hpxp = backrefcheck('hpxp', version, args) --[==[   { 'experience', 'wepxp2h', 'wepxpmhandarmour', 'wepxpoh' } local xp = string.gsub(combatxp or ,',',) local xp2h = string.gsub(wepxp2h or ,',',) local xpmh = string.gsub(wepxpmhandarmour or ,',',) local xpoh = string.gsub(wepxpoh or ,',',) if string.lower(xp) == 'n/a' then return 'N/A' else xp = tonumber(xp) end if string.lower(xp2h) ~= '' then xp2h = tonumber(xp2h) end if string.lower(xpmh) ~= '' then xpmh = tonumber(xpmh) end if string.lower(xpoh) ~= '' then xpoh = tonumber(xpoh) end if type(xp) == 'number' then local th,mh,oh if type(xp2h) == 'number' then th = xp2h else th = math.floor(xp * .06) end if type(xpmh) == 'number' then mh = xpmh else mh = math.floor(xp * .04) end if type(xpoh) == 'number' then oh = xpoh else oh = math.floor(xp * .02) end return string.format('%s / %s / %s',th,mh,oh) else return nil end --]==] end function Monster.aggressive(frame) -- yesno end function Monster.poisonous(frame) -- yesno end function Monster.slaylvl(frame) end function Monster.slayxp(frame) end function Monster.slayercat(frame) end function Monster.assigned_by(frame) end function Monster.max_melee(frame) end function Monster.max_ranged(frame) end function Monster.max_magic(frame) end function Monster.max_spec(frame) end function Monster.style(frame) end function Monster.speed(frame) end function Monster.attack(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local attack = backrefcheck('attack', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Missing combat skill levels')) or 'Unknown' return attack or default end function Monster.ranged(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local ranged = backrefcheck('ranged', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Missing combat skill levels')) or 'Unknown' return ranged or default end function Monster.magic(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local magic = backrefcheck('magic', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Missing combat skill levels')) or 'Unknown' return magic or default end function Monster.acc_melee(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local melee = backrefcheck('acc_melee', version, args) return melee or nil end function Monster.acc_ranged(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local ranged = backrefcheck('acc_ranged', version, args) return ranged or nil end function Monster.acc_magic(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local magic = backrefcheck('acc_magic', version, args) return magic or nil end function Monster.armour(frame) end function Monster.defence(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local defence = backrefcheck('defence', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Missing combat skill levels')) or 'Unknown' return defence or default end function Monster.weakness(frame) end function Monster.aff_weakness(frame) end function Monster.aff_melee(frame) end function Monster.aff_ranged(frame) end function Monster.aff_magic(frame) end function Monster.immune_to_poison(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local immune_to_poison = backrefcheck('immune_to_poison', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Missing immunity information')) or 'Unknown' return immune_to_poison or default end function Monster.immune_to_deflect(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local immune_to_deflect = backrefcheck('immune_to_deflect', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Missing immunity information')) or 'Unknown' return immune_to_deflect or default end function Monster.immune_to_stun(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local immune_to_stun = backrefcheck('immune_to_stun', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Missing immunity information')) or 'Unknown' return immune_to_stun or default end function Monster.immune_to_drain(frame) local version = getArgs(frame, { frameOnly = true })[1] or nil local args = getArgs(frame, { parentOnly = true }) local immune_to_drain = backrefcheck('immune_to_drain', version, args) local default = content and ('%s%s'):format('Unknown', HF.Category('Missing immunity information')) or 'Unknown' return immune_to_drain or default end - -- Internal functions (used in this and other Modules) -- - - -- Output (send it back to whatever called it) -- - return Monster