Module:PatchObject

From Liquipedia Stormgate Wiki

Documentation for this module may be created at Module:PatchObject/doc

local Array = require('Module:Array')
local Class = require('Module:Class')
local DateExt = require('Module:Date/Ext')
local Faction = require('Module:Faction')
local Logic = require('Module:Logic')
local Page = require('Module:Page')
local String = require('Module:StringUtils')
local Table = require('Module:Table')
local Variables = require('Module:Variables')

local MAX_QUERY_AFFECTED = 25
local PATCH_OBJECT_TYPES = {
	buff = '<i class="fas fa-plus forest-green-text" title="Buff" size="110%"></i> ',
	nerf = '<i class="fas fa-minus cinnabar-text" title="Nerf" size="110%"></i> ',
	change = '<i class="fas fa-question bright-sun-text" title="Change" size="110%"></i> ',
	fix = '<i class="fas fa-wrench sapphire-text" title="Fix" size="110%"></i> ',
}

local PatchObject = {}

---@param args table?
---@return Html?
function PatchObject.run(args)
	if Table.isEmpty(args) then return end
	---@cast args -nil

	local patchType = (args.type or ''):lower()
	assert(PATCH_OBJECT_TYPES[patchType], 'Invalid type "' .. (args.type or '') .. '"')
	assert(args.text, 'No text specified')

	if not Logic.readBool(args.nostorage) and Logic.readBoolOrNil(Variables.varDefault('disable_LPDB_storage')) ~= false then
		PatchObject._store(args, patchType)
	end

	return PatchObject._display(patchType, args.text)
end

---@param args any
---@param patchType any
function PatchObject._store(args, patchType)
	args.patch = Logic.emptyOr(args.patch, mw.title.getCurrentTitle().prefixedText)
	assert(args.affected1, 'No "affected1" specified')

	local patchObject = (tonumber(Variables.varDefault('patchObject')) or 0) + 1
	Variables.varDefine('patchObject', patchObject)

	local extradata = {patchtype = patchType, text = args.text}
	for key, affectedPage in Table.iter.pairsByPrefix(args, 'affected') do
		extradata[key] = Page.pageifyLink(affectedPage)
	end

	mw.ext.LiquipediaDB.lpdb_datapoint('patchObject_' .. patchObject, {
		type = 'patchObject',
		name = args.patch:gsub(' ','_'),
		information = Faction.read(args.faction) or ((args.faction or ''):lower() == 'map' and 'map') or Faction.defaultFaction,
		extradata = mw.ext.LiquipediaDB.lpdb_create_json(extradata),
	})
end

---@param patchType string
---@param text string
---@return string
function PatchObject._display(patchType, text)
	return PATCH_OBJECT_TYPES[patchType] .. text
end

---@param args table?
---@return Html?
function PatchObject.retrieve(args)
	args = args or {}

	local patchObjects = mw.ext.LiquipediaDB.lpdb('datapoint', {
		conditions = PatchObject._buildConditions(args),
		limit = 5000,
	})

	if Table.isEmpty(patchObjects) then return end

	local groupedByPatch
	_, groupedByPatch = Array.groupBy(patchObjects, function(item) return item.name end)

	for patch, data in pairs(groupedByPatch) do
		data.date = PatchObject._FetchPatchDate(patch)
	end

	local display = mw.html.create('table')
		:css('text-align', 'center')
		:css('min-width', '68%')
		:addClass('wikitable wikitable-stripped wikitable-bordered collapsible')
		:node(PatchObject._header())

	for patch, data in Table.iter.spairs(groupedByPatch, PatchObject._sortPatches) do
		display:node(PatchObject._row(patch, data))
	end

	return mw.html.create('div')
		:addClass('table-responsive')
		:node(display)
end

---@param args table
---@return string
function PatchObject._buildConditions(args)
	local conditions = {
		'[[type::patchObject]]',
		'[[name::!]]',
	}

	if args.patch then
		table.insert(conditions, '[[name::' .. args.patch:gsub(' ', '_') .. ']]')
	end

	if args.faction then
		table.insert(conditions, '[[information::' .. (Faction.read(args.faction) or args.faction) .. ']]')

		if not args.page then
			return table.concat(conditions, ' AND ')
		end
	end

	local affectedPage = args.page or mw.title.getCurrentTitle().prefixedText:gsub(' ', '_')

	local affectedConditions = Array.map(Array.range(1, MAX_QUERY_AFFECTED), function(affectedIndex)
		return '[[extradata_affected' .. affectedIndex .. '::' .. affectedPage .. ']]'
	end)

	table.insert(conditions, '(' .. table.concat(affectedConditions, ' OR ') .. ')')

	return table.concat(conditions, ' AND ')
end

---@param pageName string
---@return string
function PatchObject._FetchPatchDate(pageName)
	local patch = mw.ext.LiquipediaDB.lpdb('datapoint', {
		conditions = '[[type::patch]] AND [[pagename::' .. pageName .. ']]',
		limit = 1,
	})[1] or {}

	return Logic.emptyOr(patch.date or DateExt.defaultDateTime)
end

---@return Html
function PatchObject._header()
	return mw.html.create()
		:tag('tr')
			:tag('th'):attr('colspan', 3):wikitext('Balance Patch History'):done()
			:done()
		:tag('tr')
			:addClass('wiki-backgroundcolor-light')
			:css('font-weight', 'bold')
			:tag('td'):css('width', '200px'):wikitext('Patch'):done()
			:tag('td'):css('width', '92px'):wikitext('Release'):done()
			:tag('td'):wikitext('Balance Changes')
		:allDone()
end

---@param patch string
---@param data table
---@return Html
function PatchObject._row(patch, data)
	local date = Table.extract(data, 'date')

	local patch = '[[' .. patch .. '|' .. PatchObject._getPatchName(patch) .. ']]'

	local row = mw.html.create('tr')
		:tag('td'):wikitext(patch):done()
		:tag('td'):wikitext(date ~= DateExt.defaultDateTime and date:sub(1, 10) or ''):done()

	local changes = mw.html.create('ul')

	local objectsByType = Array.groupBy(data, function(item) return item.extradata.patchtype end)

	Array.forEach(objectsByType, function(group)
		Array.forEach(group, function(patchObject)
			changes:tag('li'):node(PatchObject._display(patchObject.extradata.patchtype, patchObject.extradata.text))
		end)
	end)

	return row:tag('td')
		:css('text-align', 'left')
		:node(changes)
		:done()
end

---@param patchPage string
---@return string
function PatchObject._getPatchName(patchPage)
	local patch = mw.ext.LiquipediaDB.lpdb('datapoint', {
		conditions = '[[type::patch]] AND [[pagename::' .. patchPage .. ']]',
		limit = 1,
	})[1] or {}

	return String.nilIfEmpty(patch.name) or patchPage:gsub('_', ' ')
end

---@param tbl table
---@param key1 string
---@param key2 string
---@return boolean
function PatchObject._sortPatches(tbl, key1, key2)
	return tbl[key1].date > tbl[key2].date
end

return Class.export(PatchObject)