Module:Achievement

From Downtime Wiki

Usage

Called by {{#if:{{#switch: yes

| y | yes | t | true  | on  | 1 = yes
| n | no  | f | false | off | 0 | = 
| =  
| #default = yes

}}|}}{{{{#if: |subst: }}[[{{#if: {{#pos: AchievementRow | : }} || Template: }}AchievementRow|{{ #ifeq: | Template | AchievementRow | AchievementRow }}]]{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}}}{{#if:{{#switch: yes

| y | yes | t | true  | on  | 1 = yes
| n | no  | f | false | off | 0 | = 
| =  
| #default = yes

}}|}}{{#if: {{#pos: AchievementRow | File: }}{{#pos: AchievementRow | Category: }} | }} and {{#if:{{#switch: yes

| y | yes | t | true  | on  | 1 = yes
| n | no  | f | false | off | 0 | = 
| =  
| #default = yes

}}|}}{{{{#if: |subst: }}[[{{#if: {{#pos: Load achievements | : }} || Template: }}Load achievements|{{ #ifeq: | Template | Load achievements | Load achievements }}]]{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}{{ #if: | | }}}}{{#if:{{#switch: yes

| y | yes | t | true  | on  | 1 = yes
| n | no  | f | false | off | 0 | = 
| =  
| #default = yes

}}|}}{{#if: {{#pos: Load achievements | File: }}{{#pos: Load achievements | Category: }} | }}

{{#if:{{#pos:Achievement|/sandbox}}|| ko:모듈:Achievement pt:Módulo:Achievement }}


local achievement_page = 'Achievement'

local p = {}
local namespace = mw.title.getCurrentTitle().nsText
local page_title = mw.title.getCurrentTitle().text
local full_title = mw.title.getCurrentTitle().fullText
local html
local frame

local function sanitize(str)
	-- remove anything between < and > (simple way to remove span tag)
	-- return lower case alpha numeric characters and spaces
	return str:gsub('<.->',''):gsub('[^%w%s%?]*', ''):lower()
end

local function trim(str)
	local trimmed = (str and mw.text.trim(str) ~= "" and mw.text.trim(str))
	return trimmed
end

-- Adds one row of achievement data to html object
local function printLine(line, is_input)
	local tr = html:tag('tr'):attr('id', line.title):cssText('text-align:left')
	
	local link = nil
	if not is_input then
		link = achievement_page..'#'..line.title
	end
	
	local achievementIcon = require('Module:SpriteFile').sprite({name='AchievementSprite', sanitize(line.title), size=94, align='middle', scale=0.5, link=link}) 
	local newAchievementIcon = require('Module:SpriteFile').sprite({name='NewAchievementSprite', sanitize(line.title), height = 48, width = 84, align='middle', link=link})
	
	tr:tag('td'):wikitext(achievementIcon):cssText('text-align:center')
	tr:tag('td'):wikitext(newAchievementIcon):cssText('text-align:center')
	
	local title_extra = ''
	if line.upcoming_ver then
		title_extra = title_extra .. frame:expandTemplate{title = 'Upcoming', args = {ver = line.upcoming_ver}}
	end
	
	if line.until_ver then
		title_extra = title_extra .. frame:expandTemplate{title = 'Until', args = {ver = line.until_ver}}	
	end
	
	tr:tag('td'):wikitext('[['..(link or full_title)..'|'..line.title..']]', title_extra)
	tr:tag('td'):wikitext(line.game_description)
	tr:tag('td'):wikitext(line.wiki_description)
	tr:tag('td'):wikitext(line.gamerscore)
	tr:tag('td'):wikitext(line.trophy_type)
	if is_input then -- These are only displayed on the input page, not on individual content pages
		tr:tag('td'):wikitext(line.reward)
	end
end

-- Puts one line of data into SMW for later querying
local function storeLine(line)
	-- Put data into SMW
	-- The only top level params that are needed is stuff that would be used as selectors
	-- everything else is put into a json blob for flexibility
	if not (namespace == '' or namespace ==  'Minecraft_Wiki') then
		 return -- only store data if we are in main or Minecraft_Wiki namespaces
	end
	
	local smw_sub = {}
	local sanitized_title = sanitize(line.title)
	local subname = 'ACHIEVEMENT'..'_'..sanitized_title
	
	-- mw.text.unstrip gets rid of parser artifacts that are put there when a <ref> tag is used.
	line.wiki_description = mw.text.unstrip(line.wiki_description)
	line.game_description = mw.text.unstrip(line.game_description)
	
	local smw_sub = { -- the actual SMW sub-object
		['Achievement Title'] = sanitized_title,
		['Achievement JSON'] = mw.text.jsonEncode(line) -- serializes all data into a json blob
	}
	mw.smw.subobject(smw_sub, subname)
end

-- Public function - Called by {{AchievementRow}}
function p.inputLine(_frame)
	frame = _frame -- store frame for later use by expandTemplate
	local args = frame:getParent().args
	
	-- Normalize inputs and set defaults
	local line = {
		title = args['title'], -- name
		upcoming_ver = args['upcoming'], -- version where achievement will be introduced
		until_ver = args['until'], -- version where achievement will be removed
		game_description = trim(args[1]) or "—",
		wiki_description = trim(args[2]) or "—",	
		gamerscore = trim(args[3]) or '—',
		trophy_type = trim(args[4]) or '—',
		reward = trim(args[5]) or '—',
	}
	
	html = mw.html.create()
	
	printLine(line, true)
	storeLine(line)
		
	return html
end

local function printHeader(short_header)
	-- Can't use table tag because it will close itself, but we need to close it with footer
	html = mw.html.create():wikitext('<div style="overflow: auto;"><table class="wikitable collapsible sortable" style="text-align:center">')

	local th = html:tag('tr'):tag('th'):addClass('unsortable'):attr('colspan', 8)
	
	local tr = html:tag('tr')
	tr:tag('th'):wikitext('Icon'):attr('colspan', 2):addClass('unsortable')
	tr:tag('th'):wikitext('Achievement'):attr('rowspan', 2):addClass('sortable')	
	tr:tag('th'):wikitext('In-game description'):attr('rowspan', 2):addClass('unsortable')
	tr:tag('th'):wikitext('Actual requirements (if different)'):attr('rowspan', 2):addClass('unsortable')
	tr:tag('th'):wikitext('Gamerscore earned'):attr('rowspan', 2):addClass('sortable')
	tr:tag('th'):wikitext('Trophy type (<span class="explain" title="PlayStation">PS</span>)'):attr('rowspan', 2):addClass('sortable')	
	if not short_header then
		tr:tag('th'):wikitext('Rewards'):attr('rowspan', 2)
	end
	html:tag('th'):wikitext('PS4'):addClass('unsortable')
	html:tag('th'):wikitext('Other'):addClass('unsortable')	

	return html
end

local function printFooter()
	return '</table></div>'
end

-- Public function - called by {{AchievementTable}}
function p.table(frame)
	local args = frame:getParent().args
	if args[1] == 'foot' then
		return printFooter()
	else
		return printHeader(false)
	end
end

-- Public function - called by {{Load achievements}}
function p.load(frame_)
	frame = frame_ -- store frame for later use by expandTemplate
	local args = frame:getParent().args
	
	achievement_page = args.page or achievement_page
	
	printHeader(true)
	
	local queryString = {}
	-- SMW can query up to 15 conditions at once and we always specify the source page, so put queries into groups of 13 names
	local counter = 13 -- Start at 13 so queryString index starts at 1, it just looks cleaner when debugging
	for name in string.gmatch(args[1], "[^;]+") do
		counter = counter + 1
		queryString[math.floor(counter/13)] = (queryString[math.floor(counter/13)] or '')..'||'..sanitize(name)
	end
	
	local expected = counter - 13
	local found = 0
	for _,str in ipairs(queryString) do
		local query = {
			'[[-Has subobject::'..achievement_page..']]',
			'[[Achievement Title::'..str:sub(3)..']]', --Remove the first 2 characters because they will always be ||
			'?Achievement JSON',
			limit = 25
		}
	
		local smwdata = mw.smw.ask(query)
		
		if smwdata then
			for i,v in ipairs(smwdata) do
				if type(v['Achievement JSON']) == "table" then -- if a page has a drop happening multiple times it is returned in a table instead of a string
					for j,k in ipairs(v['Achievement JSON']) do
						found = found + 1
						local line = mw.text.jsonDecode(v['Achievement JSON'][j] or '{}')
						printLine(line, false)
					end
				else
					found = found + 1
					local line = mw.text.jsonDecode(v['Achievement JSON'] or '{}')
					printLine(line, false)
				end
			end
		end
	end
	
	if found ~= expected then
		html:wikitext('[[Category:Missing achievement]]')
	end

	html:wikitext(printFooter())

	return html
end

return p