Module:Skills/Archaeology: Difference between revisions

From Melvor Idle
No edit summary
No edit summary
 
(15 intermediate revisions by 4 users not shown)
Line 6: Line 6:


local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local Constants = require('Module:Constants')
local Common = require('Module:Common')
local GameData = require('Module:GameData')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local SkillData = GameData.skillData
local Modifiers = require('Module:Modifiers')
local Items = require('Module:Items')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Cartography = require('Module:Skills/Cartography')
local Cartography = require('Module:Skills/Cartography')
local Num = require('Module:Number')


local sizes = {'small', 'tiny', 'medium', 'large'}
local sizes = {'small', 'tiny', 'medium', 'large'}
Line 33: Line 35:
end
end
return nil
return nil
end
function p._getArtefactType(item)
if item.isArtefact then
if item.isGenericArtefact then
return 'Generic'
elseif item.validSlots ~= nil then
local searchSlots = { 'Consumable', 'Gem' }
for i, slotID in ipairs(searchSlots) do
if Shared.contains(item.validSlots, slotID) then
return Common.getEquipmentSlotLink(slotID)
end
end
end
return 'Other'
end
end
end


Line 65: Line 83:
end
end
end)
end)
local lootValue, plusOneMinimumRefinementValue, doubleConsumableRefinementValue = {}, {}, {}
for i, row in ipairs(lootTable) do
for i, row in ipairs(lootTable) do
local thisItem = Items.getItemByID(row.itemID)
local thisItem = Items.getItemByID(row.itemID)
Line 75: Line 94:
end
end
table.insert(result, '||'..thisItem.type..'')
table.insert(result, '||' .. p._getArtefactType(thisItem) .. '')
table.insert(result, '||style="text-align:right" data-sort-value="'..row.maxQuantity..'"|')
table.insert(result, '||style="text-align:right" data-sort-value="'..row.maxQuantity..'"|')
if row.maxQuantity > row.minQuantity then
if row.maxQuantity > row.minQuantity then
table.insert(result, Shared.formatnum(row.minQuantity) .. ' - ')
table.insert(result, Num.formatnum(row.minQuantity) .. ' - ')
end
end
table.insert(result, Shared.formatnum(row.maxQuantity))
table.insert(result, Num.formatnum(row.maxQuantity))


--Adding price columns
local dropChance = row.weight / totalWt
local itemPrice = 0
-- Adding price columnsR
if thisItem == nil then
if thisItem == nil then
table.insert(result, '||data-sort-value="0"|???')
table.insert(result, '||data-sort-value="0"|???')
else
else
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
table.insert(result, '||' .. Items.getValueText(thisItem, row.minQuantity, row.maxQuantity))
if itemPrice == 0 or row.maxQuantity == row.minQuantity then
 
table.insert(result, '||'..Icons.GP(itemPrice * row.minQuantity))
-- Add to total loot value
else
local sellAmount = thisItem.sellsFor or 0
table.insert(result, '||'..Icons.GP(itemPrice * row.minQuantity, itemPrice * row.maxQuantity))
local sellCurrency = thisItem.sellsForCurrency or 'melvorD:GP'
if sellAmount ~= nil and sellCurrency ~= nil then
if lootValue[sellCurrency] == nil then
lootValue[sellCurrency] = 0
plusOneMinimumRefinementValue[sellCurrency] = 0
doubleConsumableRefinementValue[sellCurrency] = 0
end
local artefactAvgValue = dropChance * sellAmount * ((row.minQuantity + row.maxQuantity) / 2)
lootValue[sellCurrency] = lootValue[sellCurrency] + artefactAvgValue
plusOneMinimumRefinementValue[sellCurrency] = plusOneMinimumRefinementValue[sellCurrency] + artefactAvgValue + (dropChance * sellAmount)
-- Checking if item is a consumable, then adding its value a second time
local doubleExtraVal = (Items._canItemUseSlot(thisItem, 'Consumable') and artefactAvgValue) or 0
doubleConsumableRefinementValue[sellCurrency] = doubleConsumableRefinementValue[sellCurrency] + artefactAvgValue + doubleExtraVal
end
end
end
end


