Fortnite Esports Wiki
Register
Advertisement

Edit the documentation or categories for this module. This module has an i18n file.


local lang = mw.getLanguage('en')
local m_team = require('Module:Team')
local TeamHistory = require('Module:Infobox/TeamHist').teamHist
local util_args = require('Module:ArgsUtil')
local util_cargo = require('Module:CargoUtil')
local util_dpl = require('Module:DPLUtil')
local util_game = require('Module:GameUtil')
local util_html = require('Module:HtmlUtil')
local util_infobox = require('Module:InfoboxUtil')
local util_map = require('Module:MapUtil')
local util_sentence = require("Module:SentenceUtil")
local util_table = require('Module:TableUtil')
local util_text = require("Module:TextUtil")
local util_title = require("Module:TitleUtil")
local util_vars = require('Module:VarsUtil')
local i18n = require('Module:i18nUtil')
local RoleList = require('Module:RoleList')
local Country = require('Module:Country')
local CountryList = require('Module:CountryList')
local Region = require('Module:Region')
local RegionList = require('Module:RegionList')
local ChampionList = require('Module:ChampionList')
local PlayerCurrentTeam = require('Module:PlayerCurrentTeam').main
local IntroSentence = require('Module:IntroSentence/Player').main
local PRStore = require('Module:PlayerPRStore')._main

local NOIMAGE = 'Unknown Infobox Image - Player.png'

local Infobox = require('Module:Infobox'):extends()

Infobox.type = 'Player'

Infobox.LAYOUT = {
	tabs = 'PlayerTabsHeader',
	sections = { 'Background Information', 'Power Rankings', 'Competitive', 'Social Media & Links', 'Team History', 'Wiki Awards' },
	contents = {
		{ 'Name', 'Country', 'Nationality', 'Birthday', 'Died', 'Residency', 'ResidencyPrev' },
		{ 'All', 'past30', 2018, 2019, 2020, 2021, 2022, 2023, 2024 },
		{ 'Team', 'Contract', 'Role', 'PreviousRole', 'InputDevice', 'CompIDs', 'SoloIDs' },
		{ 'social', social = 'wide' },
		{ 'teamhist', teamhist = 'wide' },
		{ 'wikiawards', wikiawards = 'wide' },
	},
}

local DIRECT_STORE = { ID = 'id', Stream = 'stream', Twitter = 'twitter', Facebook = 'facebook', Askfm = 'askfm', Reddit = 'reddit', Youtube = 'youtube', Weibo = 'weibo', Vk = 'vk', Instagram = 'instagram', IsRetired = 'isretired',
}

local AWARD_ARG_ORDER = { 'year', 'award', 'place', 'file' }

local h = {} -- helper functions
local p = {}
function p.main(frame)
	local args = util_args.merge()
	return Infobox(args):run()
end

function Infobox:castArgs(args)
	args.role = RoleList(args.role, { sub = util_args.castAsBool(args.checkboxIsSub) })
	args.favchamps = ChampionList(util_args.numberedArgsToTable(args, 'favchamp'))
	args.residency = Region(args.residency)
	args.res_prev1 = Region(args['residency-prev1'])
	args.res_prev2 = Region(args['residency-prev2'])
	args.res_prev = RegionList(util_args.numberedArgsToTable(args, 'residency-prev'))
	args.nationality = CountryList(args.nationality or args.country)
	args.country = Country(args.country)
	
	-- on Fortnite wiki we always do nationality
	
	args.isretiredplayer = util_args.castAsBool(args.isretiredplayer)
	args.isretired = util_args.castAsBool(args.isretired)
	args.islookingforduo = util_args.castAsBool(args.islookingforduo)
	args.islookingfortrio = util_args.castAsBool(args.islookingfortrio)
	util_map.arrayInPlace(args, h.castArgAwards)
end

function h.castArgAwards(args)
	args.medals = util_args.splitArgs(args.medals, AWARD_ARG_ORDER, ';;;')
	return args.medals
