Module:Events/Table

From Rise of Cultures Wiki
Jump to navigation Jump to search

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

local EraMod = require( 'Module:Era' )
local GenMod = require( 'Module:General' )

local p = {}

local function active()
	local active_state = false
	
    if start_date_beta ~= nil and start_date_beta ~= "" then
    	active_start = start_date_beta
    end
    
    if start_date ~= nil and start_date ~= "" then
    	active_start = start_date
    end
    
    if active_start ~= nil and active_start ~= "" then
        local day, month, year = active_start:match("(%d+)-(%d+)-(%d+)")
        
        if day and month and year then
            day = tonumber(day)
            month = tonumber(month)
            year = tonumber(year)
            
            if day >= 1 and day <= 31 and month >= 1 and month <= 12 and year >= 1900 and year <= 2100 then
                local inputDate = os.time({year = year, month = month, day = day, hour = 0, min = 0, sec = 0})
                local endDate = inputDate + (duration * 24 * 60 * 60)  -- Calculate end date in seconds
                
                local today = os.time({year = os.date("*t").year, month = os.date("*t").month, day = os.date("*t").day, hour = 0, min = 0, sec = 0})
                
                if today >= inputDate then
                    active_state = true
                end
            end
        end
    end
    
    return active_state 
end

local function day_suffix(day)
    local suffix
    day = day:gsub('0*', '', 1)
    if day == "11" or day == "12" or day == "13" then
        suffix = "th"
    else
        local lastDigit = tonumber(day) % 10
        suffix = lastDigit == 1 and "st" or (lastDigit == 2 and "nd" or (lastDigit == 3 and "rd" or "th"))
    end

    return day .. suffix
end


local function countdown()
    local text_string = ""
    
    if start_date_beta ~= nil and start_date_beta ~= "" and start_time_beta ~= nil and start_time_beta  ~= "" then
        text_string = '<b>Beta:</b> '
        local day, month, year = start_date_beta:match("(%d+)-(%d+)-(%d+)")
        
        if day and month and year then
            day = tonumber(day)
            month = tonumber(month)
            year = tonumber(year)
            
            if day >= 1 and day <= 31 and month >= 1 and month <= 12 and year >= 1900 and year <= 2100 then
                local inputDate = os.time({year = tonumber(year), month = tonumber(month), day = tonumber(day)})
                local endDate = inputDate + (duration * 24 * 60 * 60)  -- Calculate end date in seconds
                text_string = text_string .. '<span data-options="short-format" data-end="toggle" data-toggle="next" class="countdown" style="display:none;"><span class="countdowndate">' .. os.date("%B %d %Y", endDate) .. ' ' .. start_time_beta .. '</span></span><span style="display:none;">Ended</span>'
            else
                text_string = text_string .. start_date_beta
            end
        else
            text_string = text_string .. start_date_beta
        end
    end
    
    if start_date ~= nil and start_date ~= "" then
        local day, month, year = start_date:match("(%d+)-(%d+)-(%d+)")
        
        if day and month and year then
            day = tonumber(day)
            month = tonumber(month)
            year = tonumber(year)
            
            if day >= 1 and day <= 31 and month >= 1 and month <= 12 and year >= 1900 and year <= 2100 then
                local inputDate = os.time({year = tonumber(year), month = tonumber(month), day = tonumber(day)})
                local endDate = inputDate + (duration * 24 * 60 * 60)  -- Calculate end date in seconds
        
                if start_time_am ~= nil and start_time_am  ~= "" then
                    if text_string ~= "" then
                        text_string = text_string .. '<br>'
                    end
                    text_string = text_string .. '<b>AM:</b> <span data-options="short-format" data-end="toggle" data-toggle="next" class="countdown" style="display:none;"><span class="countdowndate">' .. os.date("%B %d %Y", endDate) .. ' ' .. start_time_am .. '</span></span><span style="display:none;">Ended</span>'
                end
        
                if start_time_eu ~= nil and start_time_eu  ~= "" then
                    if text_string ~= "" then
                        text_string = text_string .. '<br>'
                    end
                    text_string = text_string .. '<b>EU:</b> <span data-options="short-format" data-end="toggle" data-toggle="next" class="countdown" style="display:none;"><span class="countdowndate">' .. os.date("%B %d %Y", endDate) .. ' ' .. start_time_eu .. '</span></span><span style="display:none;">Ended</span>'
                end
            else
            	if text_string ~= "" then
                	text_string = text_string .. '<br>'
                end
                text_string = text_string .. '<b>AM:</b> ' .. start_date .. '<br><b>EU:</b> ' .. start_date
            end
        else
    		if text_string ~= "" then
        		text_string = text_string .. '<br>'
        	end
            text_string = text_string .. '<b>AM:</b> ' .. start_date .. '<br><b>EU:</b> ' .. start_date
        end
    end
    
    return text_string