--Getting the drop chance
local dropChance = (row.weight / totalWt)
if dropChance < 100 then
if dropChance < 100 then
--Show fraction as long as it isn't going to be 1/1
--Show fraction as long as it isn't going to be 1/1
table.insert(result, '||style="text-align:right" data-sort-value="'..row.weight..'"')
table.insert(result, '||style="text-align:right" data-sort-value="'..row.weight..'"')
table.insert(result, '|'..Shared.fraction(row.weight, totalWt))
table.insert(result, '|'..Num.fraction(row.weight, totalWt))
table.insert(result, '||')
table.insert(result, '||')
else
else
Line 109: Line 137:
local fmt = (dropChance < 0.10 and '%.2g') or '%.2f'
local fmt = (dropChance < 0.10 and '%.2g') or '%.2f'
table.insert(result, 'style="text-align:right"|'..string.format(fmt, dropChance * 100)..'%')
table.insert(result, 'style="text-align:right"|'..string.format(fmt, dropChance * 100)..'%')
end
 
lootValue = lootValue + (dropChance * thisItem.sellsFor * ((row.minQuantity + row.maxQuantity)/ 2))
local function lootValueText(lootValue)
plusOneMinimumRefinementValue = plusOneMinimumRefinementValue + (dropChance * thisItem.sellsFor * (((row.minQuantity+1) + (row.maxQuantity+1))/ 2))
local returnPart = {}
--Checking if item is a consumable, then adding its value a second time
for _, currencyDefn in ipairs(GameData.rawData.currencies) do
if thisItem._canItemUseSlot({thisItem}, 'Consumable') then
-- Guarantee order by iterating through currency game data definition
doubleConsumableRefinementValue = lootValue + (dropChance * thisItem.sellsFor * ((row.minQuantity + row.maxQuantity)/ 2))
local currID = currencyDefn.id
local val = Num.round(lootValue[currID], 2, 2)
if val ~= nil then
table.insert(returnPart, Icons._Currency(currID, val))
end
end
end
return table.concat(returnPart, ', ')
end
end
table.insert(result, '\r\n|}')
table.insert(result, '\r\n|}')
table.insert(result, '\r\nThe average value of one action is '..Icons.GP(Shared.round(lootValue, 2, 0))..'.')
local lootText = lootValueText(lootValue)
table.insert(result, '\r\nThe average value of one action with the +1 Minimum Items refinement is '..Icons.GP(Shared.round(plusOneMinimumRefinementValue, 2, 0))..'.')
local plusOneLootText = lootValueText(plusOneMinimumRefinementValue)
if doubleConsumableRefinementValue>lootValue then
local doubleLootText = lootValueText(doubleConsumableRefinementValue)
table.insert(result, '\r\nThe average value of one action with the x2 Consumables refinement is '..Icons.GP(Shared.round(doubleConsumableRefinementValue, 2, 0))..'.')
table.insert(result, '\r\nThe average value of one action is ' .. lootText .. ', ')
table.insert(result, 'increasing to ' .. plusOneLootText .. ' when the +1 Minimum Items refinement is active.')
if doubleLootText ~= lootText then
table.insert(result, '\r\n\r\nThe average value of one action with the x2 Consumables refinement is ' .. doubleLootText .. '.')
end
end
Line 193: Line 229:
return table.concat(resultArray, '')
return table.concat(resultArray, '')
end
function p._getMuseumRewards(reward)
local rewardTable = {}
local equipment = nil
if reward.gp ~= nil then
table.insert(rewardTable, Icons._Currency('melvorD:GP', reward.gp))
end
if reward.items ~= nil then
for _, reward in ipairs(reward.items) do
local item = Items.getItemByID(reward.id)
table.insert(rewardTable, Icons.Icon({item.name, type='item', qty=reward.quantity}))
if item.category == 'Archaeology' then
equipment = item.name
end
end
end
if reward.modifiers ~= nil then
table.insert(rewardTable, Modifiers.getModifiersText(reward.modifiers, true))
end
if equipment ~= nil then
table.insert(rewardTable, 'Unlocks the ability to purchase '..equipment..' from the '..Icons.Icon({'Shop'}))
end
return rewardTable
end
function p.getMuseumRewardsTable(frame)
-- Build the table
local resultTable = mw.html.create('table')
resultTable:addClass('wikitable sortable lighttable')
resultTable:tag('tr'):addClass('headerRow-0')
:tag('th'):wikitext('Donations')
:tag('th'):wikitext('Rewards')
for _, reward in ipairs(SkillData.Archaeology.museumRewards) do
local tr = mw.html.create('tr')
tr:tag('td'):wikitext(reward.museumCount)
tr:tag('td'):wikitext(table.concat(p._getMuseumRewards(reward), '<br/>'))
resultTable:node(tr)
end
return tostring(resultTable)
end
end


