コンテンツにスキップ

モジュール:Infobox

提供: Undertale Wiki

このモジュールについての説明文ページを モジュール:Infobox/doc に作成できます

--- Helper functions for formatting infobox data and autocategorization based on
--  it.
--  @module             infobox
--  @alias              p
--  @require            Module:User error
--  @require            Module:Yesno
--  @author             [[User:KockaAdmiralac|KockaAdmiralac]]
--  @author             [[User:Jacky720|Jacky720]]
--  <nowiki>
require('strict')
local p = {}

--  Package imports.
local userError = require('Module:User error')
local yesno = require('Module:Yesno')
local data = mw.loadData('Module:Infobox/data')

--  Package variables.
local title = mw.title.getCurrentTitle()

--  Private logic.

--- Adds a category into a specified string (represented as a table).
--  Only works if the current page is in the main namespace.
--  @function           category
--  @param              {table} t String to insert the category in
--  @param              {table} c Category to insert
--  @param              {string|nil} sortkey The sort key for this category
--  @local
local function category(t, c, sortkey)
    if title.namespace == 0 and title.text ~= 'Items' then
        table.insert(t, '[[Category:')
        table.insert(t, c)
        if sortkey ~= nil and sortkey ~= '' then
            table.insert(t, '|')
            table.insert(t, sortkey)
        end
        table.insert(t, ']]')
    end
end

local function multicategory(t, c)
    for _, cat in ipairs(c) do
        category(t, cat)
    end
end

--  Package items.