end

function Infobox:getProcessed(args)
	if util_args.castAsBool(args.checkboxAutoTeams) then
		args.team = nil
		args.team2 = nil
	end
	local processed = {
		pagename = mw.title.getCurrentTitle().text,
		bday = h.getBirthdayAndAgeIfAppropriate(args),
		died = h.getDeathDateIfNeeded(args),
		lc = args.id and lang:lcfirst(args.id) == args.id,
		hasAutoTeams = util_args.castAsBool(args.checkboxAutoTeams),
		image = h.getImage(args),
		isLowContent = util_args.castAsBool(args.low_content),
		redirects = util_dpl.whatRedirectsHere(nil, { notuses = 'Template:NoDPL/PlayerRedirects' }),
		PR = PRStore(args.id or mw.title.getCurrentTitle().baseText),
	}
	processed.entity = processed.lc and util_text.lcfirst(processed.pagename) or processed.pagename
	local teamhist, last = TeamHistory(args)
	processed.teams = h.getTeamsTable(args, processed, last)
	processed.teamhist = not processed.hasAutoTeams and teamhist
	processed.roleLast = h.getCargoLastRole(args, processed)
	processed.resPrevList = h.getResPrevList(args, processed.teams, processed.hasAutoTeams)
	return processed
end

function h.getImage(args)
	if not util_args.castAsBool(args.checkboxAutoImage) then
		return util_infobox.getFile(args.image, NOIMAGE)
	end
	local image = h.queryForImage(args)
	return image or NOIMAGE
end

function h.getBirthdayAndAgeIfAppropriate(args)
	local bday = util_infobox.makeBday(args.birth_date_year, args.birth_date_month, args.birth_date_day)
	if args.died then
		bday.age = nil
		bday.displayandage = bday.display
	end
	return bday
end

function h.getDeathDateIfNeeded(args)
	if not args.died then return {} end
	return util_infobox.makeBday(args.birth_date_year, args.birth_date_month, args.birth_date_day, args.died)
end

function h.getTeamsTable(args, processed, last)
	if processed.hasAutoTeams then
		return PlayerCurrentTeam(mw.title.getCurrentTitle().text)
	end
	local teams = h.getLegacyTeams(args, processed)
	teams.last = last or {}
	return teams
end

function h.getLegacyTeams(args, processed)
	local ret = util_map.inPlace(
		util_args.numberedArgsToTable(args, 'team') or {},
		h.getOneLegacyTeam, args, processed
	)
	return ret
end

function h.getOneLegacyTeam(team, args, processed)
	local ret = {
		team = m_team.teamlinkname(team),
		role = args.role,
		sub = util_args.castAsBool(args.checkboxIsSub),
	}
	return ret
end

function h.getCargoLastRole(args, processed)
	if not processed.hasAutoTeams then return args.role end
	
	-- role could be nil if player has 0 team entries
	if not processed.teams.last.RoleList then return args.role end
	return processed.teams.last.RoleList:_or(args.role)
end

function h.getResPrevList(args, teams, hasAutoTeams)
	if not hasAutoTeams then
		return args.res_prev
	end
	if #teams.resPrevList == 0 then return RegionList() end
	return RegionList(util_table.concat(teams.resPrevList))
end