return p
return p

Latest revision as of 22:45, 29 June 2024

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

--New module for Archaeology-related tables
--Unavoidably has some overlap with Module:Skills/Cartography
--Crucially, stuff that involves both MUST go here
--Because otherwise we get circular references, which are Not Fun.
local p = {}

local Shared = require('Module:Shared')
local Common = require('Module:Common')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Modifiers = require('Module:Modifiers')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Cartography = require('Module:Skills/Cartography')
local Num = require('Module:Number')

local sizes = {'small', 'tiny', 'medium', 'large'}

function p.getDigSite(name)
	name = string.gsub(name, "%%27", "'")
	name = string.gsub(name, "&#39;", "'")
	for i, digSite in ipairs(SkillData.Archaeology.digSites) do
		if digSite.name == name then
			return digSite
		end
	end
	return nil
end

function p.getDigSiteByID(id)
	for i, digSite in ipairs(SkillData.Archaeology.digSites) do
		if digSite.id == id then
			return digSite
		end
	end
	return nil
end

function p._getArtefactType(item)
	if item.isArtefact then
		if item.isGenericArtefact then
			return 'Generic'
		elseif item.validSlots ~= nil then
			local searchSlots = { 'Consumable', 'Gem' }
			for i, slotID in ipairs(searchSlots) do
				if Shared.contains(item.validSlots, slotID) then
					return Common.getEquipmentSlotLink(slotID)
				end
			end
		end
		return 'Other'
	end
end