end


local function formatDate(add_days)
	
	if type(add_days)  == 'number' then
		duration = add_days
	end
	
    local text_string = ""
	local day, month, year = start_date:match("(%d+)-(%d+)-(%d+)")
    if day and month and year then
        if tonumber(day) >= 1 and tonumber(day) <= 31 and tonumber(month) >= 1 and tonumber(month) <= 12 and tonumber(year) >= 1900 and tonumber(year) <= 2100 then
			start_date_values = os.time({year = tonumber(year), month = tonumber(month), day = tonumber(day)})
			local formattedStartDate = day_suffix(day)  .. " " .. os.date("%B %Y", start_date_values)
			local endDate = start_date_values + (duration * 24 * 60 * 60)  -- Calculate end date in seconds
			local endDay = day_suffix(os.date("%d",endDate))
			local formattedEndDate = endDay .. ' ' .. os.date("%B %Y", endDate)  -- Format end date
			
			if add_days then
				text_string = formattedEndDate
			else
				text_string = formattedStartDate .. " - " .. formattedEndDate .. "<br>(" .. duration .. " days)"
			end
        end
	else
		text_string = start_date
	end

    return text_string
end


local function daily_specials()
	if status == nil then -- for older event modules that do not have this variable, set as 'live' to show the daily specials.
		status = 'live'
	end
	
	local text_string = '<div style="overflow: auto;"><h2>Daily Specials</h2>'
    
    if type(status) == "string" and status:lower() == "beta" then
    	start_date = start_date_beta
    end
    
    if daily_special ~= nil and start_date ~= nil and status ~= "" then
        local Timestamp = os.time{year=tonumber(start_date:sub(7)), month=tonumber(start_date:sub(4, 5)), day=tonumber(start_date:sub(1, 2))}
        for i = 1, #daily_special do
            newTimestamp = Timestamp + ((i-1) * 24 * 60 * 60)
            local newDateString = os.date("%d %b", newTimestamp)
            local padded_day = i
            if i < 10 then 
                padded_day = i .. '&nbsp;'
            end
            
            local description = ""
            local reward_string = ""
            if type(daily_special[i]) == "string" and daily_special[i] ~= "" then
            	description = daily_special[i]
			    local item = string.upper(daily_special[i])
			
			    if string.find(item, 'REFILL') or string.find(item, 'TOKEN') or string.find(item, 'GEARS') or string.find(item, 'KIT') or string.find(item, 'CHEST') then
			        reward_string = '[[File:' .. daily_special[i] .. '.png|link=|100px]]'
			    else
			    	reward_string = '[[File:' .. daily_special[i] .. '.png|link=|x272px]]'
			    end
            	
            end
            if daily_special_desc ~= nil and daily_special_desc[i] ~= nil then
			    description = daily_special_desc[i]
			end
            
            text_string = text_string .. '<tt><div style="white-space: nowrap; display: flex; gap: 10px; justify-content: flex-start;">'
            text_string = text_string .. '<div class="mw-customtoggle-' .. i  ..' mw-customtoggle-' .. i  ..'expand mw-customtoggle-' .. i  ..'collapse" style="width: 22px;"><span class="mw-collapsible" id="mw-customcollapsible-' .. i  ..'expand" style="text-align:center;">[+]</span><span class="mw-collapsible mw-collapsed" id="mw-customcollapsible-' .. i  ..'collapse" style="text-align:center;">[-]</span></div>'
            if string.lower(status) == 'live' then
            	text_string = text_string .. '<div style="font-weight: bold;"><span style="color: rgb(' .. Era_Array[5][3] .. ');">' .. newDateString .. '</span>&nbsp;'
            end
            text_string = text_string .. '<span style="color: rgb(' .. Era_Array[6][3] .. ');">Day ' .. padded_day .. '</span>&nbsp;' .. description .. '</div></div></tt>'
            text_string = text_string .. '<div id="mw-customcollapsible-' .. i  ..'" class="mw-collapsible mw-collapsed">' .. reward_string .. '</div>'
        end
    end
    
    text_string = text_string .. '</div>'
    return text_string