function Infobox:getDisplay(args, processed)
	local display = self:super('getDisplay', args, processed)
	local tbl = {
		title = args.id or processed.pagename,
		image = not processed.isLowContent and processed.image,
		notice = h.getNotice(args),
		
		-- background info
		Name = args.name and (args.name .. (args.nativename and (' (%s)'):format(args.nativename) or '')),
		Nationality = args.nationality:flairs{sep='<br>'},
		Birthday = processed.bday.displayandage,
		Died = processed.died.displayandage,
		Residency = args.residency:flair(),
		ResidencyPrev = processed.resPrevList:flairs{sep='<br>'},
		
		-- competitive
		Server = args.server,
		Team = h.getCurrentTeam(processed.teams),
		Contract = h.getContract(processed.teams),
		Role = args.role:flairs{len='role'},
		InputDevice = args.inputdevice,
		CompIDs = util_table.concatFromArgs(args, 'compID',', '),
		SoloIDs = args.ids,
		
		-- social
		teamhist = processed.teamhist,
		
		--wikiawards = h.getAwards(args),
		
		class = h.getClass(args, processed)
	}
	h.mergePowerRankingsIntoDisplay(display, processed.PR.display)
	return Infobox.mergeDisplay(display, tbl)
end

function h.mergePowerRankingsIntoDisplay(display, pr)
	for _, tag in ipairs(pr) do
		display[tag] = pr[tag].Score
	end
end

