Module:Items/ComparisonTables/Sandbox

From Melvor Idle

Documentation for this module may be created at Module:Items/ComparisonTables/Sandbox/doc

local p = {}

local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local Common = require('Module:Common')
local Modifiers = require('Module:Modifiers')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Num = require('Module:Number')


local Debug = require('Module:Debug')

local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}

local function getAttackSpeed(item)
	if item.equipmentStats ~= nil and item.equipmentStats['attackSpeed'] ~= nil then
		return item.equipmentStats['attackSpeed'] or 4000
	end
	return 0
end

local function getSlotID(slot)
	-- If slot is a slot name, convert it to the slot ID instead
	local slotID = Shared.getNamespacedID('melvorD', slot)
	local slotData = GameData.getEntityByID('equipmentSlots', slotID)
	-- Validate slotID
	if slotData == nil then
		-- slotID invalid, check if user provided a slot name
		slotData = GameData.getEntityByProperty('equipmentSlots', 'emptyName', slot)
		if slotData == nil then
			return nil
		end
		slotID = slotData.id
	end
	return slotID
end

local function getItems(slotID)
	local slotNS, slotLocalID = Shared.getLocalID(slotID)
	
	local sortFunc = function(item)
		-- Exclude the debug item
		if item.id == 'melvorD:DEBUG_ITEM' then
			return false
		end
		-- Exclude Golbin raid exclusives for now, such that they don't pollute various equipment tables
		if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
			return false
		end
		
		local isMatch = true
		local sID = slotLocalID
		if not Shared.contains(item.validSlots, sID) then 
			isMatch = false 
		end
		
		if isMatch and other ~= nil then
			if slot == 'Cape' then
				-- TODO Would be more reliable if based on items appearing within the relevant shop categories instead
				local isSkillcape = Shared.contains(item.name, 'Skillcape') or Shared.contains(item.name, 'Cape of Completion')
				if other == 'Skillcapes' then
					isMatch = isSkillcape
				elseif other == 'No Skillcapes' then
					isMatch = not isSkillcape
				end
			end
			if slotLocalID == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
				isMatch = other == item.ammoTypeRequired
			elseif slotLocalID == 'Quiver' then
				if other == 'Thrown' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
					isMatch = true
				else
					isMatch = other == item.ammoType
				end
			end
		end
		return isMatch
	end

	return Items.getItems(sortFunc)
end

--== Helper Functions for getCategoryTable ==--
local function createStatCell(row, statVal)
	local cell = row:tag('td')
	if statVal > 0 then
		cell:addClass('table-positive')
	elseif statVal < 0 then
		cell:addClass('table-negative')
	end
	cell:css('text-align', 'right')
	return cell
end

local function addStatCell(row, item, stat)
	local statVal = 0
	if item.equipmentStats ~= nil then
		statVal = item.equipmentStats[stat] or 0
	end
	
	return createStatCell(row, statVal)
		:wikitext(statVal)
end

local function addDRCell(row, item)
	local dr = 0
	local icon = nil
	
	-- Grab damage reduction figure
	if item.equipmentStats ~= nil then
		if item.equipmentStats.damageReduction then
			dr, icon = item.equipmentStats.damageReduction, 'Damage Reduction'
		elseif item.equipmentStats.resistance then
			dr, icon = item.equipmentStats.resistance, 'Abyssal Resistance'
		end
	end
	
	local cell = createStatCell(row, dr)
	
	-- Add DR icons, if there's any value
	if dr ~= 0 then
		cell:wikitext(Icons.Icon({icon, size=15, notext='true'}) .. ' ')
	end
	
	-- Add DR value
	cell:wikitext(dr .. '%')
	return cell
end

