17,481
edits
Falterfire (talk | contribs) (more right-aligning) |
(_getItemUses: Ensure 'All skills' is displayed before any skills) |
||
(57 intermediate revisions by 7 users not shown) | |||
Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local Constants = require('Module:Constants') | local Constants = require('Module:Constants') | ||
local Common = require('Module:Common') | |||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
local GameData = require('Module:GameData') | |||
local SkillData = GameData.skillData | |||
local Modifiers = require('Module:Modifiers') | |||
local Skills = require('Module:Skills') | |||
local Magic = require('Module:Magic') | local Magic = require('Module:Magic') | ||
local Areas = require('Module:CombatAreas') | local Areas = require('Module:CombatAreas') | ||
Line 13: | Line 14: | ||
local Agility = require('Module:Skills/Agility') | local Agility = require('Module:Skills/Agility') | ||
local Shop = require('Module:Shop') | local Shop = require('Module:Shop') | ||
local Num = require('Module:Number') | |||
--Brute forcing some item uses to make things easier | --Brute forcing some item uses to make things easier | ||
local itemUseArray = { | local itemUseArray = { | ||
Agility = {}, | |||
Astrology = {'melvorF:Stardust', 'melvorF:Golden_Stardust', 'melvorItA:Abyssal_Stardust'}, | |||
Archaeology = {}, | |||
Attack = {}, | |||
Cartography = {}, | |||
Combat = {'melvorF:Gold_Emerald_Ring', 'melvorD:Obsidian_Cape', 'melvorF:Throwing_Power_Gloves'}, | |||
Cooking = {'melvorD:Cooking_Gloves'}, | |||
Crafting = {'melvorItA:Abyssal_Crafting_Gloves'}, | |||
Defence = {}, | |||
Farming = {'melvorD:Compost', 'melvorD:Weird_Gloop', 'melvorItA:Abyssal_Compost', 'melvorD:Bobs_Rake'}, | |||
Firemaking = {'melvorItA:Abyssal_Firemaking_Gloves'}, | |||
Fishing = {'melvorD:Message_In_A_Bottle', 'melvorD:Barbarian_Gloves'}, | |||
Fletching = {'melvorItA:Abyssal_Fletching_Gloves'}, | |||
Harvesting = {'melvorItA:Abyssal_Harvesting_Gloves'}, | |||
Herblore = {'melvorItA:Abyssal_Herblore_Gloves'}, | |||
Hitpoints = {}, | |||
Magic = {}, | |||
Mining = {'melvorD:Mining_Gloves', 'melvorD:Gem_Gloves', 'melvorItA:Abyssal_Mining_Gloves'}, | |||
Prayer = {}, | |||
Ranged = {}, | |||
Runecrafting = {'melvorItA:Abyssal_Runecrafting_Gloves'}, | |||
Slayer = {}, | |||
Smithing = {'melvorD:Smithing_Gloves', 'melvorItA:Abyssal_Smithing_Gloves'}, | |||
Smithing = {}, | |||
local | Strength = {}, | ||
Summoning = {}, | |||
Thieving = {'melvorF:Thieving_Gloves'}, | |||
Township = {}, | |||
Woodcutting = {}, | |||
} | |||
-- List of modifier IDs which, if present on an item and not specific to any | |||
-- particular skill, indicate that the item applies to all skills | |||
local allSkillModIDs = { | |||
-- Skill XP | |||
'skillXP', | |||
'nonCombatSkillXP', | |||
'abyssalSkillXP', | |||
-- Mastery | |||
'flatMasteryTokens', | |||
'xpFromMasteryTokens', | |||
'masteryPoolCap', | |||
'masteryXP', | |||
'masteryPoolProgress', | |||
-- Resource preservation | |||
'bypassGlobalPreservationChance', | |||
'skillPreservationChance', | |||
'skillPreservationCap', | |||
-- Other preservation (consumables, summons, potion charges) | |||
'consumablePreservationChance', | |||
'summoningChargePreservationChance', | |||
'potionChargePreservationChance', | |||
-- Item doubling | |||
'globalItemDoublingChance', | |||
-- Resource quantity | |||
'flatBasePrimaryProductQuantity', | |||
'basePrimaryProductQuantity', | |||
'flatBaseRandomProductQuantity', | |||
'flatAdditionalSkillItem', | |||
'flatAdditionalPrimaryProductQuantity', | |||
-- Cost reduction | |||
'skillCostReduction', | |||
-- Off item chance | |||
'offItemChance' | |||
} | } | ||
function | local function allSkillModCriteriaFromIDs(modIDs) | ||
local modIDsWithProps = {} | |||
for i, modID in ipairs(allSkillModIDs) do | |||
table.insert(modIDsWithProps, { | |||
["id"] = modID, | |||
["type"] = 'id', | |||
["props"] = { | |||
["skillID"] = 'nil', | |||
["modType"] = 'pos' | |||
} | |||
}) | |||
end | |||
return Modifiers.getMatchCriteriaFromIDs(modIDsWithProps) | |||
end | |||
local allSkillModCriteria = allSkillModCriteriaFromIDs(allSkillModIDs) | |||
function p._getItemUses(item, asList, addCategories) | |||
-- Another fun one. This time getting all the different possible ways an item can be used | |||
local categoryArray = {} | |||
local skillUses = {} | |||
local otherUses = {} | |||
local otherUseText = { | |||
["Combat"] = Icons.Icon({'Combat'}), | |||
["Upgrade"] = '[[Upgrading Items]]', | |||
["Food"] = '[[Food]]', | |||
["Chest"] = '[[Chest|Can Be Opened]]', | |||
["Mastery"] = Icons.Icon({'Mastery'}), | |||
["AllSkills"] = 'All skills', | |||
["AltMagic"] = Icons.Icon({'Alt. Magic', type='skill'}), | |||
["ChargeStone"] = 'Powering ' .. Icons.Icon({'Crown of Rhaelyx', type='item'}), | |||
["Shop"] = Icons.Icon({'Shop'}), | |||
["TownshipTask"] = Icons.Icon({'Tasks', type='township'}) | |||
} | |||
local addUse = function(useName) | |||
local skillID = Constants.getSkillID(useName) | |||
if skillID == nil then | |||
-- May have been passed a skill ID instead | |||
local skillName = Constants.getSkillName(useName) | |||
if skillName ~= nil then | |||
skillID = useName | |||
skillUses[skillID] = skillName | |||
end | |||
else | |||
skillUses[skillID] = useName | |||
end | |||
if skillID == nil and not otherUses[useName] then | |||
otherUses[useName] = true | |||
end | |||
end | |||
local hasUse = function(useName) | |||
local skillID = Constants.getSkillID(useName) | |||
if skillID == nil and Constants.getSkillName(useName) ~= nil then | |||
skillID = useName | |||
end | |||
if skillID ~= nil then | |||
return (skillUses[skillID] ~= nil) or false | |||
else | |||
return otherUses[useName] or false | |||
end | |||
end | |||
-- Check for any overrides within itemUseArray | |||
for useName, itemList in pairs(itemUseArray) do | |||
if Shared.contains(itemList, item.id) then | |||
addUse(useName) | |||
end | |||
end | |||
-- If the item has any modifiers that affect a given skill, add it to those tables | |||
-- Added special handling for Mastery Tokens since they were being incorrectly flagged as usable for all skills | |||
if item.modifiers ~= nil then | |||
if item.modifiers.masteryToken ~= nil then | |||
-- Mastery tokens | |||
addUse('Mastery') | |||
else | |||
local modsAllSkills = Modifiers.getMatchingModifiers(item.modifiers, allSkillModCriteria) | |||
if not Shared.tableIsEmpty(modsAllSkills.matched) then | |||
addUse('AllSkills') | |||
else | |||
local skillArray = Modifiers.getModifierSkills(item.modifiers) | |||
for i, skillName in ipairs(skillArray) do | |||
addUse(skillName) | |||
end | |||
end | |||
end | |||
if item.modifiers.thievingStealth ~= nil then | |||
addUse('melvorD:Thieving') | |||
end | |||
end | |||
--First things added to the list are non-skill things that are easy to check | |||
if not hasUse('Combat') and (Items.hasCombatStats(item) or item.specialAttacks ~= nil) then | |||
addUse('Combat') | |||
end | |||
-- Check if the item is an entry requirement for any Slayer area | |||
if not hasUse('Slayer') and (item.validSlots ~= nil or item.occupiesSlots ~= nil or item.equipmentStats ~= nil) then | |||
local slayerAreas = Areas.getAreas(function(area) return area.type == 'slayerArea' and type(area.entryRequirements) == 'table' end) | |||
for i, area in ipairs(slayerAreas) do | |||
for j, req in ipairs(area.entryRequirements) do | |||
if req.type == "SlayerItem" and req.itemID == item.id then | |||
addUse('Slayer') | |||
break | |||
end | |||
end | |||
if hasUse('Slayer') then | |||
break | |||
end | |||
end | |||
end | |||
-- Is the item a cost in an upgrade? | |||
for i, upgrade in ipairs(GameData.rawData.itemUpgrades) do | |||
for j, itemCost in ipairs(upgrade.itemCosts) do | |||
if itemCost.id == item.id then | |||
addUse('Upgrade') | |||
table.insert(categoryArray, '[[Category:Upgradeable Items]]') | |||
break | |||
end | |||
end | |||
if hasUse('Upgrade') then | |||
break | |||
end | |||
end | |||
if item.healsFor ~= nil then | |||
table.insert(categoryArray, '[[Category:Food Items]]') | |||
addUse('Food') | |||
end | |||
if item.dropTable ~= nil then | |||
table.insert(categoryArray, '[[Category:Openable Items]]') | |||
addUse('Chest') | |||
end | |||
-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore | |||
-- All have somewhat consistent recipe data structures | |||
local recipeSkillIDs = { | |||
'melvorD:Cooking', | |||
'melvorD:Smithing', | |||
'melvorD:Fletching', | |||
'melvorD:Crafting', | |||
'melvorD:Runecrafting', | |||
'melvorD:Herblore' | |||
} | |||
for i, recipeSkillID in ipairs(recipeSkillIDs) do | |||
if not hasUse(recipeSkillID) then | |||
local _, localSkillID = GameData.getLocalID(recipeSkillID) | |||
-- Iterate over all recipes for the current skill | |||
for j, recipe in ipairs(SkillData[localSkillID].recipes) do | |||
for k, itemCost in ipairs(recipe.itemCosts) do | |||
if itemCost.id == item.id then | |||
addUse(recipeSkillID) | |||
break | |||
end | |||
end | |||
-- Some items (such as Arrow shafts) have multiple recipes | |||
if not hasUse(recipeSkillID) and type(recipe.alternativeCosts) == 'table' then | |||
for k, altCost in ipairs(recipe.alternativeCosts) do | |||
for m, itemCost in ipairs(altCost.itemCosts) do | |||
if itemCost.id == item.id then | |||
addUse(recipeSkillID) | |||
break | |||
end | |||
end | |||
if hasUse(recipeSkillID) then | |||
break | |||
end | |||
end | |||
end | |||
if hasUse(recipeSkillID) then | |||
break | |||
end | |||
end | |||
end | |||
end | |||
-- Firemaking | |||
if not hasUse('melvorD:Firemaking') then | |||
for i, recipe in ipairs(SkillData.Firemaking.logs) do | |||
if recipe.logID == item.id then | |||
addUse('melvorD:Firemaking') | |||
break | |||
end | |||
end | |||
end | |||
-- Farming | |||
if not hasUse('melvorD:Farming') then | |||
for i, recipe in ipairs(SkillData.Farming.recipes) do | |||
if recipe.seedCost.id == item.id then | |||
addUse('melvorD:Farming') | |||
break | |||
end | |||
end | |||
end | |||
-- Agility | |||
if not hasUse('melvorD:Agility') and not Shared.tableIsEmpty(Agility.getObstaclesForItem(item.id)) then | |||
addUse('melvorD:Agility') | |||
end | |||
-- Summoning | |||
if not hasUse('melvorD:Summoning') then | |||
for i, recipe in ipairs(SkillData.Summoning.recipes) do | |||
-- Tablets & Non-shard items | |||
if recipe.productID == item.id or Shared.contains(recipe.nonShardItemCosts, item.id) then | |||
addUse('melvorD:Summoning') | |||
break | |||
else | |||
-- Shards | |||
for j, itemCost in ipairs(recipe.itemCosts) do | |||
if itemCost.id == item.id then | |||
addUse('melvorD:Summoning') | |||
break | |||
end | |||
end | |||
end | |||
end | |||
end | |||
-- Prayer | |||
if item.prayerPoints ~= nil then | |||
table.insert(categoryArray, '[[Category:Buriable Items]]') | |||
if not hasUse('melvorD:Prayer') then | |||
addUse('melvorD:Prayer') | |||
end | |||
elseif item.soulPoints ~= nil then | |||
table.insert(categoryArray, '[[Category:Releasable Items]]') | |||
if not hasUse('melvorD:Prayer') then | |||
addUse('melvorD:Prayer') | |||
end | |||
end | |||
-- Magic | |||
if not (hasUse('Magic') and hasUse('AltMagic')) then | |||
-- First check if the item its self is used in any spells | |||
local spellList = Magic.getSpellsUsingItem(item.id, true) | |||
for i, spell in ipairs(spellList) do | |||
local useKey = (spell.type == 'altMagic' and 'AltMagic' or 'Magic') | |||
if not hasUse(useKey) then | |||
addUse(useKey) | |||
end | |||
end | |||
-- Check if the item provides runes, if it does check where they are used also | |||
if item.providedRunes ~= nil then | |||
for i, rune in ipairs(item.providedRunes) do | |||
if hasUse('Magic') and hasUse('AltMagic') then | |||
break | |||
else | |||
local spellList = Magic.getSpellsUsingItem(rune.id, false) | |||
for j, spell in ipairs(spellList) do | |||
local useKey = (spell.type == 'altMagic' and 'AltMagic' or 'Magic') | |||
if not hasUse(useKey) then | |||
addUse(useKey) | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
-- Other odds and ends: | |||
-- Skillcapes are tied to the appropriate skill | |||
-- Except Maximum Skillcape, which is tied to all skills. (And so is the Signet Ring) | |||
-- And combat skillcapes, since combat skills don't get special treatment | |||
if item.tier == 'Skillcape' then | |||
local ignoreCapes = { | |||
'melvorD:Attack_Skillcape', | |||
'melvorD:Strength_Skillcape', | |||
'melvorD:Defence_Skillcape', | |||
'melvorD:Hitpoints_Skillcape', | |||
'melvorF:Ranged_Skillcape', | |||
'melvorTotH:Superior_Attack_Skillcape', | |||
'melvorTotH:Superior_Strength_Skillcape', | |||
'melvorTotH:Superior_Defence_Skillcape', | |||
'melvorTotH:Superior_Hitpoints_Skillcape', | |||
'melvorTotH:Superior_Ranged_Skillcape', | |||
} | |||
local allCapes = { | |||
'melvorF:Max_Skillcape', | |||
'melvorF:Cape_of_Completion', | |||
'melvorTotH:Superior_Max_Skillcape', | |||
'melvorTotH:Superior_Cape_Of_Completion', | |||
} | |||
if Shared.contains(allCapes, item.id) then | |||
addUse('AllSkills') | |||
elseif Shared.contains({'melvorF:Magic_Skillcape', 'melvorTotH:Superior_Magic_Skillcape'}, item.id) then | |||
addUse('melvorD:Magic') | |||
addUse('AltMagic') | |||
elseif not Shared.contains(ignoreCapes, item.id) then | |||
local splitName = Shared.splitString(item.name, ' ') | |||
local skillName = (splitName[1] == 'Superior' and splitName[2]) or splitName[1] | |||
addUse(skillName) | |||
end | |||
table.insert(categoryArray, '[[Category:Skillcapes]]') | |||
end | |||
--Special note for Charge Stone of Rhaelyx | |||
if item.id == 'melvorD:Charge_Stone_of_Rhaelyx' then | |||
addUse('ChargeStone') | |||
end | |||
--Some items are needed to make shop purchases | |||
local shopArray = Shop.getItemCostArray(item.id) | |||
if not Shared.tableIsEmpty(shopArray) then | |||
addUse('Shop') | |||
end | |||
-- Township Tasks | |||
for _, task in ipairs(SkillData.Township.tasks) do | |||
if task.goals.items ~= nil then -- Skip tasks with no items | |||
if GameData.getEntityByID(task.goals.items, item.id) then | |||
addUse('TownshipTask') | |||
break | |||
end | |||
end | |||
end | |||
-- Generate result text | |||
local useArray = {} | |||
local prefix, delim = asList and '* ' or '', asList and '\r\n' or '<br/>' | |||
-- Always place 'All skills' use before any skills | |||
if hasUse('AllSkills') then | |||
table.insert(useArray, prefix .. (otherUseText.AllSkills or 'AllSkills')) | |||
otherUses.AllSkills = nil | |||
end | |||
for skillID, skillName in Shared.spairs(skillUses, function(t, a, b) return t[a] < t[b] end) do | |||
table.insert(useArray, prefix .. Icons.Icon({skillName, type='skill'})) | |||
end | |||
for use, _ in Shared.skpairs(otherUses) do | |||
table.insert(useArray, prefix .. (otherUseText[use] or use)) | |||
end | |||
return table.concat(useArray, delim) .. (addCategories and table.concat(categoryArray, '') or '') | |||
end | end | ||
function p.getItemUses(frame) | function p.getItemUses(frame) | ||
local itemName = frame.args ~= nil and frame.args[1] or frame | |||
local item = Items.getItem(itemName) | |||
local addCategories = false | |||
local asList = true | |||
if frame.args ~= nil then | |||
addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false' | |||
asList = frame.args.addCategories == nil or frame.args.addCategories == '' or frame.args.addCategories == 'true' | |||
end | |||
if item == nil then | |||
return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | |||
end | |||
return p._getItemUses(item, asList, addCategories) | |||
end | end | ||
function p._getItemUseTable(item) | function p._getItemUseTable(item) | ||
local useArray = {} | |||
-- Loop through all upgrades to find anything that can be upgraded using our source | |||
for i, upgrade in ipairs(GameData.rawData.itemUpgrades) do | |||
for j, itemCost in ipairs(upgrade.itemCosts) do | |||
if itemCost.id == item.id then | |||
local rowReq = nil | |||
-- Potions do have upgrade requirements though | |||
local upgradeItem = Items.getItemByID(upgrade.upgradedItemID) | |||
if upgradeItem ~= nil and upgradeItem.charges ~= nil and upgradeItem.tier ~= nil then | |||
local levelUnlock = GameData.getEntityByProperty(SkillData.Herblore.masteryLevelUnlocks, 'descriptionID', upgradeItem.tier + 1) | |||
if levelUnlock ~= nil then | |||
rowReq = Icons._MasteryReq(upgradeItem.name, levelUnlock.level) | |||
end | |||
end | |||
table.insert(useArray, {item = {id = upgradeItem.id, name = upgradeItem.name}, qty = (upgrade.quantity or 1), mats = upgrade.itemCosts, skill = 'Upgrade', req = rowReq, xp = 'N/A', gp = upgrade.gpCost, sc = upgrade.scCost}) | |||
end | |||
end | |||
end | |||
-- Cooking, Smithing, Fletching, Crafting, Runecrafting, Herblore | |||
-- All have somewhat consistent recipe data structures | |||
local recipeSkillIDs = { | |||
'melvorD:Cooking', | |||
'melvorD:Smithing', | |||
'melvorD:Fletching', | |||
'melvorD:Crafting', | |||
'melvorD:Runecrafting', | |||
'melvorD:Herblore' | |||
} | |||
for i, recipeSkillID in ipairs(recipeSkillIDs) do | |||
local skillName = Constants.getSkillName(recipeSkillID) | |||
local _, localSkillID = GameData.getLocalID(recipeSkillID) | |||
-- Iterate over all recipes for the current skill | |||
for j, recipe in ipairs(SkillData[localSkillID].recipes) do | |||
local costLists = {recipe.alternativeCosts or {}, {{["itemCosts"] = recipe.itemCosts}}} | |||
for k, costList in pairs(costLists) do | |||
for m, costDef in pairs(costList) do | |||
for n, itemCost in ipairs(costDef.itemCosts) do | |||
if itemCost.id == item.id then | |||
local recipeItemIDs = nil | |||
if recipeSkillID == 'melvorD:Herblore' then | |||
recipeItemIDs = recipe.potionIDs | |||
elseif recipeSkillID == 'melvorD:Cooking' then | |||
recipeItemIDs = {recipe.productID, recipe.perfectCookID} | |||
else | |||
recipeItemIDs = {recipe.productID} | |||
end | |||
for o, recipeItemID in ipairs(recipeItemIDs) do | |||
local recipeItem = Items.getItemByID(recipeItemID) | |||
if recipeItem ~= nil then | |||
local itemDef = {id = recipe.itemID, name = recipeItem.name} | |||
local qty = (recipe.baseQuantity or 1) * (costDef.quantityMultiplier or 1) | |||
local rowReq, isAbyssal = Skills.getRecipeLevelRealm(recipeSkillID, recipe) | |||
local reqVal = nil | |||
local xp = recipe.baseAbyssalExperience or recipe.baseExperience | |||
if recipeSkillID == 'melvorD:Herblore' then | |||
-- Herblore may also have a mastery requirement | |||
local levelUnlock = GameData.getEntityByProperty(SkillData.Herblore.masteryLevelUnlocks, 'descriptionID', recipeItem.tier + 1) | |||
if levelUnlock ~= nil and levelUnlock.level > 1 then | |||
local masteryReq = Icons._MasteryReq(recipeItem.name, levelUnlock.level) | |||
reqVal = rowReq + levelUnlock.level * 0.01 | |||
rowReq = Icons._SkillReq(skillName, rowReq, false, (isAbyssal and "melvorItA:Abyssal" or nil)) .. '<br/>' .. masteryReq | |||
end | |||
end | |||
table.insert(useArray, {item = itemDef, qty = qty, mats = costDef.itemCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = skillName, reqVal = reqVal, req = rowReq, isAbyssal = isAbyssal, xp = xp}) | |||
end | |||
end | |||
break | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
-- Farming | |||
for i, recipe in ipairs(SkillData.Farming.recipes) do | |||
if recipe.seedCost.id == item.id then | |||
local product = Items.getItemByID(recipe.productId) | |||
local mat = {{id = recipe.seedCost.id, quantity = recipe.seedCost.quantity}} | |||
local rowReq, isAbyssal = Skills.getRecipeLevelRealm('melvorD:Farming', recipe) | |||
local category = GameData.getEntityByID(SkillData.Farming.categories, recipe.categoryID) | |||
local qty = 5 * category.harvestMultiplier | |||
local xp = recipe.baseAbyssalExperience or recipe.baseExperience | |||
table.insert(useArray, {item = {id = product.id, name = product.name}, qty = qty, mats = mat, skill = 'Farming', req = rowReq, isAbyssal = isAbyssal, xp = xp}) | |||
end | |||
end | |||
-- Agility | |||
local obstacles = Agility.getObstaclesForItem(item.id) | |||
for i, obstacle in ipairs(obstacles) do | |||
local itemCosts = {} | |||
for j, itemDef in ipairs(obstacle.itemCosts) do | |||
table.insert(itemCosts, {id = itemDef.id, quantity = itemDef.quantity}) | |||
end | |||
local req = Agility._getObstacleRequirements(obstacle) | |||
--local objType = (obstacle.category == nil and 'Pillar') or 'Obstacle' | |||
table.insert(useArray, {item = {id = obstacle.id, name = obstacle.name}, qty = 1, mats = itemCosts, gp = obstacle.gpCost, sc = obstacle.scCost, skill = 'Agility', req = req, type = 'skill'}) | |||
end | |||
-- Summoning | |||
for i, recipe in ipairs(SkillData.Summoning.recipes) do | |||
local recipeCost = 0 | |||
if recipe.abyssalLevel ~= nil then | |||
recipeCost = SkillData.Summoning.recipeAPCost | |||
else | |||
recipeCost = SkillData.Summoning.recipeGPCost | |||
end | |||
local useShards = false | |||
local recipeItem = nil | |||
for j, itemCost in ipairs(recipe.itemCosts) do | |||
if itemCost.id == item.id then | |||
useShards = true | |||
break | |||
end | |||
end | |||
-- Non-shard items | |||
-- Familiar recipes may also have a currency cost without any non-shard | |||
-- items, so account for this with a dummy ID such that one iteration | |||
-- of the below loop always occurs | |||
local nonShardItemIDs = (Shared.tableIsEmpty(recipe.nonShardItemCosts) and {''} or recipe.nonShardItemCosts) | |||
for j, nonShardItemID in ipairs(nonShardItemIDs) do | |||
if useShards or nonShardItemID == item.id then | |||
-- Item is used in this particular synergy recipe | |||
if recipeItem == nil then | |||
recipeItem = Items.getItemByID(recipe.productID) | |||
end | |||
local nonShardItem = Items.getItemByID(nonShardItemID) | |||
local recipeCosts = Shared.clone(recipe.itemCosts) | |||
local xp = recipe.baseAbyssalExperience or recipe.baseExperience | |||
local req, isAbyssal = Skills.getRecipeLevelRealm('melvorD:Summoning', recipe) | |||
local recipeCosts = {} | |||
for k, itemCost in ipairs(recipe.itemCosts) do | |||
table.insert(recipeCosts, {id = itemCost.id, quantity = itemCost.quantity}) | |||
end | |||
if nonShardItem ~= nil then | |||
-- Item ID can be nil for recipes such as Leprechaun or Cyclops | |||
local itemValue = math.max(nonShardItem.sellsFor, 20) | |||
local nonShardQty = math.max(1, math.ceil(recipeCost / itemValue)) | |||
table.insert(recipeCosts, {id = nonShardItemID, quantity = nonShardQty}) | |||
end | |||
table.insert(useArray, {item = {id = recipeItem.id, name = recipeItem.name}, qty = recipe.baseQuantity, mats = recipeCosts, gp = recipe.gpCost, sc = recipe.scCost, skill = 'Summoning', req = req, isAbyssal = isAbyssal, xp = xp}) | |||
end | |||
end | |||
end | |||
--Handle shop purchases using Module:Shop | |||
local shopUses = Shop.getItemCostArray(item.id) | |||
for i, shopUse in ipairs(shopUses) do | |||
local purchase = shopUse.purchase | |||
local rowReq = Shop.getRequirementString(purchase.purchaseRequirements) | |||
local iconType = Common.getPurchaseIconType(purchase) | |||
local gpCost = Shop.getCurrencyCostString(purchase.cost, 'gp') | |||
local scCost = Shop.getCurrencyCostString(purchase.cost, 'slayerCoins') | |||
local rcCost = Shop.getCurrencyCostString(purchase.cost, 'raidCoins') | |||
table.insert(useArray, {item = {name = Shop._getPurchaseName(purchase)}, qty = 1, mats = purchase.cost.items, skill = 'Shop', req = rowReq, xp = 'N/A', gp = gpCost, sc = scCost, rc = rcCost, type = iconType}) | |||
end | |||
--Finally build the table using what we've learned | |||
table.sort(useArray, function(a, b) | |||
local aReqVal = a.reqVal ~= nil and a.reqVal or a.req | |||
local bReqVal = b.reqVal ~= nil and b.reqVal or b.req | |||
if a.skill ~= b.skill then | |||
return a.skill < b.skill | |||
elseif type(aReqVal) ~= type(bReqVal) then | |||
return tostring(aReqVal) < tostring(bReqVal) | |||
elseif aReqVal ~= bReqVal then | |||
return aReqVal < bReqVal | |||
else | |||
return a.item.name < b.item.name | |||
end | |||
end) | |||
local resultPart = {} | |||
if not Shared.tableIsEmpty(useArray) then | |||
local typeTextList = { | |||
["Shop"] = Icons.Icon({'Shop'}), | |||
["Upgrade"] = '[[Upgrading Items|Upgrade]]' | |||
} | |||
-- Header | |||
table.insert(resultPart, '{| class="wikitable stickyHeader sortable col-1-img"') | |||
table.insert(resultPart, '\r\n|- class="headerRow-0"') | |||
table.insert(resultPart, '\r\n!colspan=2|Item Created!!Type!!Requirements!!XP!!Ingredients') | |||
-- Rows | |||
for i, row in ipairs(useArray) do | |||
local qty = row.qty ~= nil and row.qty or 1 | |||
local iconType = row.type ~= nil and row.type or 'item' | |||
local iconName = row.item.name | |||
if row.skill == 'Agility' then | |||
iconType = 'agility' | |||
end | |||
local typeName = row.skill ~= nil and row.skill or '' | |||
local typeText = typeTextList[typeName] or Icons.Icon({typeName, type='skill'}) or '' | |||
local reqVal, reqText = row.reqVal, 'None' | |||
if type(row.req) == 'number' then | |||
reqVal = row.req | |||
reqText = Icons._SkillReq(typeName, row.req, false, (row.isAbyssal and "melvorItA:Abyssal" or nil)) | |||
elseif type(row.req) == 'string' then | |||
reqText = row.req | |||
end | |||
local xpVal, xpText = 0, 'N/A' | |||
if type(row.xp) == 'string' then | |||
xpText = row.xp | |||
elseif type(row.xp) == 'number' then | |||
xpVal = row.xp | |||
xpText = Num.formatnum(row.xp) .. ' ' .. Icons.Icon({typeName, type='skill', notext=true}) .. ' XP' | |||
end | |||
local matRow = {} | |||
if type(row.mats) == 'table' then | |||
for j, itemCost in ipairs(row.mats) do | |||
local matItemID = itemCost.id or itemCost[1] or -1 | |||
local matItem = Items.getItemByID(matItemID) | |||
local matQty = itemCost.quantity or itemCost[2] or 1 | |||
if matItem == nil then | |||
table.insert(matRow, Shared.printError('Failed to find item with ID "' .. itemCost.id .. '"')) | |||
elseif type(matQty) == 'number' then | |||
table.insert(matRow, Icons.Icon({matItem.name, type='item', qty=matQty})) | |||
else | |||
table.insert(matRow, Icons.Icon({matItem.name, type='item'})) | |||
end | |||
end | |||
end | |||
if row.gp ~= nil then | |||
local gpText = nil | |||
if type(row.gp) == 'number' and row.gp > 0 then | |||
gpText = Icons._Currency('melvorD:GP', row.gp) | |||
elseif type(row.gp) == 'string' then | |||
gpText = row.gp | |||
end | |||
table.insert(matRow, gpText) | |||
end | |||
if row.sc ~= nil then | |||
local scText = nil | |||
if type(row.sc) == 'number' and row.sc > 0 then | |||
scText = Icons._Currency('melvorD:SlayerCoins', row.sc) | |||
elseif type(row.sc) == 'string' then | |||
scText = row.sc | |||
end | |||
table.insert(matRow, scText) | |||
end | |||
if row.rc ~= nil then | |||
local rcText = nil | |||
if type(row.rc) == 'number' and row.rc > 0 then | |||
rcText = Icons._Currency('melvorD:RaidCoins', row.rc) | |||
elseif type(row.rc) == 'string' then | |||
rcText = row.rc | |||
end | |||
table.insert(matRow, rcText) | |||
end | |||
-- Item created | |||
table.insert(resultPart, '\r\n|-\r\n|data-sort-value="' .. row.item.name .. '"| ') | |||
table.insert(resultPart, Icons.Icon({iconName, row.item.name, type=iconType, notext=true, size=50})) | |||
table.insert(resultPart, '\r\n| ') | |||
if qty > 1 then | |||
table.insert(resultPart, "'''" .. Num.formatnum(qty) .. "x''' ") | |||
end | |||
table.insert(resultPart, Icons.Icon({iconName, row.item.name, type=iconType, noicon=true})) | |||
-- Type | |||
table.insert(resultPart, '\r\n|data-sort-value="' .. typeName .. '"| ' .. typeText) | |||
-- Requirements | |||
table.insert(resultPart, '\r\n|style="text-align:right;"') | |||
if row.reqVal ~= nil then | |||
table.insert(resultPart, ' data-sort-value="' .. reqVal .. '"') | |||
end | |||
table.insert(resultPart, '| ' .. reqText) | |||
-- XP | |||
table.insert(resultPart, '\r\n|style="text-align:right;" data-sort-value="' .. xpVal .. '"| ' .. xpText) | |||
-- Ingredients | |||
table.insert(resultPart, '\r\n| ' .. table.concat(matRow, '<br/>')) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
end | |||
local spellUseTable = p._getSpellUseTable(item) | |||
if spellUseTable ~= nil and spellUseTable ~= '' then | |||
table.insert(resultPart, '\r\n===' .. Icons.Icon({'Magic', type='skill', size=30}) .. '===\r\n' .. spellUseTable) | |||
end | |||
if Shared.tableIsEmpty(resultPart) then | |||
return '' | |||
else | |||
return '==Uses==\r\n' .. table.concat(resultPart) | |||
end | |||
end | end | ||
function p.getItemUseTable(frame) | function p.getItemUseTable(frame) | ||
local itemName = frame.args ~= nil and frame.args[1] or frame | |||
local item = Items.getItem(itemName) | |||
if item == nil then | |||
return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | |||
end | |||
return p._getItemUseTable(item) | |||
end | end | ||
function p._getSpellUseTable(item) | function p._getSpellUseTable(item) | ||
local spellList = Magic.getSpellsUsingItem(item.id, true) | |||
--Bail immediately if no spells are found | |||
if Shared.tableIsEmpty(spellList) then | |||
return '' | |||
end | |||
--Adding a check for if the Items column is needed | |||
local hasItems = false | |||
for i, spell in pairs(spellList) do | |||
if Magic._getSpellItems(spell) ~= '' then | |||
hasItems = true | |||
break | |||
end | |||
end | |||
local resultPart = {} | |||
table.insert(resultPart, '{|class="wikitable sortable"\r\n!colspan="2"|Spell') | |||
table.insert(resultPart, '!!Requirements') | |||
table.insert(resultPart, '!!Type!!style="width:275px"|Description') | |||
table.insert(resultPart, '!!Runes') | |||
if hasItems then | |||
table.insert(resultPart, '!!Item Cost') | |||
end | |||
for i, spell in pairs(spellList) do | |||
local spellBook = Magic.getSpellBookFromSpell(spell) | |||
local rowPart = {} | |||
table.insert(rowPart, '\r\n|-\r\n|data-sort-value="'..spell.name..'"|') | |||
local iconType = Magic._getSpellIconType(spell) | |||
table.insert(rowPart, Icons.Icon({spell.name, type=iconType, notext=true, size=50})) | |||
table.insert(rowPart, '||'..Icons.Icon({spell.name, type=iconType, noicon=true})) | |||
table.insert(rowPart, '||data-sort-value="'..Skills.getRecipeLevel('melvorD:Summoning', spell)..'"|'..Magic._getSpellRequirements(spell)) | |||
table.insert(rowPart, '||data-sort-value="'.. spellBook.id ..'"|') | |||
table.insert(rowPart, Magic.getSpellTypeLink(spellBook.id)) | |||
table.insert(rowPart, '||'..Magic._getSpellStat(spell, 'description')) | |||
table.insert(rowPart, '||style="text-align:center"|') | |||
table.insert(rowPart, Magic._getSpellRunes(spell)) | |||
if hasItems then | |||
table.insert(rowPart, '||style="text-align:right"|') | |||
table.insert(rowPart, Magic._getSpellItems(spell)) | |||
end | |||
table.insert(resultPart, table.concat(rowPart)) | |||
end | |||
--Add the table end and add the table to the result string | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | end | ||
function p.getSpellUseTable(frame) | function p.getSpellUseTable(frame) | ||
local itemName = frame.args ~= nil and frame.args[1] or frame | |||
local item = Items.getItem(itemName) | |||
if item == nil then | |||
return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | |||
end | |||
return p._getSpellUseTable(item) | |||
end | |||
--[==[ | |||
-- Uncomment this block and execute 'p.test()' within the debug console | |||
-- to test after making changes | |||
function p.test() | |||
local checkItems = { | |||
'Gold Bar', | |||
'Raw Shrimp', | |||
'Coal Ore', | |||
'Rune Platebody', | |||
'Arrow Shafts', | |||
'Garum Seeds', | |||
'Rune Essence', | |||
'Runite Bar', | |||
'Water Rune', | |||
'Steam Rune', | |||
'Controlled Heat Potion II', | |||
'Wolf', | |||
'Cyclops', | |||
'Leprechaun', | |||
'Redwood Logs', | |||
'Carrot Cake', | |||
'Carrot Cake (Perfect)', | |||
'Mantalyme Herb', | |||
'Carrot', | |||
'Topaz', | |||
'Rune Essence', | |||
'Infernal Claw', | |||
'Chapeau Noir', | |||
'Stardust', | |||
'Rope', | |||
'Ancient Ring of Mastery', | |||
'Mysterious Stone', | |||
'Mastery Token (Cooking)', | |||
'Gem Gloves', | |||
'Basic Bag', | |||
'Bird Nest', | |||
'Abyssal Stone', | |||
'Withered Ash', | |||
'Wrath Rune' | |||
} | |||
local checkFuncs = { | |||
p.getItemUses, | |||
p.getItemUseTable | |||
} | |||
local errCount = 0 | |||
for i, item in ipairs(checkItems) do | |||
local param = {args={item}} | |||
mw.log('=' .. item .. '=') | |||
for j, func in ipairs(checkFuncs) do | |||
local callSuccess, retVal = pcall(func, param) | |||
if not callSuccess then | |||
errCount = errCount + 1 | |||
mw.log('Error with item "' .. item .. '": ' .. retVal) | |||
else | |||
mw.log(retVal) | |||
end | |||
end | |||
end | |||
if errCount == 0 then | |||
mw.log('Test successful') | |||
else | |||
mw.log('Test failed with ' .. errCount .. ' failures') | |||
end | |||
end | end | ||
--]==] | |||
return p | return p |