function h.getAwards(args)
	local ret = {}
	util_vars.log(args.medals)
	for _, award in ipairs(args.medals) do
		if not award.file then
			ret[#ret+1] = ('[[File:%s_%s_%s.png]]'):format(award.award, award.year, award.place)
		else
			ret[#ret+1] = ('[[%s]]'):format(award.file)
		end
	end
	return util_table.concat(ret, ' &#8226; ')
end

function h.queryForImage(args)
	local query = {
		tables = {
			'PlayerRedirects=PR',
			'PlayerImages=PI',
			'Tournaments=T',
		},
		join = {
			'PR.AllName=PI.Link',
			'PI.Tournament=T.OverviewPage',
		},
		where = h.getImageWhere(args),
		fields = { 'PI.FileName' },
		orderBy = 'COALESCE(PI.SortDate, T.DateStartFuzzy, T.Date) DESC',
	}
	return util_cargo.getOneResult(query)
end

function h.getImageWhere(args)
	local where = {
		('PR._pageName="%s"'):format(mw.title.getCurrentTitle().text),
		('PI._pageName IS NOT NULL'),
		('PI.IsProfileImage="1"'),
	}
	return where
end

function h.getNotice(args)
	-- args.died is not a boolean
	if args.died then
		return i18n.print('notice_deceased')
	end
	if args.islookingforduo then
	 	return i18n.print('notice_lookingForDuo')
	end
	if args.islookingfortrio then
	 	return i18n.print('notice_lookingForTrio')
	end
	if args.isretiredplayer then
		return i18n.print('notice_retiredPlayer')
	end
	if args.isretired then
		return i18n.print('notice_retiredCompetition')
	end
	return nil
end

function h.getCurrentTeam(teams)
	if #teams == 0 then return nil end
	return util_table.concat(
		util_map.extractField(teams, 'team', m_team.rightmediumlinked),
		'<br>'
	)
end

function h.getContract(teams)
	if #teams == 0 then return nil end
	if not teams.contractDates then return nil end
	if #teams == 1 then
		return h.getContractDateFromFirstTeam(teams)
	end
	
	-- if they are on two teams but it's one org, we just show the date, nothing else
	-- but if they're on more than one org then we need to show a team icon next to the contract date
	local allSisterTeams = h.getListOfSisterTeamPages(teams)
	if #allSisterTeams == 1 then
		return h.getContractDateFromFirstTeam(teams)
	end
	return h.getAllContracts(teams)
end

function h.getContractDateFromFirstTeam(teams)
	return teams.contractDates[teams[1].SisterTeamPage]
end

function h.getAllContracts(teams)
	local contracts = {}
	for _, team in ipairs(teams) do
		local contract = teams.contractDates[team.SisterTeamPage]
		if contract then
			contracts[#contracts+1] = ('%s%s'):format(
				m_team.onlyimagelinked(team.team),
				contract
			)
		end
	end
	if #contracts == 0 then return nil end
	return util_table.concat(contracts, '<br>')
end

function h.getListOfSisterTeamPages(teams)
	local sisterTeamsUsed = {}
	local allSisterTeams = {}
	for _, team in ipairs(teams) do
		if not sisterTeamsUsed[team.SisterTeamPage] then
			allSisterTeams[#allSisterTeams+1] = team.SisterTeamPage
			sisterTeamsUsed[team.SisterTeamPage] = true
		end
	end
	return allSisterTeams
end

function h.getClass(args, processed)
	local listOfClasses = {
		processed.hasAutoTeams and 'infobox-player-narrow',
		processed.isLowContent and 'infobox-low-content',
	}
	return util_table.concat(listOfClasses, ' ')
end

function Infobox:getCargo(args, processed)
	local cargo = self:super('getCargo', args, processed)
	cargo[#cargo+1] = h.getPlayersCargo(args, processed)
	util_table.mergeArrays(
		cargo,
		h.getPlayerLeagueHistory(args)
	)
	return cargo
end

function h.getPlayersCargo(args, processed)
	local idlist = mw.clone(processed.redirects)
	idlist[#idlist+1] = processed.pagename
	local ret = {
		_table = 'Players',
		IDList = table.concat(idlist,','),
		OverviewPage = mw.title.getCurrentTitle().text,
		FortniteID = args.fortnite_id or mw.title.getCurrentTitle().text,
		Image = h.getImageCargo(args, processed),
		Name = args.name,
		Player = processed.entity,
		NativeName = args.nativename,
		NameAlphabet = args.namealphabet,
		NameFull = args.name and (args.name .. (args.nativename and (' (%s)'):format(args.nativename) or '')),
		Country = args.country,
		Nationality = args.nationality,
		NationalityPrimary = args.nationality:first(),
		Age = processed.bday.age or '',
		Birthdate = processed.bday.store or '',
		ResidencyFormer = args['residency-prev1'],
		Team = processed.teams[1] and processed.teams[1].team,
		Team2 = processed.teams[2] and processed.teams[2].team,
		TeamSystem = 'PC',
		Team2System = 'PC',
		Residency = args.residency,
		Role = args.role,
		InputDevice = args.inputdevice,
		FavChamps = util_table.concatFromArgs(args, 'favchamp', ','),
		TeamLast = processed.teams.last and processed.teams.last.team,
		RoleLast = processed.roleLast,
		IsSubstitute = args.checkboxIsSub,
		IsLowercase = processed.lc,
		IsAutoTeam = args.checkboxAutoTeams,
		SoloqueueIds = args.ids,
		IsPersonality = args.page_type == 'Personality',
		IsLowContent = processed.isLowContent,
		IsLookingForDuo = args.islookingforduo,
		IsLookingForTrio = args.islookingfortrio
	}
	h.addUnprocessedArgs(ret, args)
	return ret
end

function h.getImageCargo(args, processed)
	if util_args.castAsBool(args.checkboxAutoImage) then
		return nil
	end
	if processed.image == NOIMAGE then return nil end
	if not processed.image then return nil end
	return processed.image:gsub('_', ' ')
end

function h.addUnprocessedArgs(tbl, args)
	for k, v in pairs(DIRECT_STORE) do
		tbl[k] = args[v]
	end
end

function h.getPlayerLeagueHistory(args)
	if util_args.castAsBool(args.noplh) then return {} end
	if not util_game.store_player_league_history then return {} end
	local plhTable = require('Module:PlayerLeagueHistory')._main(mw.title.getCurrentTitle().text)
	return plhTable
end

function Infobox:getDisambigSentence(args, processed)
	-- we can't store "now known as" here but we can store everything else
	-- the "now known as" text is going to have to change, but that's ok maybe
	local replacements = {
		RESIDENCY = args.residency:image(),
		LINK = util_text.intLinkOrText(mw.title.getCurrentTitle().text),
		
		-- TODO: should this, actually be country...?
		NATIONALITY = args.country:exists() and args.country:get('adjective'),
		FORMER = not processed.teams[1] and i18n.default('former') or '',
		ROLE = processed.roleLast:sentence(),
		TEAM = processed.teams[1] and m_team.rightmediumlinked(processed.teams[1].team),
		-- NOW_KNOWN_AS = row.CurrentName and i18n.default('nowKnownAs', row.CurrentName) or '',
	}
	return util_sentence.makeReplacements(i18n.default('disambigSentence'), replacements)
end

function Infobox:getVariables(args, processed)
	local variables = self:super('getVariables', args, processed)
	local tbl = {
		suppressorgnavbox = args.checkboxSuppressOrgNavbox,
		introSentence = h.getIntroSentence(args, processed, 'Display'),
		description = h.getIntroSentence(args, processed, 'Description'),
		checkboxAutoTeams = args.checkboxAutoTeams,
		residency = args.residency:get(),
		isretired = args.isretired or args.isretiredplayer,
		current_id = processed.entity,
	}
	local i = 2
	repeat
		thisteam = 'team' .. i
		tbl[thisteam] = args[thisteam] and m_team.teamlinkname(args[thisteam])
		i = i + 1
	until(not args[thisteam])
	return util_table.merge(variables, tbl)
end

function h.getIntroSentence(args, processed, sentenceType)
	local data = mw.clone(args)
	local name = mw.text.split(args.name or '', ' ')
	data.firstname = table.remove(name, 1)
	data.lastname = table.concat(name, ' ')
	data.lastteam = processed.teams.last.team
	data.role = args.role:_or(processed.roleLast)
	data.teamStr = args.team and m_team.teamlinkname(args.team)
	data.isasub = util_args.castAsBool(args.checkboxIsSub)
	data.ispersonality = args.page_type == 'Personality'
	data.page_type = args.page_type
	data.isretired = util_args.castAsBool(args.isretired)
	data.lastteampresent = h.getLastTeamPresent(args, processed)
	data.currentTeamList = processed.teams
	data.hasAutoTeams = processed.hasAutoTeams
	return IntroSentence(data, sentenceType)
end

function h.getLastTeamPresent(args, processed)
	if processed.teams and #processed.teams > 0 then return true end
	return processed.teams.last.date and processed.teams.last.date:find('Present')
end

function Infobox:getCategories(args, processed)
	if util_args.castAsBool(args.pronly) then
		return {
			'Power Ranking Player Pages'
		}
	end
	local tbl = {
		'Players',
		args.residency:exists() and args.residency:get('adjective') .. ' Residents',
		not args.role:exists() and 'Players Without Role',
		h.getSuspendedCategory(args, processed),
		h.isAFreeAgent(args, processed) and 'Free Agents',
		util_args.castAsBool(args.isretired) and 'Retired Players',
		args.birth_date_year and 'Players Born In ' .. args.birth_date_year,
		args.role:has(nil, 'Coach') and 'Coaches',
		processed.isLowContent and 'LowContent',
	}
	
	if args.country:exists() then
		if args.page_type == 'Personality' then
			tbl[#tbl+1] = ('%s Personalities'):format(args.country:get('adjective'))
		else
			tbl[#tbl+1] = 'Players Born in ' .. args.country:name{the=true}
		end
	end
	
	for _, region in ipairs(processed.resPrevList) do
		tbl[#tbl+1] = ('Former %s Residents'):format(region:get('adjective'))
	end
	return tbl
end

function h.isAFreeAgent(args, processed)
	if not util_args.castAsBool(args.checkboxAutoTeams) then
		if args.team then return false end
		if util_args.castAsBool(args.isretired) then return false end
		return true
	end
	return not h.getCurrentTeam(processed.teams)
end

function h.getSuspendedCategory(args, processed)
	if processed.teams.last.team ~= 'Suspended' then return false end
	if not processed.teams.last.date then return false end
	if not processed.teams.last.date:find('Present') then return false end
	return 'Suspended Players'
end

return p
Advertisement