Module:QuestDetails
Jump to navigation
Jump to search
Module documentation
This documentation is transcluded from Module:QuestDetails/doc. [edit] [history] [purge]
This module does not have any documentation. Please consider adding documentation at Module:QuestDetails/doc. [edit]
Module:QuestDetails's function details is invoked by Template:Quest details.
Module:QuestDetails requires Module:Infobox.
Module:QuestDetails requires Module:Mainonly.
Module:QuestDetails requires Module:Questreq.
Module:QuestDetails requires Module:Yesno.
Module:QuestDetails requires strict.
Module:QuestDetails is required by Module:Quest List.
| Function list |
|---|
| L 51 — p.details L 195 — subjectName L 203 — iconParam L 213 — iconDisp L 282 — yearParam L 297 — startDisp L 305 — membersDisp L 314 — lengthParam L 327 — requirementDisp L 376 — ironmanConcernsDisp L 389 — itemsDisp L 408 — followsEventsDisp L 436 — recommendedDisp L 448 — fullCompletionDisp L 462 — emptyToNone L 470 — yesnoParam L 484 — hasParamDefined L 488 — emptyToNil L 496 — questBucket L 510 — parseReqsSkill L 525 — parseReqsSkillLevel L 543 — onQuickGuide L 547 — addcategories |
-- {{Quest details}}
--
require("strict");
local onmain = require('Module:Mainonly').on_main
local yesno = require('Module:Yesno')
local infobox = require('Module:Infobox')
local lang = mw.language.getContentLanguage()
local quest_req, has_quest_req;
do
local _module_questreq = require("Module:Questreq");
quest_req = _module_questreq._main;
has_quest_req = _module_questreq.has_reqs;
end
local currentTitle = mw.title.getCurrentTitle()
local pagename = currentTitle.fullText
local subjectName, iconParam, iconDisp, yearParam, startDisp, membersDisp,
lengthParam, requirementDisp, ironmanConcernsDisp, itemsDisp,
followsEventsDisp, recommendedDisp, fullCompletionDisp, emptyToNone,
yesnoParam, hasParamDefined, emptyToNil, questBucket, onQuickGuide,
addcategories, parseReqsSkill, parseReqsSkillLevel
local p = {}
p.difficulties = {
["novice"] = { 0, "Novice", '[[File:Novice.svg|7px|Novice|link=]] Novice' },
["intermediate"] = { 1, "Intermediate", '[[File:Intermediate.svg|7px|Intermediate|link=]] Intermediate' },
["experienced"] = { 2, "Experienced", '[[File:Experienced.svg|7px|Experienced|link=]] Experienced' },
["master"] = { 3, "Master", '[[File:Master.svg|7px|Master|link=]] Master' },
["grandmaster"] = { 4, "Grandmaster", '[[File:Grandmaster.svg|7px|Grandmaster|link=]] Grandmaster' },
["special"] = { 250, "Special", '[[File:Special.svg|7px|Special|link=]] Special' },
["none"] = { 251, "N/A", "N/A" } -- special values for non-quest items
}
p.lengths = {
["seasonal"] = { 0, "Seasonal" },
["very short"] = { 1, "Very Short" },
["short"] = { 2, "Short" },
["short to medium"] = { 3, "Short to Medium" },
["medium"] = { 4, "Medium" },
["medium to long"] = { 5, "Medium to Long" },
["long"] = { 6, "Long" },
["long to very long"] = { 7, "Long to Very Long" },
["very long"] = { 8, "Very Long" },
["very, very long"] = { 9, "Very, Very Long" },
["none"] = { 10, "N/A" } -- special values for non-quest items
}
function p.details(frame)
local _args = frame:getParent().args
local ret = infobox.new(_args)
ret:defineParams {
{ name = 'name', func = subjectName },
{ name = 'icon', func = iconParam },
{ name = 'release_year', func = yearParam },
{ name = 'release_year_nth', func = 'numbers' },
{ name = 'iconDisp', func = { name = iconDisp, params = { 'icon', 'release_year', 'release_year_nth', 'age', 'name' } } },
{ name = 'start', func = 'has_content' },
{ name = 'start_path', func = 'has_content' },
{ name = 'startDisp', func = { name = startDisp, params = { 'start', 'start_path' } } },
{ name = 'members', func = yesnoParam },
{ name = 'membersDisp', func = { name = membersDisp, params = { 'members' } } },
{ name = 'length', func = lengthParam },
{ name = 'quest_requirement', func = yesnoParam },
{ name = 'quest_requirement_limit', func = 'numbers' },
{ name = 'quest_requirement_collapsed', func = yesnoParam },
{ name = 'requirements', func = 'has_content' },
{ name = 'ironman', func = 'has_content' },
{ name = 'full', func = 'has_content' },
{ name = 'fullDisp', func = { name = fullCompletionDisp, params = { 'full' } } },
{ name = 'follows', func = 'has_content' },
{ name = 'followsDisp', func = {
name = followsEventsDisp,
params = { 'follows', 'name', 'quest_requirement_limit' }
} },
{ name = 'items', func = 'has_content' },
{ name = 'itemsDisp', func = { name = itemsDisp, params = { 'items' } } },
{ name = 'recommended', func = 'has_content' },
{ name = 'recommendedDisp', func = { name = recommendedDisp, params = { 'recommended' } } },
{ name = 'kills', func = emptyToNone },
}
local quest_bucket_params = {
icon = 'icon',
length = 'official_length',
release_year = 'official_release_year',
release_year_nth = 'release_year_nth',
requirements = 'requirements',
requirement_skill = 'requirement_skill',
requirement_skill_level = 'requirement_skill_level',
quest_bucket = 'json',
}
ret:defineParams {
{ name = 'quest_bucket', func = {
name = questBucket,
params = { 'start', 'length', 'requirements', 'items', 'kills', 'members' }
} },
}
ret:defineParams {
{ name = 'requirement_skill', func = {
name = parseReqsSkill,
params = { 'requirements' }
} },
}
ret:defineParams {
{ name = 'requirement_skill_level', func = {
name = parseReqsSkillLevel,
params = { 'requirements' }
} },
}
ret:customButtonPlacement(true)
ret:setAddRSWInfoboxClass(false)
ret:setNameCheck(false)
ret:create()
ret:cleanParams()
if not onQuickGuide() then
ret:useBucket('quest', quest_bucket_params)
end
ret:addClass('questdetails plainlinks')
ret:attr { cellspacing = '3' }
local auto_width = { ["max-width"] = "85%", ["width"] = "auto" }
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = 'Start point' },
{ tag = 'argd', class = "questdetails-info", content = 'startDisp', css = auto_width },
{ tag = 'argd', class = "text-align-center", content = 'iconDisp', rowspan = 3,
css = { ["vertical-align"] = "top" }
}
}
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = '[[Members]]' },
{ tag = 'argd', class = "questdetails-info", content = 'membersDisp', css = auto_width }
}
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = "Length<br/><small>''as displayed in-game''</small>" },
{ tag = 'argd', class = "questdetails-info", content = 'length', colspan = 2 }
}
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = 'Requirements' },
{ tag = 'td', class = "questdetails-info qc-active", content = requirementDisp(ret), colspan = 2 }
}
if hasParamDefined(ret, 'follows') then
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = 'Follows events' },
{ tag = 'argd', class = "questdetails-info qc-active", content = 'followsDisp', colspan = 2 },
}
end
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = 'Required items' },
{ tag = 'argd', class = "questdetails-info", content = 'itemsDisp', colspan = 2 }
}
if hasParamDefined(ret, 'recommended') then
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = 'Recommended' },
{ tag = 'argd', class = "questdetails-info", content = 'recommendedDisp', colspan = 2 },
}
end
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = 'Combat' },
{ tag = 'argd', class = "questdetails-info", content = 'kills', colspan = 2 }
}
if hasParamDefined(ret, 'full') then
ret:addRow {
{ tag = 'th', class = "questdetails-header", content = 'Full completion' },
{ tag = 'argd', class = "questdetails-info qc-active", content = 'fullDisp', colspan = 2 }
}
end
ret:finish()
if onmain() and not onQuickGuide() then
local allargs = ret:param('all')
local catargs = ret:categoryData()
ret:wikitext(addcategories(allargs, catargs))
end
return ret:tostring()
end
function subjectName(arg)
if infobox.isDefined(arg) then
return arg
end
return pagename:gsub("/Quick guide", "")
end
function iconParam(icon)
if infobox.isDefined(icon) then
icon = icon:gsub('%[',''):gsub('%]',''):gsub('[Ff]ile:',''):gsub('{{!}}','|')
icon = mw.text.split(icon, '|')
return icon[1]
else
return nil
end
end
function iconDisp(icon, year, year_nth, age, name)
local node = mw.html.create('div')
:css('position', 'relative')
:css('float', 'right')
if infobox.isDefined(icon) then
node:wikitext('[[File:' .. icon .. '|50px]]')
else
node:tag('div')
:css({ width = '50px', height = '50px' })
end
node
:tag('br'):done()
:tag('span')
:wikitext(year)
if infobox.isDefined(year_nth) then
node:tag('sup')
:css('display', 'none')
:wikitext('#' .. year_nth)
:done()
end
if infobox.isDefined(age) then
age = age:lower()
else
local result = bucket('infobox_quest')
.select('official_age')
.where('name', name)
.where('official_age', '!=', bucket.Null())
.limit(1)
.run()
if #result > 0 then
age = result[1].official_age:lower()
end
end
if age == '5' then
node:tag('div')
:css('position', 'absolute')
:css('top', '0')
:css('right', '0')
:css('pointer-events', 'none')
:wikitext('[[File:Fifth age quest icon.png|50px]]')
:done()
elseif age == '6' then
node:tag('div')
:css('position', 'absolute')
:css('top', '0')
:css('right', '0')
:css('pointer-events', 'none')
:wikitext('[[File:Sixth age quest icon.png|50px]]')
:done()
elseif age == 'chaos' or age == 'age of chaos' then
node:tag('div')
:css('position', 'absolute')
:css('top', '0')
:css('right', '0')
:css('pointer-events', 'none')
:wikitext('[[File:Sixth age quest icon.png|50px]]')
:done()
end
node:done()
return tostring(node)
end
function yearParam(y)
if not infobox.isDefined(y) then
return nil
end
local yr = tonumber(y)
-- Keep the lower bound check only
-- if yr >= 2001 and yr <= tonumber(os.date("%Y")) + 1 then
if yr >= 2001 then
return yr
else
return nil
end
end
function startDisp(start, startPath)
if infobox.isDefined(startPath) then
return '[[File:Quest map icon.png|17px|link=]] ' .. start .. " " .. startPath
else
return '[[File:Quest map icon.png|17px|link=]] ' .. start
end
end
function membersDisp(members)
if not infobox.isDefined(members) then
return nil
end
return members == 'Yes' and '[[File:P2P icon.png|30px|link=]] Members only' or
'[[File:F2P icon.png|30px|link=]] Free to play'
end
function lengthParam(length)
if not infobox.isDefined(length) then
return nil
end
local len = length:lower()
if len == "no" or len == "none" or len == "n/a" then
return p.lengths.none[2]
end
return p.lengths[len][2]
end
function requirementDisp(ret)
local name = ret:param('name')
if not infobox.isDefined(name) then
return nil
end
local quest_req_limit = ret:param('quest_requirement_limit')
if not infobox.isDefined(quest_req_limit) then
quest_req_limit = 5
end
local quest_req_collapsed = ret:param('quest_requirement_collapsed')
if not infobox.isDefined(quest_req_collapsed) then
quest_req_collapsed = false
end
local req = mw.html.create()
local must_show_quest_req = ret:param('quest_requirement')
if not infobox.isDefined(must_show_quest_req) then
must_show_quest_req = nil
end
local extra = ret:param('requirements')
if must_show_quest_req == 'Yes' or (must_show_quest_req ~= 'No' and not string.find(extra, "questreq")) then
req:node(quest_req(name, quest_req_limit, quest_req_collapsed)):newline()
else -- Skip adding questreq if it does
req = req:newline()
end
local has_requirement = has_quest_req(name)
if infobox.isDefined(extra) then
req:wikitext(extra):newline()
has_requirement = true
end
local ironman_conerns = ret:param('ironman')
if infobox.isDefined(ironman_conerns) then
req:node(ironmanConcernsDisp(ironman_conerns)):newline()
has_requirement = true
end
if not has_requirement then
req:wikitext('* None')
end
return tostring(req)
end
function ironmanConcernsDisp(concerns)
return mw.html.create()
:tag('table')
:addClass('mw-collapsible mw-collapsed')
:css('background', 'none')
:tag('tr'):tag('th')
:addClass('inventory-image'):wikitext("[[File:Ironman badge.png|link=]] [[File:Hardcore Ironman badge.png|link=]] '''Ironmen:'''")
:tag('tr'):tag('td')
:newline()
:wikitext(concerns)
:allDone()
end
function itemsDisp(items)
local node = mw.html.create()
:tag('i')
:wikitext('Items from the [[tool belt]] are not listed unless they do not work or are not automatically added.')
:done()
if not infobox.isDefined(items) then
node:newline():wikitext("* None")
return tostring(node)
end
node
:tag('div')
:addClass('lighttable checklist')
:newline()
:wikitext(items)
return tostring(node)
end
function followsEventsDisp(recommended, name, quest_req_limit)
if not infobox.isDefined(recommended) then
return nil
end
local b = yesno(recommended)
if b == false then
return nil
end
local remark
if b == true then
remark = "''For full storyline comprehension, completion of the following quests is recommended but not required.''"
else
remark = recommended
end
if not infobox.isDefined(quest_req_limit) then
quest_req_limit = 3
end
local node = mw.html.create()
:wikitext(remark)
:newline()
:node(quest_req('Follows:' .. name, quest_req_limit, true))
return tostring(node)
end
function recommendedDisp(recommended)
if not infobox.isDefined(recommended) then
return nil
end
local node = mw.html.create('div')
:addClass('qc-active lighttable checklist')
:newline()
:wikitext(recommended)
return tostring(node)
end
function fullCompletionDisp(requirements)
if not infobox.isDefined(requirements) then
return nil
end
local node = mw.html.create()
:tag('i')
:wikitext('For full completion and unlocking additional rewards, the following is required.')
:done()
node:newline():wikitext(requirements)
return tostring(node)
end
function emptyToNone(value)
if not infobox.isDefined(value) then
return "* None"
end
return value
end
function yesnoParam(p)
if infobox.isDefined(p) then
local b = yesno(p)
if b then
return 'Yes'
elseif not b then
return 'No'
else
return nil
end
end
return nil
end
function hasParamDefined(ret, name)
return infobox.isDefined(ret:param(name))
end
function emptyToNil(value)
if not infobox.isDefined(value) then
return nil
end
return value
end
function questBucket(start, length, requirements, items, kills, members)
local bucketJSON = {
name = currentTitle.text,
start = emptyToNil(start),
length = emptyToNil(length),
requirements = emptyToNil(requirements),
items = emptyToNil(items),
kills = emptyToNil(kills),
members = emptyToNil(members),
}
return mw.text.jsonEncode(bucketJSON)
end
function parseReqsSkill(requirements)
-- extracts all the skills from the skillreq html stored in the requirements
local split = mw.text.split(requirements,'*')
local out = {}
for _,s in ipairs(split) do
s = mw.ustring.match(s,'^%s*<span class=\"skillreq\" data%-skill="[^"]*"')
if s~=nil then
s = mw.ustring.gsub(s,'^%s*<span class=\"skillreq\" data%-skill="','')
s = mw.ustring.gsub(s,'"$','')
table.insert(out,s)
end
end
return out
end
function parseReqsSkillLevel(requirements)
-- extracts all the skills and their levels from the skillreq html stored in the requirements
local split = mw.text.split(requirements,'*')
local out = {}
for _,s in ipairs(split) do
local s1 = mw.ustring.match(s,'^%s*<span class=\"skillreq\" data%-skill="[^"]*"')
local s2 = mw.ustring.match(s,'data%-level="[^"]*"')
if s1~=nil and s2~=nil then
s1 = mw.ustring.gsub(s1,'^%s*<span class=\"skillreq\" data%-skill="','')
s1 = mw.ustring.gsub(s1,'"$','')
s2 = mw.ustring.gsub(s2,'^data%-level="','')
s2 = mw.ustring.gsub(s2,'"$','')
table.insert(out,s1..':'..s2)
end
end
return out
end
function onQuickGuide()
return currentTitle.subpageText == "Quick guide"
end
function addcategories(args, catargs)
local ret = {}
local cat_map = {
-- Added if the parameter has content
defined = {
ironman = 'Quests with Ironman requirements'
},
-- Added if the parameter has no content
notdefined = {
length = 'Needs official length added',
release_year = 'Needs official release year added',
},
}
local length = args.length.d
if infobox.isDefined(length) and length ~= "N/A" then
local cat = string.format("%s quests", length)
table.insert(ret, cat)
end
local release_year = args.release_year.d
if infobox.isDefined(release_year) then
local cat = string.format("Quests released in %s", release_year)
table.insert(ret, cat)
end
local requirements = args.requirements.d
if infobox.isDefined(requirements) then
for _, requirement in ipairs(mw.text.split(requirements, "\n")) do
local data_skill_name = requirement:match('data%-skill=\"([a-zA-Z %-]+)\"')
if data_skill_name then
data_skill_name = lang:ucfirst(data_skill_name:lower())
local cat = "Quests with " .. data_skill_name .. " requirement"
table.insert(ret, cat)
end
end
end
-- Run and add mapped categories
for n, v in pairs(cat_map.defined) do
if catargs[n] and catargs[n].one_defined then
table.insert(ret, v)
end
end
for n, v in pairs(cat_map.notdefined) do
if catargs[n] and not catargs[n].all_defined then
table.insert(ret, v)
end
end
-- combine table and format category wikicode
for i, v in ipairs(ret) do
if v ~= '' then
ret[i] = string.format('[[Category:%s]]', v)
end
end
return table.concat(ret, '')
end
return p
-- </nowiki>