function p._getCategoryTable(slot)
	local iconSize = 20
	local slotID = getSlotID(slot)
	if slotID == nil then
		return Shared.printError('Invalid slot ID: ' .. (slot or 'nil'))
	end
	
	-- Always sort by name.
	local itemList = getItems(slotID)
	table.sort(itemList, function(a, b) return a.name < b.name end)
	
	local isWeapon = (slot == 'Weapon')
	local itemColspan = 3
	if isWeapon == true then itemColspan = 4 end
	
	local html = mw.html.create('table')
		:addClass('wikitable sortable stickyHeader')
		:addClass('col-1-center col-3-center')

	local header0 = html:tag('tr'):addClass('headerRow-0')
	header0:tag('th'):attr('colspan', itemColspan)
	header0:tag('th'):attr('colspan', 5)
					 :wikitext("Attack Bonus")
	header0:tag('th'):attr('colspan', 3)
					 :wikitext("Strength Bonus")
	header0:tag('th'):attr('colspan', 3)
					 :wikitext("Defence Bonus")
	
	-- TODO: if abyssal switch text
	header0:tag('th'):wikitext("DR/AR")
	header0:tag('th') -- Empty row above "Equip Req"
	
	local header1 = html:tag('tr'):addClass('headerRow-1')
	header1:tag('th'):wikitext('Name')
					 :attr('colspan', 2)
	header1:tag('th'):wikitext('DLC')
	if isWeapon == true then
		header1:tag('th'):wikitext('Attack<br>Speed')
	end

	-- Attack bonuses
	header1:tag('th'):wikitext(Icons.Icon({'Attack', type='skill', size=iconSize, notext='true'}))
	header1:tag('th'):wikitext(Icons.Icon({'Strength', type='skill', size=iconSize, notext='true'}))
	header1:tag('th'):wikitext(Icons.Icon({'Defence', type='skill', size=iconSize, notext='true'}))
	header1:tag('th'):wikitext(Icons.Icon({'Ranged', type='skill', size=iconSize, notext='true'}))
	header1:tag('th'):wikitext(Icons.Icon({'Magic', type='skill', size=iconSize, notext='true'}))

	--Strength bonuses
	header1:tag('th'):wikitext(Icons.Icon({'Strength', type='skill', size=iconSize, notext='true'}))
	header1:tag('th'):wikitext(Icons.Icon({'Ranged', type='skill', size=iconSize, notext='true'}))
	header1:tag('th'):wikitext(Icons.Icon({'Magic', type='skill', size=iconSize, notext='true'}))

	--Defence bonuses
	header1:tag('th'):wikitext(Icons.Icon({'Strength', type='skill', size=iconSize, notext='true'}))
	header1:tag('th'):wikitext(Icons.Icon({'Ranged', type='skill', size=iconSize, notext='true'}))
	header1:tag('th'):wikitext(Icons.Icon({'Magic', type='skill', size=iconSize, notext='true'}))
	
	-- Damage reduction
	header1:tag('th'):wikitext(Icons.Icon({'Damage Reduction', size=iconSize, notext='true'}))
	
	--Level requirements
	header1:tag('th'):wikitext('Equip Req')
	Debug.log(itemList)
	-- Fill the table with all items
	for _, item in ipairs(itemList) do
		local row = html:tag('tr')
		row:tag('td'):wikitext(Icons.Icon({item.name, type='item', notext=true}))
					 :attr('data-sort-value', item.name)
		row:tag('td'):wikitext(Icons.Icon({item.name, type='item', noicon=true}))
					 :attr('data-sort-value', item.name)
		row:tag('td'):wikitext(Icons.getDLCColumnIcon(item.id))
					 :attr('data-sort-value', Icons.getExpansionID(item.id))

		-- Add attack speed.
		if isWeapon == true then
			local atkSpeed = getAttackSpeed(item)
			row:tag('td'):wikitext(Num.round(atkSpeed / 1000, 3, 1) .. 's')
						 :attr('data-sort-value', atkSpeed)
						 :css('text-align', 'right')
		end
		
		-- Attack bonuses
		addStatCell(row, item, 'stabAttackBonus')
		addStatCell(row, item, 'slashAttackBonus')
		addStatCell(row, item, 'blockAttackBonus')
		addStatCell(row, item, 'rangedAttackBonus')
		addStatCell(row, item, 'magicAttackBonus')
		
		-- Strength bonuses
		addStatCell(row, item, 'meleeStrengthBonus')
		addStatCell(row, item, 'rangedStrengthBonus')
		addStatCell(row, item, 'magicDamageBonus'):wikitext('%')	

		-- Defence bonuses
		addStatCell(row, item, 'meleeDefenceBonus')
		addStatCell(row, item, 'rangedDefenceBonus')
		addStatCell(row, item, 'magicDefenceBonus')

		-- Add Damage Reduction / Abyssal Resistance
		addDRCell(row, item)
	
		-- TODO: Format requirements
		row:tag('td'):wikitext('None')
		--'attackLevelRequired', 'strengthLevelRequired', 
		--'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired',
		--'attackAbyssalLevel', 'strengthAbyssalLevel','defenceAbyssalLevel',
		--'rangedAbyssalLevel', 'magicAbyssalLevel'		
	end

	return tostring(html)
end

-- TODO:
--- Split Melvor / Abyss items (based on realm?)
--- Split 1h and 2h weapons
--- When the category is 'Weapon', include every wieldable items (incl knives, javs, etc)
function p.getCategoryTable(frame)
	local slot = frame.args ~= nil and frame.args[1] or frame[1]

	return p._getCategoryTable(slot)
end

return p