end


local function event_buildings(link)
	local text_string = ''
	link = link or ''
	
	if evolution_building ~= nil then
		for i = 1, #evolution_building do
			if i > 1 then
				text_string = text_string .. ', '
			end
			
			if string.lower(link) == 'link' then
				text_string = text_string .. '<span style="white-space: nowrap;">[[Buildings/' .. evolution_building[i] .. '|' .. evolution_building[i] .. ']]</span>'
			else
				text_string = text_string .. '<span style="white-space: nowrap;">' .. evolution_building[i] .. '</span>'
			end
		end
	end
	
	return text_string
end


local function event_buildings_heading()
	local text_string = ''
	 
	if evolution_building ~= nil and #evolution_building > 0 then
		text_string = '<h2>Event Building'
	
		if #evolution_building > 1 then
			text_string = text_string .. 's'
		end
		text_string = text_string .. '</h2><div style="display: flex; flex-wrap: wrap; gap: 24px;">'
		for i = 1, #evolution_building do
			text_string = text_string .. '<table class="article-table"><tr class="bg-th"><td>[[File:' .. evolution_building[i] .. '.png|center|100px|link=]]</td></tr><tr><td>' .. GenMod.Icon(evolution_building[i] .. ' Token') .. ' [[Buildings/' .. evolution_building[i] .. '|' .. evolution_building[i] .. ']]</td></tr></table>'
		end
		text_string = text_string .. '</div>'
	end
	
	return text_string
end