function p._getDigSiteArtefactTable(digSite, size)
	if not Shared.contains(sizes, string.lower(size)) then
		return Shared.printError(size..' is an invalid size for Archeology artefacts.')
	end
	
	local result = {}
	table.insert(result, '{|class="wikitable sortable" id="itemdrops"')
	table.insert(result, '\r\n!Item!!Type!!Qty')
	table.insert(result, '!!Price!!colspan="2"|Chance')
	
	local lootTable = Shared.shallowClone(digSite.artefacts[string.lower(size)])
	local lootValue = 0
	local plusOneMinimumRefinementValue = 0
	local doubleConsumableRefinementValue = 0
	local totalWt = 0
	for i, row in ipairs(lootTable) do
		totalWt = totalWt + row.weight
	end
	table.sort(lootTable, function(a, b)
			if a.weight == b.weight then
				local aItem, bItem = Items.getItemByID(a.itemID), Items.getItemByID(b.itemID)
				if aItem ~= nil and bItem ~= nil then
					return aItem.name < bItem.name
				else
					return a.itemID < b.itemID
				end
			else
				return a.weight > b.weight
			end
	end)
	local lootValue, plusOneMinimumRefinementValue, doubleConsumableRefinementValue = {}, {}, {}
	for i, row in ipairs(lootTable) do
		local thisItem = Items.getItemByID(row.itemID)
		
		
		if thisItem ~= nil then
			table.insert(result, '\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}))
		else
			table.insert(result, '\r\n|-\r\n|Unknown Item[[Category:Pages with script errors]]')
		end
		
		table.insert(result, '||' .. p._getArtefactType(thisItem) .. '')
		table.insert(result, '||style="text-align:right" data-sort-value="'..row.maxQuantity..'"|')
		if row.maxQuantity > row.minQuantity then
			table.insert(result, Num.formatnum(row.minQuantity) .. ' - ')
		end
		table.insert(result, Num.formatnum(row.maxQuantity))

		local dropChance = row.weight / totalWt
		-- Adding price columnsR
		if thisItem == nil then
			table.insert(result, '||data-sort-value="0"|???')
		else
			table.insert(result, '||' .. Items.getValueText(thisItem, row.minQuantity, row.maxQuantity))

			-- Add to total loot value
			local sellAmount = thisItem.sellsFor or 0
			local sellCurrency = thisItem.sellsForCurrency or 'melvorD:GP'
			if sellAmount ~= nil and sellCurrency ~= nil then
				if lootValue[sellCurrency] == nil then
					lootValue[sellCurrency] = 0
					plusOneMinimumRefinementValue[sellCurrency] = 0
					doubleConsumableRefinementValue[sellCurrency] = 0
				end
				local artefactAvgValue = dropChance * sellAmount * ((row.minQuantity + row.maxQuantity) / 2)
				lootValue[sellCurrency] = lootValue[sellCurrency] + artefactAvgValue
				plusOneMinimumRefinementValue[sellCurrency] = plusOneMinimumRefinementValue[sellCurrency] + artefactAvgValue + (dropChance * sellAmount)
				-- Checking if item is a consumable, then adding its value a second time
				local doubleExtraVal = (Items._canItemUseSlot(thisItem, 'Consumable') and artefactAvgValue) or 0
				doubleConsumableRefinementValue[sellCurrency] = doubleConsumableRefinementValue[sellCurrency] + artefactAvgValue + doubleExtraVal
			end
		end

		if dropChance < 100 then
			--Show fraction as long as it isn't going to be 1/1
			table.insert(result, '||style="text-align:right" data-sort-value="'..row.weight..'"')
			table.insert(result, '|'..Num.fraction(row.weight, totalWt))
			table.insert(result, '||')
		else
			table.insert(result, '||colspan="2" data-sort-value="'..row.weight..'"')
		end
		-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
		local fmt = (dropChance < 0.10 and '%.2g') or '%.2f'
		table.insert(result, 'style="text-align:right"|'..string.format(fmt, dropChance * 100)..'%')
	end

	local function lootValueText(lootValue)
		local returnPart = {}
		for _, currencyDefn in ipairs(GameData.rawData.currencies) do
			-- Guarantee order by iterating through currency game data definition
			local currID = currencyDefn.id
			local val = Num.round(lootValue[currID], 2, 2)
			if val ~= nil then
				table.insert(returnPart, Icons._Currency(currID, val))
			end
		end
		return table.concat(returnPart, ', ')
	end
	
	table.insert(result, '\r\n|}')
	local lootText = lootValueText(lootValue)
	local plusOneLootText = lootValueText(plusOneMinimumRefinementValue)
	local doubleLootText = lootValueText(doubleConsumableRefinementValue)
	table.insert(result, '\r\nThe average value of one action is ' .. lootText .. ', ')
	table.insert(result, 'increasing to ' .. plusOneLootText .. ' when the +1 Minimum Items refinement is active.')
	if doubleLootText ~= lootText then
		table.insert(result, '\r\n\r\nThe average value of one action with the x2 Consumables refinement is ' .. doubleLootText .. '.')
	end
	
	return table.concat(result, '')
end

function p.getDigSiteArtefactTable(frame)
	local name = frame.args ~= nil and frame.args[1] or frame[1]
	local size = frame.args ~= nil and frame.args[2] or frame[2]
	
	local digSite = p.getDigSite(name)
	
	if digSite == nil then
		return Shared.printError('No Dig Site named '..name..' found')
	end
	
	return p._getDigSiteArtefactTable(digSite, size)
end

--Trying something new this time. Building the entire infobox in Lua and then passing it to the module.
function p.getDigSiteInfobox(frame)
	local name = frame.args ~= nil and frame.args[1] or frame
	name = string.gsub(name, "%%27", "'")
	name = string.gsub(name, "&#39;", "'")
	
	local digSite = p.getDigSite(name)
	
	if digSite == nil then
		return Shared.printError('No Dig Site named '..name..' found')
	end
	
	local poi = Cartography.getPointOfInterestForDigSite(digSite.id)
	local hex = Cartography.getHexByAxial(poi.coords.q, poi.coords.r)
	local coordX, coordY = Cartography.convertAxialToXY(poi.coords)
	
	local resultArray = {}
	
	table.insert(resultArray, '{| class="wikitable infobox"')
	--Expansion Symbol + Name
	table.insert(resultArray, '\r\n!')
	table.insert(resultArray, Icons.getExpansionIcon(digSite.id))
	table.insert(resultArray, digSite.name)
	--Image
	table.insert(resultArray, '\r\n|-\r\n| style="text-align:center" |')
	table.insert(resultArray, Icons.Icon({digSite.name, type='poi', size=250, notext='true'}))
	--ID
	table.insert(resultArray, "\r\n|-\r\n|'''ID:''' ")
	table.insert(resultArray, digSite.id)
	--Coordinates
	table.insert(resultArray, "\r\n|-\r\n|'''Coordinates:''' ")
	table.insert(resultArray, '('..coordX..', '..coordY..')')
	--Requirements
	table.insert(resultArray, "\r\n|-\r\n|'''Discovery Requirements:''' ")
	local reqTable = Cartography._getPOIRequirements(poi)
	if Shared.tableCount(reqTable) == 0 then
		table.insert(resultArray, 'None')
	else
		table.insert(resultArray, '\r\n* '..table.concat(reqTable, '\r\n* '))
	end
	table.insert(resultArray, "\r\n|-\r\n|'''Archaeology Level:''' ")
	table.insert(resultArray, Icons._SkillReq('Archaeology', digSite.level))
	--Description
	table.insert(resultArray, '\r\n|-\r\n| style="text-align:center" |')
	table.insert(resultArray, "''"..poi.description.."''")
	
	table.insert(resultArray, '\r\n|}')
	table.insert(resultArray, '\n[[Category:Dig Sites]]')
	
	
	return table.concat(resultArray, '')
end

function p._getMuseumRewards(reward)
	local rewardTable = {}
	local equipment = nil
	if reward.gp ~= nil then
		table.insert(rewardTable, Icons._Currency('melvorD:GP', reward.gp))
	end
	if reward.items ~= nil then
		for _, reward in ipairs(reward.items) do
			local item = Items.getItemByID(reward.id)
			table.insert(rewardTable, Icons.Icon({item.name, type='item', qty=reward.quantity}))
			if item.category == 'Archaeology' then
				equipment = item.name
			end
		end
	end
	if reward.modifiers ~= nil then
		table.insert(rewardTable, Modifiers.getModifiersText(reward.modifiers, true))
	end
	if equipment ~= nil then
		table.insert(rewardTable, 'Unlocks the ability to purchase '..equipment..' from the '..Icons.Icon({'Shop'}))
	end
	return rewardTable
end

function p.getMuseumRewardsTable(frame)
	-- Build the table
	local resultTable = mw.html.create('table')
	resultTable:addClass('wikitable sortable lighttable')
	resultTable:tag('tr'):addClass('headerRow-0')
		:tag('th'):wikitext('Donations')
		:tag('th'):wikitext('Rewards')
		
	for _, reward in ipairs(SkillData.Archaeology.museumRewards) do
		local tr = mw.html.create('tr')
		tr:tag('td'):wikitext(reward.museumCount)
		tr:tag('td'):wikitext(table.concat(p._getMuseumRewards(reward), '<br/>'))
		resultTable:node(tr)
	end
	return tostring(resultTable)
end

return p