local InputParser = require('Module:Input parser')
-- Object for the character
-- (main object for this module)
--
-- @field pageName string
-- @field name string
-- @field gender string 'male' or 'female' (needed for pronouns, otherwise will use they/them)
-- @field game string
-- @field responses table<Response>
local Character = {
pageName = mw.title.getCurrentTitle().text,
name = nil,
gender = nil,
game = nil,
responses = {}
}
-- Object for a response the character makes
-- (A character should have a response for each group-rating combination)
--
-- @field group string The name of the item group the response is to
-- @field rating number Number between 1 and 5
-- @field message string What the character says when giving this reaction
-- @field items table<string> The items that trigger this response
local Response = {
group = nil,
rating = nil,
message = nil,
items = nil,
}
-- Object for an item group
-- @field name string
-- @field trust table<number> Five numbers showing the effect each rating has on the "trust" score
-- @field mood table<number> Five numbers showing the effect each rating has on the "mood" score
-- @field card table<number> Five numbers showing the effect each rating has on the "card" score
-- @field dueling table<number> Five numbers showing the effect each rating has on the "dueling" score
-- @field them table<number> Five numbers showing the effect each rating has on the "them" score
local Group = {
name = nil,
trust = {},
mood = {},
card = {},
dueling = {},
them = {}
}
-- List of each group
local groups = {
['duel'] = {
name = 'Duel',
trust = { -25, -10, 5, 10, 25 },
mood = { -1.2, 0, 0.8, 1, 1.2 },
card = { 0, 1, 1, 2, 2 },
dueling = { 3, 3, 4, 4, 5 },
them = { 0, 1, 1, 2, 2 }
},
['hobby'] = {
name = 'Hobby',
trust = { -25, -10, 5, 10, 25 },
mood = { -1.2, 0, 0.8, 1, 1.2 },
card = { 3, 3, 4, 4, 5 },
dueling = { 0, 1, 1, 2, 2 },
them = { 0, 1, 1, 2, 2 },
},
['sundry_goods'] = {
name = 'Sundry Goods',
trust = { -25, -10, 5, 10, 25 },
mood = { -1.2, 0, 0.8, 1, 1.2 },
card = { 0, 1, 1, 2, 2 },
dueling = { 0, 1, 1, 2, 2 },
them = { 3, 3, 4, 4, 5 },
},
['food'] = {
name = 'Food',
trust = { -0, 25, 50, 75, 100 },
mood = { -1.2, 0, 0, 0, 0 },
card = { -1, -1, 0, 0, 0 },
dueling = { -1, -1, 0, 0, 0 },
them = { -1, -1, 0, 0, 0 },
},
['figures'] = {
name = 'Figures',
trust = { -50, -25, 0, 5, 10 },
mood = { 0.5, 1, 1.5, 2, 2.5 },
card = { -2, -1, 0, 0, 0 },
dueling = { -2, -1, 0, 0, 0 },
them = { -2, -1, 0, 0, 0 },
messageGroup = 'hobby'
}
}
-- Normalize input
--
-- @param table args
-- @return table
local function normalizeArgs(args)
local normalizedArgs = {}
-- If a parameter just contains whitespace, ignore it
for param, value in pairs(args or {}) do
if (value and mw.text.trim(value) ~= '') then
normalizedArgs[param] = value
end
end
return normalizedArgs
end
-- Get the page name for a given item
--
-- @param name string The name of the item
-- @return string
local function getItemLink(name)
-- Map names to page names for items where the two are different
local map = {
['Amplifier'] = 'Amplifier (Tag Force item)',
['Atlas Rising'] = 'Atlas Rising (Tag Force item)',
['Bandanna'] = 'Bandanna (5D\'s Tag Force)',
['Bat'] = 'Bat (item)',
['Beef Bowl'] = 'Beef Bowl (item)',
['Card Ejector Figure'] = 'Card Ejector Figure (Tag Force)',
['Cyber Tutu Figure'] = 'Cyber Tutu Figure (Tag Force)',
['Dark Magician Girl Figure'] = 'Dark Magician Girl Figure (Tag Force)',
['Death Match Duel Rope'] = 'Death Match Duel Rope (Tag Force)',
['Duel Calculator'] = 'Duel Calculator (Tag Force)',
['Ebon Magician Curran Figure'] = 'Ebon Magician Curran Figure (Tag Force)',
['Gold Coin'] = 'Gold Coin (item)',
['Junk'] = 'Junk (item)',
['Red Dragon Archfiend Figure'] = 'Red Dragon Archfiend Figure (Tag Force)',
['Sandwich (Green)'] = 'Sandwich (5D\'s Tag Force Green)',
['Sandwich (Gold)'] = 'Sandwich (5D\'s Tag Force Gold)',
['Tea'] = 'Tea (item)',
['The Daily Duel'] = 'The Daily Duel (Tag Force)',
['Toothbush'] = 'Toothbush (Tag Force)',
['Water'] = 'Water (item)',
['White Magician Pikeru Figure'] = 'White Magician Pikeru Figure (Tag Force)',
}
-- Get the page name from the map
-- If it's not in the map, use the item name as the page name
return map[name] or name
end
-- Get the color for a given rating
-- (The higher the number, the more green. The lower the number, the more red)
--
-- @param rating number Integer between 1 and 5
-- @return string HSL color string
local function getRatingColor(rating)
return 'hsl(' .. ((rating - 1) * 30) .. 'deg, 100%, 75%)'
end
-- Render an effect for displaying
-- (Basically add a "+" before positive numbers. Negative numbers already have a "-")
--
-- @param difference number
-- @return {string|number}
local function renderEffect(difference)
if (difference > 0) then
return '+' .. difference
end
return difference
end
-- Render a "trust" effect for displaying
-- (Shows how much the score increases/decreases by. Hover to see what percentage of a heart that equates to.)
--
-- @param difference number
-- @return {string|number}
local function renderTrustEffect(difference)
local diff = renderEffect(difference)
local heartDiff = renderEffect(difference / 10) .. '%'
return '<abbr title="' .. heartDiff .. ' of a heart">' .. diff .. '</abbr>'
end
-- Create a new instance of the `Character` object
--
-- @param args table
-- @return Character
function Character:new(args)
local character = mw.clone(Character)
character:setData(args)
return character
end
-- Populate character attributes based on input
--
-- @param args table
function Character:setData(args)
args = normalizeArgs(args)
-- If name isn't specified, default to the page name, without parenthetical text
self.name = args['character_name'] or mw.text.split(self.pageName, ' %(')[1]
self.game = args['game']
self.gender = args['gender'] and args['gender']:lower()
self.responses = {}
local groupNames = { 'duel', 'hobby', 'figures', 'sundry_goods', 'food' }
-- Loop through each group and rating combination
for _, groupName in pairs(groupNames) do
for rating = 5, 1, -1 do
local group = groups[groupName]
-- Create a response for each combination
-- And fill with data from input params
local response = Response:new()
-- If a group shares its messages with another group
-- ("Figures" uses the same messages as "Hobby")
local messageGroup = group.messageGroup or groupName
response.group = groupName
response.rating = rating
response.message = args[messageGroup .. '_' .. rating .. '_response']
response.items = InputParser.ulToArray(args[groupName .. '_' .. rating .. '_items'])
table.insert(self.responses, response)
end
end
end
-- Get a character's pronoun
-- @return string "he", "she", or "they"
function Character:getPronoun()
local map = { ['male'] = 'he', ['female'] = 'she' }
return map[self.gender] or 'they'
end
-- Get a character's objective pronoun
-- @return string "him", "her", or "them"
function Character:getObjectivePronoun()
local map = { ['male'] = 'him', ['female'] = 'her' }
return map[self.gender] or 'them'
end
-- Render all the information as wikitext
-- @return string
function Character:renderResponses()
local intro = '<p>In <i>[[' .. self.game .. ']]</i>, ' .. (self.name) .. ' has the following responses when the player gives ' .. (self:getObjectivePronoun()) .. ' a present.</p>'
intro = intro .. '<p>The "Trust" column shows how much of a heart the present will increase in the level of trust by. '
intro = intro .. 'The "Card", "Dueling", and "Them" columns show the effect it has on how many more times ' .. (self:getPronoun())
intro = intro .. (self:getPronoun() == 'they' and ' are' or ' is') ..' willing to talk about that topic (maximum 5).</p>'
local chart = mw.html.create('table'):attr('class', 'wikitable hlist sortable toggleable-columns-table')
local theadRow = chart:tag('tr')
theadRow:tag('th'):attr('scope', 'col'):tag('abbr'):attr('title', 'Rating'):wikitext('★')
theadRow:tag('th'):attr('data-col-group', 'Group'):wikitext('Group')
theadRow:tag('th'):attr('data-col-group', 'Items'):wikitext('Items')
theadRow:tag('th'):attr('data-col-group', 'Response'):wikitext('Response')
theadRow:tag('th'):attr('data-col-group', 'Effects'):wikitext('Trust')
theadRow:tag('th'):attr('data-col-group', 'Effects'):wikitext('Mood')
theadRow:tag('th'):attr('data-col-group', 'Effects'):wikitext('Card')
theadRow:tag('th'):attr('data-col-group', 'Effects'):wikitext('Dueling')
theadRow:tag('th'):attr('data-col-group', 'Effects'):wikitext('Them')
local groupNames = { 'duel', 'hobby', 'figures', 'sundry_goods', 'food' }
for rating = 5, 1, -1 do
for groupNumber, groupName in pairs(groupNames) do
local group = groups[groupName]
local response = self:getResponse(groupName, rating)
local row = chart:tag('tr')
if groupNumber == 1 then
row:tag('td'):attr('rowspan', 5):css('background-color', getRatingColor(rating)):css('color', '#000'):css('text-align', 'center'):wikitext(rating)
end
row:tag('td'):wikitext(group.name)
row:tag('td'):wikitext(response and response:renderItemList())
row:tag('td'):wikitext(response and response.message)
row:tag('td'):wikitext(renderTrustEffect(group.trust[rating]))
row:tag('td'):wikitext(renderEffect(group.mood[rating]))
row:tag('td'):wikitext(renderEffect(group.card[rating]))
row:tag('td'):wikitext(renderEffect(group.dueling[rating]))
row:tag('td'):wikitext(renderEffect(group.them[rating]))
end
end
return intro .. tostring(chart)
end
-- Get a particular response
--
-- @field groupName string The group that the response is for
-- @field rating number The rating from 1 to 5 the character is giving
function Character:getResponse(groupName, rating)
-- Loop through all responses
for _, response in pairs(self.responses) do
-- Stop on the one with the matching group and rating
if (response.rating == rating and response.group == groupName) then
return response
end
end
end
-- Create a new instance of a `Response` object
-- @return Response
function Response:new(args)
local r = mw.clone(Response)
return r
end
-- Render a response's items as a list in wikitext
-- @return string
function Response:renderItemList()
local list = mw.html.create('ul')
for _, item in pairs(self.items) do
local link = getItemLink(item)
list:tag('li'):wikitext('[[' .. link .. '|' .. item .. ']]')
end
return tostring(list)
end
-- The main method that gets invoked by templates
-- @return string
function Character.main(frame)
-- `frame:getParent().args` should get the template parameters
-- Default to just `frame`, so the params can be passed directly when debugging
local args = frame.getParent and frame:getParent().args or frame
-- Create instance of the character
local character = Character:new(args)
-- Render responses for the character
return tostring(character:renderResponses())
end
return Character