function p.main(data)
	
	if type(data.args[1]) ~= 'string' or data.args[1] == '' then
		return
    else
		data.args[1] = data.args[1]:gsub('^%s+', '') --Strip leading spaces
		data.args[1] = data.args[1]:gsub('%s+$', '') --Strip trailing spaces
	end
	
	local event_module = 'Module:Events/' .. data.args[1]
	
	-- If event module doesn't exist then return
	if mw.title.new(event_module).exists == false then
		return
	end
	
	local strings = {}
	require(event_module)
	
	if type(data.args[2]) == 'string' then
		data.args[2] = data.args[2]:gsub('^%s+', '') --Strip leading spaces
		data.args[2] = data.args[2]:gsub('%s+$', '') --Strip trailing spaces
		if string.lower(data.args[2]) == 'video' then return video end
		if string.lower(data.args[2]) == 'date' then return formatDate() end
		if string.lower(data.args[2]) == 'token' then return token end
		if string.lower(data.args[2]) == 'active' then return active() end
		if string.lower(data.args[2]) == 'progress' then return progress end
		if string.lower(data.args[2]) == 'status' then return status:lower() end
		if string.lower(data.args[2]) == 'countdown' then return countdown() end
		if string.lower(data.args[2]) == 'start_tokens' then return start_tokens end
		if string.lower(data.args[2]) == 'daily_tokens' then return daily_tokens end
		if string.lower(data.args[2]) == 'daily_special' then return daily_specials() end
		if string.lower(data.args[2]) == 'incident_tokens' then return incident_tokens end
		if string.lower(data.args[2]) == 'event_buildings' then return event_buildings() end
		if string.lower(data.args[2]) == 'event_buildings_link' then return event_buildings('link') end
		if string.lower(data.args[2]) == 'event_buildings_heading' then return event_buildings_heading() end
		return
	end
	
	if questline ~= nil then
		quest_total = 0 --Hold the total of all previous tasks before the current questline that is being contructed. Used to calculate the date
		for questlines = 1, #questline do
			
			if questlines > 1 then table.insert(strings, '<br><br>') end
				
			-- Section
			table.insert(strings, '<h2>' .. quest_header .. ' ' .. questlines .. '/' .. #questline .. '</h2>')
			
			for quest = 1, #questline[questlines] do
				if quest == 1 then
					-- Questline table
					table.insert(strings, '<div style="overflow: auto;">')
					table.insert(strings, '<table class="table mw-collapsible mw-collapsed" style="padding: 0px;">')
					table.insert(strings, '<tr><td></td></tr>')
					table.insert(strings, '<tr><td>')
			
					table.insert(strings, '<div style="display: flex; gap: 24px; align-items: flex-start;">')
					
					table.insert(strings, '<div style="border: 1px black solid; border-radius: 8px; min-width: 120px; max-width: 120px; background: #f3e4af; text-align: center;">')
					table.insert(strings, '<div style=" text-align: center; vertical-align: bottom; background: radial-gradient(white, #359baf); border-radius: 8px 8px 0px 0px;">' .. GenMod.Icon(questline_questgiver[questlines],100) .. '</div>')
					table.insert(strings, '<div style="padding: 4px; color: #4f3e0f;">' .. questline_reward[questlines] .. '</div>')
					table.insert(strings, '</div>')
				end
				
				--Calculate which date the tasks are available.
				if data.args[1] == 'Hercules Event (2021)' then
					--Hercules Event (2021) starts with 38 then 1 per day
					if (quest_total + quest) < 39 then
						add_days = 0
					else
						add_days = math.floor(((quest_total + quest) - 39))+1
					end	
				else
					--General formula: Start with 28 then 4 per day
					if (quest_total + quest) < 29 then
						add_days = 0
					else
						add_days = math.floor(((quest_total + quest) - 29)/4)+1
					end
				end
				
				quest_date = formatDate(add_days)
				
				table.insert(strings, '<div style="display: flex; width: 630px; gap: 12px; flex-direction: column;">')
				table.insert(strings, '<div style="background: #25778a; border-radius: 8px; overflow: hidden;">')
				if left_colour == nil or left_colour == '' then left_colour = '#329CB3' end
				table.insert(strings, '<div style="display: flex; justify-content: center; gap: 4px; height: min-content; background: linear-gradient(to right, ' .. left_colour .. ', #329CB3 100%); border-radius: 8px 8px 0 0; padding: 4px; color: white;">')
				table.insert(strings, '<div style="text-shadow: 0px 0px 3px black; font-weight: bold; flex: 1; text-align: left;">' .. quest_header .. ' ' .. questlines .. '/' .. #questline .. '</div><div style="text-shadow: 0px 0px 3px black; font-weight: bold; flex: 1; text-align: center;">' .. quest_date .. '</div><div style="text-shadow: 0px 0px 3px black; font-weight: bold; flex: 1; text-align: right;">' .. quest .. '/' .. #questline[questlines] .. '</div>')
				table.insert(strings, '</div>')
				table.insert(strings, '<div style="min-height: 4px; max-height: 4px; background: linear-gradient(#404040, #25778a);"></div>')
				table.insert(strings, '<div style="display: flex; gap: 4px; padding: 4px;">')
				table.insert(strings, '<div style="min-width: 80%; max-width: 80%;">')
				if type(questline[questlines][quest][1]) ~= 'string' or questline[questlines][quest][1] == '' then questline[questlines][quest][1] = '[task 1]' end -- Error handler if the incorrect type or blank data is used
				table.insert(strings, '<div style="display: flex; min-height: 64px; align-items: center; background: #f3e4af; border-radius: 8px; padding: 4px;"><span style="color: #4f3e0f;">' .. highlight(questline[questlines][quest][1]) .. '</span></div>')
				
				if questline[questlines][quest][2] ~= nil then
					table.insert(strings, '<div style="position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 19px; margin: 4px;">')
					table.insert(strings, '<div style="width: 100%; height: 2px; vertical-align: middle; background: linear-gradient(to right, transparent, #6bbcc9, #6bbcc9, #6bbcc9, transparent);"></div>')
					table.insert(strings, '<div style="position: absolute; top: 0px; left: 0px; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;"><div style="width: 60px; height: 19px; background-color: #6bbcc9; border-radius: 90px;"></div></div>')
					table.insert(strings, '<div style="position: absolute; top: 0px; left: 0px; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;"><div style="line-height: 19px; color: #25778a; font-weight: bold;">OR</div></div>')
					table.insert(strings, '</div>')
					
					-- Error handler if the incorrect type or blank data is used	
					if type(questline[questlines][quest][2]) ~= 'string' or questline[questlines][quest][2] == '' then
						questline[questlines][quest][2] = '[task 2]'
					end
					table.insert(strings, '<div style="display: flex; min-height: 64px; align-items: center; background: #f3e4af; border-radius: 8px; padding: 4px;"><span style="color: #4f3e0f;>' .. highlight(questline[questlines][quest][2]) .. '</span></div>')
				end
				
				table.insert(strings, '</div>')
				table.insert(strings, '<div style="display: flex; width: 20%; align-items: center; justify-content: center; background: #f3e4af; border-radius: 8px; padding: 4px;"><span style="text-align: center; color: #4f3e0f;">')
				if questline[questlines][quest][0] ~= nil then
					table.insert(strings, questline[questlines][quest][0]) 
				else
					table.insert(strings, GenMod.Icon(token) .. ' ' .. quest_reward)
				end
				table.insert(strings, '</span></div>')
				table.insert(strings, '</div>')
				table.insert(strings, '</div>')
			end
			
			table.insert(strings, '</div>')
			table.insert(strings, '</td></tr></table>')
			table.insert(strings, '</div>')
			quest_total = quest_total + #questline[questlines]
		end
	end
	
	if #strings > 0 and questline_extra ~= nil and #questline_extra > 0 then
		table.insert(strings, '<br><br>')
	
		for questlines_extra = 1, #questline_extra do
			
			if questlines_extra > 1 then table.insert(strings, '<br><br>') end
				
			-- Section
			table.insert(strings, '<h2>' .. quest_extra_header .. ' ' .. questlines_extra .. '/' .. #questline_extra .. '</h2>')
			
			for quest = 1, #questline_extra[questlines_extra] do
				if quest == 1 then
					-- Questline table
					table.insert(strings, '<div style="overflow: auto;">')
					table.insert(strings, '<table class="table mw-collapsible mw-collapsed" style="padding: 0px;">')
					table.insert(strings, '<tr><td></td></tr>')
					table.insert(strings, '<tr><td>')
			
					table.insert(strings, '<div style="display: flex; gap: 24px; align-items: flex-start;">')
					
					table.insert(strings, '<div style="border: 1px black solid; border-radius: 8px; min-width: 120px; max-width: 120px; background: #f3e4af; text-align: center;">')
					table.insert(strings, '<div style=" text-align: center; vertical-align: bottom; background: radial-gradient(white, #359baf); border-radius: 8px 8px 0px 0px;">' .. GenMod.Icon(questline_extra_questgiver[questlines_extra],100) .. '</div>')
					table.insert(strings, '<div style="padding: 4px; color: #4f3e0f;">' .. questline_extra_reward[questlines_extra] .. '</div>')
					table.insert(strings, '</div>')
				end
				
				table.insert(strings, '<div style="display: flex; width: 630px; gap: 12px; flex-direction: column;">')
				table.insert(strings, '<div style="background: #25778a; border-radius: 8px; overflow: hidden;">')
				if left_colour == nil or left_colour == '' then left_colour = '#329CB3' end
				table.insert(strings, '<div style="display: flex; align-items: center; gap: 4px; width: 100%; height: min-content; background: linear-gradient(to right, ' .. left_colour .. ', #329CB3 100%); border-radius: 8px 8px 0 0; padding: 4px; color: white;">')
				table.insert(strings, '<div style="flex: 1; text-shadow: 0px 0px 3px black; font-weight: bold;">' .. quest_extra_header .. '</div><span style="text-shadow: 0px 0px 3px black; font-weight: bold;">' .. quest .. '/' .. #questline_extra[questlines_extra] .. '</span>')
				table.insert(strings, '</div>')
				table.insert(strings, '<div style="min-height: 4px; max-height: 4px; background: linear-gradient(#404040, #25778a);"></div>')
				table.insert(strings, '<div style="display: flex; gap: 4px; padding: 4px;">')
				table.insert(strings, '<div style="min-width: 80%; max-width: 80%;">')
				if type(questline_extra[questlines_extra][quest][1]) ~= 'string' or questline_extra[questlines_extra][quest][1] == '' then questline_extra[questlines_extra][quest][1] = '[task 1]' end -- Error handler if the incorrect type or blank data is used
				table.insert(strings, '<div style="display: flex; min-height: 64px; align-items: center; background: #f3e4af; border-radius: 8px; padding: 4px;"><span style="color: #4f3e0f;">' .. highlight(questline_extra[questlines_extra][quest][1]) .. '</span></div>')
				
				if questline_extra[questlines_extra][quest][2] ~= nil then
					table.insert(strings, '<div style="position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 19px; margin: 4px;">')
					table.insert(strings, '<div style="width: 100%; height: 2px; vertical-align: middle; background: linear-gradient(to right, transparent, #6bbcc9, #6bbcc9, #6bbcc9, transparent);"></div>')
					table.insert(strings, '<div style="position: absolute; top: 0px; left: 0px; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;"><div style="width: 60px; height: 19px; background-color: #6bbcc9; border-radius: 90px;"></div></div>')
					table.insert(strings, '<div style="position: absolute; top: 0px; left: 0px; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;"><div style="line-height: 19px; color: #25778a; font-weight: bold;">OR</div></div>')
					table.insert(strings, '</div>')
					
					-- Error handler if the incorrect type or blank data is used
					if type(questline_extra[questlines_extra][quest][2]) ~= 'string' or questline_extra[questlines_extra][quest][2] == '' then
						questline_extra[questlines_extra][quest][2] = '[task 2]'
					end
					table.insert(strings, '<div style="display: flex; min-height: 64px; align-items: center; background: #f3e4af; border-radius: 8px; padding: 4px;"><span style="color: #4f3e0f;">' .. highlight(questline_extra[questlines_extra][quest][2]) .. '</span></div>')

				end
				
				table.insert(strings, '</div>')
				table.insert(strings, '<div style="display: flex; width: 20%; align-items: center; justify-content: center; background: #f3e4af; border-radius: 8px; padding: 4px;"><span style="text-align: center; color: #4f3e0f;>' .. questline_extra[questlines_extra][quest][0] .. '</span></div>')
				table.insert(strings, '</div>')
				table.insert(strings, '</div>')
			end
			
			table.insert(strings, '</div>')
			table.insert(strings, '</td></tr></table>')
			table.insert(strings, '</div>')
		end
	end
	
	return table.concat(strings)

end


function highlight(quest)
	
	quest = string.gsub(quest, "Collect (Coins)", "Collect <b>%1</b>")
	quest = string.gsub(quest, "Collect (Food)", "Collect <b>%1</b>")
	quest = string.gsub(quest, "Collect (%d+) production(s?) in (Farms) from (%[Previous Era%] or %[Current Era%]) in (Capital City)", "Collect <b>%1</b> production%2 in <b>%3</b> from <b>%4</b> in <b>%5</b>")
	quest = string.gsub(quest, "Collect (%d+ short) production(s?) in (Farms) from (%[Previous Era%] or %[Current Era%]) in (Capital City)", "Collect <b>%1</b> production%2 in <b>%3</b> from <b>%4</b> in <b>%5</b>")
	quest = string.gsub(quest, "Collect (%d+ medium) production(s?) in (Farms) from (%[Previous Era%] or %[Current Era%]) in (Capital City)", "Collect <b>%1</b> production%2 in <b>%3</b> from <b>%4</b> in <b>%5</b>")
	quest = string.gsub(quest, "Collect (%d+ long) production(s?) in (Farms) from (%[Previous Era%] or %[Current Era%]) in (Capital City)", "Collect <b>%1</b> production%2 in <b>%3</b> from <b>%4</b> in <b>%5</b>")
	quest = string.gsub(quest, "Collect (%d+) production(s?) in (Luxurious Farm) from (%[Previous Era%] or %[Current Era%]) in (Capital City)", "Collect <b>%1</b> production%2 in <b>%3</b> from <b>%4</b> in <b>%5</b>")
	
	quest = string.gsub(quest, "Pay (Coins)", "Pay <b>%1</b>")
	quest = string.gsub(quest, "Pay (Food)", "Pay <b>%1</b>")
	quest = string.gsub(quest, "Pay (%d+ Research Points?)", "Pay <b>%1</b>")
	quest = string.gsub(quest, "Pay (%w+ Goods)", "Pay <b>%1</b>")
	quest = string.gsub(quest, "Pay (%d+ Infantry units?)", "Pay <b>%1</b>")
	quest = string.gsub(quest, "Pay (%d+ Cavalry units?)", "Pay <b>%1</b>")
	quest = string.gsub(quest, "Pay (%d+ Ranged units?)", "Pay <b>%1</b>")
	quest = string.gsub(quest, "Pay (%d+ Heavy Infantry units?) %(or era equivalent%)", "Pay <b>%1</b> (or era equivalent)")
	quest = string.gsub(quest, "Pay (%d+ Siege units?) %(or era equivalent%)", "Pay <b>%1</b> (or era equivalent)")
	
	quest = string.gsub(quest, "Accept (%d+) trade offer(s?)", "Accept <b>%1</b> trade offer%2")
	
	quest = string.gsub(quest, "Research (%d+) (technolog%w+)", "Research <b>%1</b> %2")
	
	quest = string.gsub(quest, "Solve (%d+) incident(s?)", "Solve <b>%1</b> incident%2")
	
	quest = string.gsub(quest, "Spend (%d+) Research Point(s?) %(e.g. in the Research Tree or Wonders%)", "Spend <b>%1</b> Research Point%2 (e.g. in the Research Tree or Wonders)")
	
	quest = string.gsub(quest, "Win (%d+) battle(s?)", "Win <b>%1</b> battle%2")
	
	quest = string.gsub(quest, "Acquire (%d+) part(s?) of any region", "Acquire <b>%1</b> part%2 of any region")
	
	quest = string.gsub(quest, "Have (1 .*) on level (%d+) in (Capital City)", "Have <b>%1</b> on level <b>%2</b> in <b>%3</b>")
	
	return quest
end

return p