--- Formats a location parameter.
--  Complex logic in this function is supposed to both deduce which location is
--  something happening in and which page should the page be categorized under.
--  Location parameters that pass through this function are even allowed to
--  be separated by bullet points and it'll parse them as separate locations.
--  Location formats parsed by this function are:
--  <pre>Card Castle &rarr; [[Card Castle]]</pre>
--  <pre>[[Card Castle]] &rarr; [[Card Castle]]</pre>
--  <pre>Card Castle (F1) &rarr; [[Card Castle#F1|Card Castle]] (F1)</pre>
--  <pre>[[Card Castle]] (F1) &rarr; [[Card Castle#F1|Card Castle]] (F1)</pre>
--  <pre>Card Castle#F1 &rarr; [[Card Castle#F1|Card Castle]] (F1)</pre>
--  <pre>[[Card Castle#F1]] &rarr; [[Card Castle#F1|Card Castle]] (F1)</pre>
--  <pre>[[Island Board (Original Game)]] &rarr; [[Island Board (Original Game)]]</pre>
--  Invalid locations are trimmed and returned.
--  @function           p.location
--  @param              {table} frame Scribunto frame object
--  @returns            {string} Formatted location
function p.location(frame)
    local str = {}
    local index = 0
    local categories = {}
    local infotype = frame.args[2]
    for _, location in ipairs(mw.text.split(frame.args[1], '*', true)) do
        local trimmed = mw.text.trim(location)
        if trimmed ~= '' then
            index = index + 1
            if index == 2 then
                table.insert(str, 1, '* ')
            end
            local link, text = mw.ustring.match(trimmed, '^%[%[([^%]]+)%]%]%s*%(?([^)]*)%)?$')
            if link == nil then
                link, text = mw.ustring.match(trimmed, '([^%](]+)%s*%(?([^)]*)%)?$')
            end
            if link then
                link = mw.text.trim(link)
                if index > 1 then
                    table.insert(str, '\n* ')
                end
                local spl = mw.text.split(link, '#', true)
                local name = spl[1]
                local hash = spl[2]
                if text and text ~= '' and not hash then
                    hash = text
                end
                local ldata = data.locations[name]
                if ldata then
                    if ldata.link then
                        link = ldata.link
                    end
                    if ldata.categories then
                        multicategory(categories, ldata.categories)
                    end
                    if ldata.condcats and infotype and ldata.condcats[infotype] then
                        multicategory(categories, ldata.condcats[infotype])
                    end
                    if ldata.nolink then
                        table.insert(str, link)
                    else
                        table.insert(str, '[[')
                        table.insert(str, name)
                        if hash then
                            table.insert(str, '#')
                            table.insert(str, hash)
                            table.insert(str, '|')
                            table.insert(str, name)
                            table.insert(str, ']]')
                            table.insert(str, ' (')
                            table.insert(str, hash)
                            table.insert(str, ')')
                        else
                            table.insert(str, ']]')
                        end
                    end
                else
                    table.insert(str, trimmed)
                end
            end
        end
    end
    return table.concat(str) .. table.concat(categories)
end

--- Formats an item's type and weapon subtype.
--  @function           p.itemtype
--  @param              {table} frame Scribunto's frame object
--  @returns            {string} Formatted item type and weapon subtype
function p.itemtype(frame)
    local str = {}
    for _, itemtype in ipairs(mw.text.split(frame.args[1], ',', true)) do
        itemtype = mw.text.trim(itemtype)
        if data.itemtypes[itemtype] then
            if str[1] then
                if str[1] ~= '* ' then
                    table.insert(str, 1, '* ')
                end
                table.insert(str, '\n* ')
            end
            table.insert(str, itemtype)
            -- Categorize as Swords, Axes, Scarves, or Rings instead, based on equip parameter
            if itemtype == 'Weapon' and data.weapontypes[frame.args[2]] then
                category(str, data.weapontypes[frame.args[2]], frame.args[3])
            else
                category(str, data.itemtypes[itemtype], frame.args[3])
            end
        end
    end
    return table.concat(str)
end

--- Formats a game mechanic type.
--  @function           p.mechanics
--  @param              {table} frame Scribunto frame object
--  @returns            {string} Formatted game mechanic type
function p.mechanics(frame)
    local t = frame.args[1]
    if not t or not data.mechanics[t] then
        return userError(
            'Mechanic type invalid or not specified',
            'Pages with user errors'
        )
    end
    local d = data.mechanics[t]
    local str = {d}
    category(str, table.concat({d, 's'}))
    
    return table.concat(str)
end

--- Formats a given enemy's ACT field in the infobox.
--  @function           p.act
--  @param              {table} frame Scribunto frame object
--  @returns            {string} Formatted ACTs
function p.act(frame)
    local str = {}
    local index = 0
    local nocheck = frame.args[2]
    nocheck = mw.text.trim(nocheck)
    nocheck = mw.text.split(nocheck, '%s*,%s*')
    for _, battle in ipairs(mw.text.split(frame.args[1], '*', true)) do
        -- battle: A given battle's ACTs, passed to the function 
        --  e.g. "Cry (First box encounter)"
        -- acts: The ACTs OTHER than Check
        --  e.g. "Cry"
        -- form: The form or battle with these ACTs
        --  e.g. "First box encounter"
        -- "nocheck" parameter (frame.args[2]) prevents auto-adding Check.
        battle = mw.text.trim(battle)
        if battle ~= '' then
            index = index + 1
            if index == 2 then
                table.insert(str, 1, '* ')
            end
            if index > 1 then
                table.insert(str, '\n* ')
            end
 
            local acts, form = mw.ustring.match(battle, '^([^(]*)%s*%(?([^)]*)%)?$')
            if not yesno(nocheck[index] or nocheck[1], false) then
                if acts ~= '' then
                    table.insert(str, 'Check, ')
                else
                    table.insert(str, 'Check ')
                end
            end
            table.insert(str, acts)
            if form ~= '' then
                table.insert(str, ' (')
                table.insert(str, form)
                table.insert(str, ')')
            end
        end
    end
    return table.concat(str)
end

--- Used for appending the Enemies category if the <code>gold</code> argument
--  is supplied (this is how we determine whether a character is an enemy).
--  @function           p.gold
--  @param              {table} Scribunto frame object
--  @returns            {string} The same thing with tne Enemies category added
function p.gold(frame)
    local str = {frame.args[1]}
    category(str, 'Enemies')
    return table.concat(str)
end

--- Formats the parameter for linking back to Undertale Wiki.
--  Used only on Deltarune Wiki.
--  @function           p.undertale
--  @param              {table} frame Scribunto frame object
--  @returns            {string} Formatted returning character parameter
function p.undertale(frame)
    local str = {}
    local index = 0
    local names = frame.args[1]
    if yesno(names, false) then
        if frame.args[2] == '' then -- No name specified
            names = title.text
        else
            names = frame.args[2]
        end
    end
    
    -- Iterate over multiple names
    for _, name in ipairs(mw.text.split(names, ',', true)) do
        local trimmed = mw.text.trim(name)
        index = index + 1
        if index > 1 then
            table.insert(str, ', ')
        end
        table.insert(str, '[[ut:')
        table.insert(str, name)
        table.insert(str, '|')
        table.insert(str, name)
        table.insert(str, ']]')
    end
    
    if frame.args[2] == '' then -- Don't include category for NPC pages with separated infoboxes
        category(str, 'Returning Characters')
    end
    return table.concat(str)
end

--- Displays N/A if a parameter is not supplied.
--  @function           p.na
--  @param              {table} Scribunto frame object
--  @returns            {string} "N/A" if the parameter is not supplied, the
--                               parameter value otherwise
function p.na(frame)
    local val = mw.text.trim(frame.args[1])
    if val == '' then
        return 'N/A'
    else
        return val
    end
end

--- Used in [[Template:Items]] when an item has multiple infoboxes on its
--  article.
--  @function           p.alternative
--  @param              {table} frame Scribunto frame object
--  @returns            {string} A slash, then the alternative item name
function p.alternative(frame)
    local val = mw.text.trim(frame.args[1])
    if val ~= '' then
        return table.concat({'/', val})
    end
end

--- Automatically deduces an item's selling value based on its buying value.
--  Used only on Deltarune Wiki.
--  @function           p.sell
--  @param              {table} frame Scribunto frame object
--  @returns            {string} Item's selling value
function p.sell(frame)
    local val = tonumber(frame.args[1])
    if val then
        return math.ceil(val / 2) .. ' D$'
    end
end

--- Categorizes a location page under [[:Category:Locations]], as well as under
--  a category named by itself, if it exists.
--  @function           p.locationcats
--  @param              {table} frame Scribunto frame object
--  @returns            {string} Location's categories
function p.locationcats(frame)
    local cats = {}
    category(cats, 'Locations')
    if data.locationcats[title.text] then
        category(cats, title.text)
    end
    return table.concat(cats)
end

--- Stores item infobox data in item and item_type buckets.
--  @function           p.itemdata
--  @param              {table} frame Scribunto frame object
function p.itemdata(frame)
	if title.namespace ~= 0 then
		return
	end
	local args = frame:getParent().args
	local item = args.title or title.text
	bucket("item").put({
		name = item,
		description = args.flavortext,
		effects = args.effects and mw.text.killMarkers(args.effects) or nil,
		source = args.source,
		buy = args.buy,
		id = args.id and tonumber(mw.ustring.match(args.id, '^%d+')) or 999
	})
	for _, itemtype in ipairs(mw.text.split(args.type, ',', true)) do
		bucket("item_type").put({item = item, type = mw.text.trim(itemtype)})
	end
end

--- Generates a table listing of items of a certain type.
--  @function           p.itemtable
--  @param              {table} frame Scribunto frame object
--  @returns            {string} Table with items listed
function p.itemtable(frame)
	local itemtype = frame.args[1]
	local data = bucket('item_type')
		.where('type', itemtype)
		.join('item', 'item_type.item', 'item.name')
		.select(
			'item.page_name',
			'item.name',
			'item.description',
			'item.effects',
			'item.source',
			'item.buy',
			'item.id'
		)
		.orderBy('item.id')
		.run()

	local itemtable = mw.html.create('table')
		:attr('class', 'wikitable items-table')
		:tag('tr')
			:tag('th')
				:wikitext('Name')
			:done()
			:tag('th')
				:wikitext('Description')
			:done()
			:tag('th')
				:wikitext('Effects')
			:done()
			:tag('th')
				:wikitext('Source')
			:done()
			:tag('th')
				:wikitext('Buy')
			:done()
		:done()
	for _, item in ipairs(data) do
		itemtable:tag('tr')
			:tag('td')
				:wikitext(table.concat({'[[', item['item.page_name'], '|', item['item.name'], ']]'}))
			:done()
			:tag('td')
				:newline()
				:wikitext(item['item.description'] or '')
				-- See [[Module:Soundtrack]] for why we need the second newline.
				:newline()
			:done()
			:tag('td')
				:newline()
				:wikitext(item['item.effects'] or 'N/A')
				:newline()
			:done()
			:tag('td')
				:newline()
				:wikitext(p.location({args = {item['item.source'] or ''}}))
				:newline()
			:done()
			:tag('td')
				:newline()
				:wikitext(item['item.buy'] or 'N/A')
				:newline()
			:done()
		:done()
	end
	return tostring(itemtable:done())
end

return p
--  </nowiki>