|
|
(8 intermediate revisions by 2 users not shown) |
Line 1: |
Line 1: |
| local p = {}
| |
|
| |
|
|
| |
| local Constants = require('Module:Constants')
| |
| local Shared = require('Module:Shared')
| |
| local GameData = require('Module:GameData')
| |
| local SkillData = GameData.skillData
| |
| local Magic = require('Module:Magic')
| |
| local Icons = require('Module:Icons')
| |
| local Items = require('Module:Items')
| |
| local Shop = require('Module:Shop')
| |
| local Monsters = require('Module:Monsters')
| |
| local Skills = require('Module:Skills')
| |
|
| |
| function p._getCreationTable(item)
| |
| local skill = ''
| |
| local specialReq = nil
| |
| local time = 0
| |
| local maxTime = nil
| |
| local lvl = 0
| |
| local xp = 0
| |
| local qty = nil
| |
| local req = nil
| |
|
| |
| local tables = {}
| |
| local itemID = item.id
| |
| --First figure out what skill is used to make this...
| |
|
| |
| local skillIDs = {
| |
| ['Gathering'] = {
| |
| ['Woodcutting'] = { recipeKey = 'trees' },
| |
| ['Fishing'] = { recipeKey = 'fish' },
| |
| ['Mining'] = { recipeKey = 'rockData' },
| |
| ['Farming'] = { recipeKey = 'recipes' }
| |
| },
| |
| ['Artisan'] = {
| |
| ['Cooking'] = { },
| |
| ['Smithing'] = { },
| |
| ['Fletching'] = { },
| |
| ['Crafting'] = { },
| |
| ['Runecrafting'] = { },
| |
| ['Herblore'] = { },
| |
| ['Summoning'] = { }
| |
| }
| |
| }
| |
|
| |
| -- Gathering skills
| |
| -- All follow a similar data structure
| |
| for localSkillID, dataProp in pairs(skillIDs.Gathering) do
| |
| local skillData = SkillData[localSkillID]
| |
| local skill = skillData.name
| |
| local lvl, xp, qty, req, time, maxTime = 0, 0, 0, nil, 0, nil
| |
| for i, recipe in ipairs(skillData[dataProp.recipeKey]) do
| |
| if recipe.productId == itemID then
| |
| lvl = recipe.level
| |
| xp = recipe.baseExperience
| |
| qty = recipe.baseQuantity or 1
| |
| if localSkillID == 'Farming' then
| |
| req = { recipe.seedCost }
| |
| local category = GameData.getEntityByID(skillData.categories, recipe.categoryID)
| |
| qty = 5 * category.harvestMultiplier
| |
| if category.scaleXPWithQuantity then
| |
| xp = xp * qty
| |
| end
| |
| end
| |
| -- Action time
| |
| if recipe.baseMinInterval ~= nil then
| |
| time = recipe.baseMinInterval / 1000
| |
| if recipe.baseMaxInterval ~= nil then
| |
| maxTime = recipe.baseMaxInterval / 1000
| |
| end
| |
| elseif recipe.baseInterval ~= nil then
| |
| time = recipe.baseInterval /1000
| |
| elseif skillData.baseInterval ~= nil then
| |
| time = skillData.baseInterval / 1000
| |
| end
| |
| -- Special requirements
| |
| if recipe.totalMasteryRequired ~= nil then
| |
| specialReq = Icons.Icon({'Mastery', notext=true}) .. Shared.formatnum(recipe.totalMasteryRequired) .. ' total [[' .. skill .. ']] [[Mastery]]'
| |
| end
| |
| table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq))
| |
| -- Assumes item has a single source per skill
| |
| break
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- Artisan skills
| |
| -- Allow follow a similar data structure
| |
| for localSkillID, dataProp in pairs(skillIDs.Artisan) do
| |
| local skillData = SkillData[localSkillID]
| |
| local skill = skillData.name
| |
| local lvl, xp, qty, req, time, maxTime = 0, 0, 0, nil, 0, nil
| |
| for i, recipe in ipairs(skillData.recipes) do
| |
| if recipe.productID == itemID or
| |
| (localSkillID == 'Cooking' and recipe.perfectCookID == itemID) or
| |
| (localSkillID == 'Herblore' and Shared.contains(recipe.potionIDs, itemID)) then
| |
| lvl = recipe.level
| |
| xp = recipe.baseExperience
| |
| qty = recipe.baseQuantity or 1
| |
| -- Action time
| |
| if recipe.baseMinInterval ~= nil then
| |
| time = recipe.baseMinInterval / 1000
| |
| if recipe.baseMaxInterval ~= nil then
| |
| maxTime = recipe.baseMaxInterval / 1000
| |
| end
| |
| elseif recipe.baseInterval ~= nil then
| |
| time = recipe.baseInterval /1000
| |
| elseif skillData.baseInterval ~= nil then
| |
| time = skillData.baseInterval / 1000
| |
| end
| |
| -- Special requirements
| |
| -- Potions have a mastery level requirement depending on the tier
| |
| if item.charges ~= nil and item.tier ~= nil then
| |
| local levelUnlock = GameData.getEntityByProperty(skillData.masteryLevelUnlocks, 'descriptionID', item.tier + 1)
| |
| if levelUnlock ~= nil then
| |
| specialReq = Icons._MasteryReq(item.name, levelUnlock.level, false)
| |
| end
| |
| end
| |
| -- Materials & output quantity
| |
| -- Special case for Summoning recipes
| |
| if localSkillID == 'Summoning' then
| |
| local shardCostArray, otherCostArray = {}, {}
| |
| local recipeGPCost = skillData.recipeGPCost
| |
| -- Shards
| |
| for j, itemCost in ipairs(recipe.itemCosts) do
| |
| local shard = Items.getItemByID(itemCost.id)
| |
| if shard ~= nil then
| |
| table.insert(shardCostArray, Icons.Icon({shard.name, type='item', notext=true, qty=itemCost.quantity}))
| |
| end
| |
| end
| |
| -- Other costs
| |
| if recipe.gpCost > 0 then
| |
| table.insert(otherCostArray, Icons.GP(recipe.gpCost))
| |
| end
| |
| if recipe.scCost > 0 then
| |
| table.insert(otherCostArray, Icons.SC(recipe.scCost))
| |
| end
| |
| for j, nonShardID in ipairs(recipe.nonShardItemCosts) do
| |
| local nonShard = Items.getItemByID(nonShardID)
| |
| if nonShard ~= nil then
| |
| local itemValue = math.max(nonShard.sellsFor, 20)
| |
| local nonShardQty = math.max(1, math.floor(recipeGPCost / itemValue))
| |
| table.insert(otherCostArray, Icons.Icon({nonShard.name, type='item', notext=true, qty=nonShardQty}))
| |
| end
| |
| end
| |
| req = table.concat(shardCostArray, ', ')
| |
| if not Shared.tableIsEmpty(otherCostArray) then
| |
| local costLen = Shared.tableCount(otherCostArray)
| |
| req = req .. '<br/>' .. (costLen == 1 and '' or 'and one of the following:<br/>') .. table.concat(otherCostArray, "<br/>'''OR''' ")
| |
| end
| |
| table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
| |
| -- Some items (such as Arrow shafts) have multiple recipes
| |
| elseif type(recipe.alternativeCosts) == 'table' then
| |
| local reqPart, qtyPart = {}, {}
| |
| for j, altCost in ipairs(recipe.alternativeCosts) do
| |
| local reqSubPart = {}
| |
| for k, itemCost in ipairs(altCost.itemCosts) do
| |
| local reqItem = Items.getItemByID(itemCost.id)
| |
| if reqItem == nil then
| |
| table.insert(reqSubPart, itemCost.quantity .. 'x ?????')
| |
| else
| |
| table.insert(reqSubPart, Icons.Icon({reqItem.name, type='item', qty=itemCost.quantity}))
| |
| end
| |
| end
| |
| if recipe.gpCost ~= nil and recipe.gpCost > 0 then
| |
| table.insert(reqSubPart, Icons.GP(recipe.GPCost))
| |
| end
| |
| if recipe.scCost ~= nil and recipe.scCost > 0 then
| |
| table.insert(reqSubPart, Icons.SC(recipe.SCCost))
| |
| end
| |
| table.insert(reqPart, table.concat(reqSubPart, ', '))
| |
| table.insert(qtyPart, Shared.formatnum(qty * altCost.quantityMultiplier))
| |
| end
| |
| local sep = "<br/>'''OR''' "
| |
| req = table.concat(reqPart, sep)
| |
| local qtyText = table.concat(qtyPart, sep)
| |
| table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qtyText, time, maxTime, specialReq))
| |
| -- Finally, normal recipes with a single set of item costs
| |
| elseif type(recipe.itemCosts) == 'table' and not Shared.tableIsEmpty(recipe.itemCosts) then
| |
| table.insert(tables, p.buildCreationTable(skill, lvl, xp, recipe.itemCosts, qty, time, maxTime, specialReq, recipe.gpCost, recipe.scCost))
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- Alt. Magic, excludes spells which can produce a variety of items, such as Gems and Bars
| |
| -- Bars are handled by getItemSuperheatTable()
| |
| -- Gems are handled by _getItemLootSourceTable()
| |
| for i, altSpell in ipairs(Magic.getSpellsBySpellBook('altMagic')) do
| |
| if altSpell.produces == item.id then
| |
| table.insert(tables, p._buildAltMagicTable(altSpell))
| |
| end
| |
| end
| |
|
| |
| if Shared.tableIsEmpty(tables) then
| |
| return ''
| |
| else
| |
| return table.concat(tables, '\r\n')
| |
| end
| |
| end
| |
|
| |
| function p.getAltMagicTable(frame)
| |
| local spellName = frame.args ~= nil and frame.args[1] or frame
| |
| local spell = Magic.getSpell(spellName, 'altMagic')
| |
| if spell == nil then
| |
| return Shared.printError('Could not find Alt. Magic spell "' .. spellName .. '"')
| |
| else
| |
| return p._buildAltMagicTable(spell)
| |
| end
| |
| end
| |
|
| |
| function p._buildAltMagicTable(spell)
| |
| local resultPart = {}
| |
| local imgType = Magic._getSpellIconType(spell)
| |
| table.insert(resultPart, '{|class="wikitable"\r\n|-')
| |
| table.insert(resultPart, '\r\n!colspan="2"|'..Icons.Icon({spell.name, type=imgType}))
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Requirements')
| |
| table.insert(resultPart, '\r\n|'..Icons._SkillReq('Magic', spell.level))
| |
|
| |
| local costText = Magic._getAltSpellCostText(spell)
| |
| if costText ~= nil then
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials')
| |
| table.insert(resultPart, '\r\n| ' .. costText)
| |
| end
| |
|
| |
| --Add runes
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Runes\r\n| ' .. Magic._getSpellRunes(spell))
| |
|
| |
| --Now just need the output quantity, xp, and casting time (which is always 2)
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Quantity\r\n|' .. spell.productionRatio)
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base XP\r\n|' .. spell.baseExperience)
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Cast Time\r\n|2s')
| |
| table.insert(resultPart, '\r\n|}')
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq, gpCost, scCost)
| |
| if qty == nil then qty = 1 end
| |
| local resultPart = {}
| |
| table.insert(resultPart, '{|class="wikitable"')
| |
| table.insert(resultPart, '\r\n!colspan="2"|Item ' .. (req == nil and 'Creation' or 'Production'))
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align: right;"|Requirements')
| |
| table.insert(resultPart, '\r\n|'..Icons._SkillReq(skill, lvl))
| |
| if specialReq ~= nil then table.insert(resultPart, '<br/>'..specialReq) end
| |
|
| |
| if req ~= nil then
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align: right;"|Materials\r\n|')
| |
| if type(req) == 'table' then
| |
| for i, mat in ipairs(req) do
| |
| if i > 1 then table.insert(resultPart, '<br/>') end
| |
| local matItem = Items.getItemByID(mat.id)
| |
| if matItem == nil then
| |
| table.insert(resultPart, mat.quantity..'x ?????')
| |
| else
| |
| table.insert(resultPart, Icons.Icon({matItem.name, type='item', qty=mat.quantity}))
| |
| end
| |
| end
| |
| if gpCost ~= nil and gpCost > 0 then
| |
| table.insert(resultPart, '<br/>')
| |
| table.insert(resultPart, Icons.GP(gpCost))
| |
| end
| |
| if scCost ~= nil and scCost > 0 then
| |
| table.insert(resultPart, '<br/>')
| |
| table.insert(resultPart, Icons.SC(scCost))
| |
| end
| |
| else
| |
| table.insert(resultPart, req)
| |
| end
| |
| end
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Quantity')
| |
| table.insert(resultPart, '\r\n|'..qty)
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Experience')
| |
| table.insert(resultPart, '\r\n|'..Shared.formatnum(xp)..' XP')
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Creation Time')
| |
| table.insert(resultPart, '\r\n|'..Shared.timeString(time, true))
| |
| if maxTime ~= nil and maxTime > time then table.insert(resultPart, ' - '..Shared.timeString(maxTime, true)) end
| |
| table.insert(resultPart, '\r\n|}')
| |
|
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.getCreationTable(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._getCreationTable(item)
| |
| end
| |
|
| |
| function p._getItemSources(item, asList, addCategories)
| |
| local lineArray = {}
| |
| local categoryArray = {}
| |
|
| |
| --Alright, time to go through all the ways you can get an item...
| |
| --First up: Can we kill somebody and take theirs?
| |
| local killStrPart = {}
| |
| for i, monster in ipairs(GameData.rawData.monsters) do
| |
| local isDrop = false
| |
| if monster.bones ~= nil and monster.bones.itemID == item.id and Monsters._getMonsterBones(monster) ~= nil then
| |
| -- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table
| |
| isDrop = true
| |
| elseif monster.lootTable ~= nil then
| |
| -- If the monster has a loot table, check if the item we are looking for is in there
| |
| -- Dungeon exclusive monsters don't count as they are either:
| |
| -- - A monster before the boss, which doesn't drop anything except shards (checked above)
| |
| -- - A boss monster, whose drops are accounted for in data from Areas instead
| |
| for j, loot in ipairs(monster.lootTable) do
| |
| if loot.itemID == item.id and not Monsters._isDungeonOnlyMonster(monster) then
| |
| isDrop = true
| |
| break
| |
| end
| |
| end
| |
| end
| |
| if isDrop then
| |
| -- Item drops when the monster is killed
| |
| table.insert(killStrPart, Icons.Icon({monster.name, type='monster', notext=true}))
| |
| end
| |
| end
| |
| -- Is the item dropped from any dungeon?
| |
| local dungeonStrPart = {}
| |
| for i, dungeon in ipairs(GameData.rawData.dungeons) do
| |
| if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or
| |
| (type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then
| |
| table.insert(dungeonStrPart, Icons.Icon({dungeon.name, type='dungeon', notext=true}))
| |
| elseif dungeon.eventID ~= nil then
| |
| -- Is the item dropped from a combat event (e.g. Impending Darkness event)?
| |
| local event = GameData.getEntityByID('combatEvents', dungeon.eventID)
| |
| if type(event) == 'table' and type(event.itemRewardIDs) == 'table' then
| |
| for eventCycle, itemRewardID in ipairs(event.itemRewardIDs) do
| |
| if item.id == itemRewardID then
| |
| local dungPrefix = (eventCycle == Shared.tableCount(event.itemRewardIDs) and '' or eventCycle .. (eventCycle == 1 and ' cycle' or ' cycles') .. ' of ')
| |
| table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({dungeon.name, type='dungeon', notext=true}))
| |
| break
| |
| end
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| if not Shared.tableIsEmpty(dungeonStrPart) then
| |
| table.insert(lineArray, 'Completing: ' .. table.concat(dungeonStrPart, ','))
| |
| end
| |
| if not Shared.tableIsEmpty(killStrPart) then
| |
| table.insert(lineArray, 'Killing: ' .. table.concat(killStrPart, ','))
| |
| end
| |
|
| |
| -- Can we find it in an openable item?
| |
| local lootPart = {}
| |
| for i, item2 in ipairs(GameData.rawData.items) do
| |
| if item2.dropTable ~= nil then
| |
| for j, loot in ipairs(item2.dropTable) do
| |
| if loot.itemID == item.id then
| |
| table.insert(lootPart, Icons.Icon({item2.name, type='item', notext=true}))
| |
| break
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| if not Shared.tableIsEmpty(lootPart) then
| |
| table.insert(lineArray, 'Opening: ' .. table.concat(lootPart, ','))
| |
| end
| |
|
| |
| -- Is the item a result of upgrading/downgrading another item?
| |
| local upgradePart = { up = {}, down = {} }
| |
| for i, upgrade in ipairs(GameData.rawData.itemUpgrades) do
| |
| if item.id == upgrade.upgradedItemID then
| |
| local key = (upgrade.isDowngrade and 'down' or 'up')
| |
| for j, rootItemID in ipairs(upgrade.rootItemIDs) do
| |
| local rootItem = Items.getItemByID(rootItemID)
| |
| if rootItem ~= nil then
| |
| table.insert(upgradePart[key], Icons.Icon({rootItem.name, type='item', notext=true}))
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| local upgradeCat = false
| |
| for catName, parts in pairs(upgradePart) do
| |
| if not Shared.tableIsEmpty(parts) then
| |
| if not upgradeCat then
| |
| table.insert(categoryArray, '[[Category:Upgraded Items]]')
| |
| upgradeCat = true
| |
| end
| |
| local typeText = (catName == 'up' and 'Upgrading') or 'Downgrading'
| |
| table.insert(lineArray, typeText .. ': ' .. table.concat(parts, ','))
| |
| end
| |
| end
| |
|
| |
| --Next: Can we take it from somebody else -without- killing them?
| |
| local thiefItems = Skills.getThievingSourcesForItem(item.id)
| |
| if type(thiefItems) == 'table' then
| |
| local includedNPCs = {}
| |
| local thiefPart = {}
| |
| for i, thiefRow in ipairs(thiefItems) do
| |
| if thiefRow.npc == 'all' then
| |
| --if 'all' is the npc, this is a rare item so just say 'Thieving level 1'
| |
| table.insert(lineArray, Icons._SkillReq('Thieving', 1))
| |
| elseif not Shared.contains(includedNPCs, thiefRow.npc) then
| |
| table.insert(thiefPart, Icons.Icon({thiefRow.npc, type='thieving', notext=true}))
| |
| table.insert(includedNPCs, thiefRow.npc)
| |
| end
| |
| end
| |
| if not Shared.tableIsEmpty(thiefPart) then
| |
| table.insert(lineArray, 'Pickpocketing: ' .. table.concat(thiefPart, ','))
| |
| end
| |
| end
| |
|
| |
| -- Can we get this item by casting an Alt. Magic spell?
| |
| local castPart = {}
| |
| for i, spell in ipairs(Magic.getSpellsProducingItem(item.id)) do
| |
| table.insert(castPart, Icons.Icon({spell.name, type=Magic._getSpellIconType(spell), notext=true}))
| |
| end
| |
| if not Shared.tableIsEmpty(castPart) then
| |
| table.insert(lineArray, 'Casting: ' .. table.concat(castPart, ','))
| |
| end
| |
|
| |
| --Check if we can make it ourselves
| |
| local skillIDs = {
| |
| ['Gathering'] = {
| |
| ['Woodcutting'] = { recipeKey = 'trees' },
| |
| ['Fishing'] = { recipeKey = 'fish' },
| |
| ['Mining'] = { recipeKey = 'rockData' },
| |
| ['Farming'] = { recipeKey = 'recipes' }
| |
| },
| |
| ['Artisan'] = {
| |
| ['Cooking'] = { },
| |
| ['Smithing'] = { },
| |
| ['Fletching'] = { },
| |
| ['Crafting'] = { },
| |
| ['Runecrafting'] = { },
| |
| ['Herblore'] = { },
| |
| ['Summoning'] = { }
| |
| }
| |
| }
| |
|
| |
| -- Gathering skills
| |
| for localSkillID, dataProp in pairs(skillIDs.Gathering) do
| |
| local skillData = SkillData[localSkillID]
| |
| local skill = skillData.name
| |
| for i, recipe in ipairs(skillData[dataProp.recipeKey]) do
| |
| if recipe.productId == item.id then
| |
| if localSkillID == 'Farming' and recipe.seedCost ~= nil then
| |
| local seedItem = Items.getItemByID(recipe.seedCost.id)
| |
| if seedItem ~= nil then
| |
| table.insert(lineArray, 'Growing: ' .. Icons.Icon({seedItem.name, type='item', notext='true'}))
| |
| end
| |
| else
| |
| table.insert(lineArray, Icons._SkillReq(skill, recipe.level))
| |
| end
| |
| break
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- Artisan skills
| |
| for localSkillID, dataProp in pairs(skillIDs.Artisan) do
| |
| local skillData = SkillData[localSkillID]
| |
| local skill = skillData.name
| |
| for i, recipe in ipairs(skillData.recipes) do
| |
| if recipe.productID == item.id or
| |
| (localSkillID == 'Cooking' and recipe.perfectCookID == item.id) or
| |
| (localSkillID == 'Herblore' and Shared.contains(recipe.potionIDs, item.id)) then
| |
| table.insert(lineArray, Icons._SkillReq(skill, recipe.level))
| |
| break
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- Township trading
| |
| for i, tsResource in ipairs(SkillData.Township.itemConversions.fromTownship) do
| |
| local found = false
| |
| for j, tradeDef in ipairs(tsResource.items) do
| |
| if tradeDef.itemID == item.id then
| |
| found = true
| |
| local levelReq = nil
| |
| if tradeDef.unlockRequirements ~= nil then
| |
| for k, req in ipairs(tradeDef.unlockRequirements) do
| |
| if req.type == 'SkillLevel' and req.skillID == 'melvorD:Township' then
| |
| levelReq = req.level
| |
| break
| |
| end
| |
| end
| |
| if levelReq == nil then
| |
| table.insert(lineArray, Icons.Icon({SkillData.Township.name, type='skill'}))
| |
| else
| |
| table.insert(lineArray, Icons._SkillReq(SkillData.Township.name, levelReq))
| |
| end
| |
| end
| |
| end
| |
| if found then
| |
| break
| |
| end
| |
| end
| |
| if found then
| |
| break
| |
| end
| |
| end
| |
|
| |
| --AstrologyCheck (Just a brute force for now because only two items)
| |
| if Shared.contains({SkillData.Astrology.stardustItemID, SkillData.Astrology.goldenStardustItemID}, item.id) then
| |
| table.insert(lineArray, Icons.Icon({SkillData.Astrology.name, type='skill'}))
| |
| end
| |
|
| |
| -- Woodcutting
| |
| -- Raven Nest
| |
| if item.id == SkillData.Woodcutting.ravenNestItemID then
| |
| local levelReq = nil
| |
| for i, tree in ipairs(SkillData.Woodcutting.trees) do
| |
| if tree.canDropRavenNest and (levelReq == nil or tree.level < levelReq) then
| |
| levelReq = tree.level
| |
| end
| |
| end
| |
| table.insert(lineArray, Icons._SkillReq(SkillData.Woodcutting.name, levelReq))
| |
| -- Bird Nest, Ash, and Mushroom
| |
| elseif Shared.contains({
| |
| SkillData.Woodcutting.nestItemID,
| |
| SkillData.Woodcutting.ashItemID,
| |
| SkillData.Woodcutting.mushroomItemID
| |
| }, item.id) then
| |
| table.insert(lineArray, Icons._SkillReq(SkillData.Woodcutting.name, 1))
| |
| end
| |
|
| |
| -- Fishing
| |
| -- Junk
| |
| if Shared.contains(SkillData.Fishing.junkItemIDs, item.id) then
| |
| table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Junk|Junk]]')
| |
| -- Specials
| |
| elseif GameData.getEntityByProperty(SkillData.Fishing.specialItems, 'itemID', item.id) ~= nil then
| |
| table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Special|Special]]')
| |
| elseif item.id == SkillData.Fishing.lostChestItem then
| |
| table.insert(lineArray, Icons._SkillReq(SkillData.Fishing.name, 100))
| |
| end
| |
|
| |
| -- Firemaking: Coal
| |
| if Shared.contains({SkillData.Firemaking.coalItemID,
| |
| SkillData.Firemaking.ashItemID,
| |
| SkillData.Firemaking.charcoalItemID,
| |
| SkillData.Firemaking.fireSpiritItemID,
| |
| SkillData.Firemaking.diamondItemID
| |
| }, item.id) then
| |
| table.insert(lineArray, Icons._SkillReq(SkillData.Firemaking.name, 1))
| |
| end
| |
|
| |
| -- Mining: Gems
| |
| if (GameData.getEntityByProperty('randomGems', 'itemID', item.id) ~= nil or
| |
| GameData.getEntityByProperty('randomSuperiorGems', 'itemID', item.id) ~= nil) then
| |
| table.insert(lineArray, Icons.Icon({"Mining", type='skill', notext=true})..' [[Mining#Gems|Gem]]')
| |
| elseif item.id == SkillData.Mining.runestoneItemID then
| |
| -- From pure essence mining
| |
| local recipe = GameData.getEntityByID(SkillData.Mining.rockData, 'melvorTotH:Pure_Essence')
| |
| if recipe ~= nil then
| |
| table.insert(lineArray, Icons._SkillReq(SkillData.Mining.name, recipe.level))
| |
| end
| |
| end
| |
|
| |
| --Finally there are some weird exceptions:
| |
| --Rhaelyx pieces
| |
| if Shared.contains({'Circlet of Rhaelyx', 'Jewel of Rhaelyx', 'Mysterious Stone'}, item.name) then
| |
| local rhaSkills = {
| |
| Circlet = {'Woodcutting', 'Fishing', 'Mining', 'Thieving', 'Farming', 'Agility', 'Astrology'},
| |
| Jewel = {'Firemaking', 'Cooking', 'Smithing', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Summoning'}
| |
| }
| |
| local rhaSkList, subText = nil, ''
| |
| if item.name == 'Circlet of Rhaelyx' then
| |
| rhaSkList = {rhaSkills.Circlet}
| |
| elseif item.name == 'Jewel of Rhaelyx' then
| |
| rhaSkList = {rhaSkills.Jewel}
| |
| elseif item.name == 'Mysterious Stone' then
| |
| rhaSkList = {rhaSkills.Jewel, rhaSkills.Circlet}
| |
| subText = '<br/>after finding ' .. Icons.Icon({'Crown of Rhaelyx', type='item'})
| |
| end
| |
|
| |
| local rhaStrPart = {}
| |
| for i, skillList in ipairs(rhaSkList) do
| |
| for j, skillName in ipairs(skillList) do
| |
| table.insert(rhaStrPart, Icons.Icon({skillName, type='skill', notext=true}))
| |
| end
| |
| end
| |
| table.insert(lineArray, 'Any action in: ' .. table.concat(rhaStrPart, ', ') .. subText)
| |
| end
| |
|
| |
| --Tokens are from the appropriate skill
| |
| if item.modifiers ~= nil and item.modifiers.masteryToken ~= nil then
| |
| for localSkillID, skillData in pairs(SkillData) do
| |
| if skillData.masteryTokenID ~= nil and skillData.masteryTokenID == item.id then
| |
| table.insert(lineArray, Icons._SkillReq(skillData.name, 1))
| |
| break
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- Golbin Raid exclusive items
| |
| if item.golbinRaidExclusive then
| |
| table.insert(lineArray, Icons.Icon({'Golbin Raid', type='pet', img='Golden Golbin'}))
| |
| end
| |
|
| |
| --Shop items (including special items like gloves that aren't otherwise listed)
| |
| if not Shared.tableIsEmpty(Shop.getItemSourceArray(item.id)) then
| |
| table.insert(lineArray, Icons.Icon({'Shop'}))
| |
| end
| |
|
| |
| --Easter Eggs (manual list 'cause don't have a better way to do that)
| |
| if Shared.contains(Items.EasterEggs, item.name) then
| |
| table.insert(lineArray, '[[Easter Eggs]]')
| |
| end
| |
| -- Event exclusive items (also a manual list)
| |
| if Shared.contains(Items.EventItems, item.name) then
| |
| table.insert(lineArray, '[[Events]]')
| |
| end
| |
|
| |
| --Gold Topaz Ring drops from any action (when not wearing a Gold Topaz Ring)
| |
| --Also handling Signet Ring things here
| |
| if item.id == 'melvorD:Gold_Topaz_Ring' then
| |
| table.insert(lineArray, 'Any non-combat action if not worn (Instead of '..Icons.Icon({"Signet Ring Half (a)", type="item"})..')')
| |
| table.insert(lineArray, 'Killing any monster if not worn (Instead of '..Icons.Icon({"Signet Ring Half (b)", type="item"})..')')
| |
| elseif item.id == 'melvorD:Signet_Ring_Half_A' then
| |
| table.insert(lineArray, 'Any non-combat action while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
| |
| elseif item.id == 'melvorD:Signet_Ring_Half_B' then
| |
| table.insert(lineArray, 'Killing any monster while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
| |
| elseif item.id == 'melvorTotH:Deadly_Toxins_Potion' then
| |
| --Adding a special override for Deadly Toxins potions
| |
| table.insert(lineArray, 'Brewing [[Lethal Toxins Potion]]s while wearing '..Icons.Icon({'Toxic Maker Gloves', type='item'}))
| |
| end
| |
|
| |
| -- Township Task reward
| |
| for _, task in ipairs(SkillData.Township.tasks) do
| |
| if task.rewards.items[1] ~= nil then -- Skip tasks with no items
| |
| if GameData.getEntityByID(task.rewards.items, item.id) then
| |
| table.insert(lineArray, Icons.Icon({'Tasks', type='township'}))
| |
| break
| |
| end
| |
| end
| |
| end
| |
|
| |
| local resultPart = {}
| |
| if asList then
| |
| table.insert(resultPart, '* '..table.concat(lineArray, "\r\n* "))
| |
| else
| |
| table.insert(resultPart, '<div style="max-width:180px;text-align:right">' .. table.concat(lineArray, "<br/>") .. '</div>')
| |
| end
| |
| if addCategories then table.insert(resultPart, table.concat(categoryArray, '')) end
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.getItemSources(frame)
| |
| local itemName = frame.args ~= nil and frame.args[1] or frame
| |
| local item = Items.getItem(itemName)
| |
| local asList = false
| |
| local addCategories = false
| |
| if frame.args ~= nil then
| |
| asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false'
| |
| addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false'
| |
| end
| |
| if item == nil then
| |
| return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
| |
| end
| |
|
| |
| return p._getItemSources(item, asList, addCategories)
| |
| end
| |
|
| |
| function p._getItemLootSourceTable(item)
| |
| local resultPart = {}
| |
| table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
| |
| table.insert(resultPart, '\r\n|- class="headerRow-0"')
| |
| table.insert(resultPart, '\r\n!Source!!Source Type!!Quantity!!colspan="2"|Chance')
| |
|
| |
| --Set up function for adding rows
| |
| local buildRow = function(source, type, minqty, qty, weight, totalWeight, expIcon)
| |
| if minqty == nil then minqty = 1 end
| |
| if expIcon == nil then expIcon = '' end
| |
| local rowPart = {}
| |
| table.insert(rowPart, '\r\n|-')
| |
| table.insert(rowPart, '\r\n|style="text-align: left;"|'..source)
| |
| --Weeding out brackets since they don't play nice with data-sort-value
| |
| local _, _, typeText = string.find(type, "%|([%a%s]+)%]")
| |
| if typeText == nil then _, _, typeText = string.find(type, "%[%[([%a%s]+)%]") end
| |
| if typeText == nil then typeText = type end
| |
| table.insert(rowPart, '\r\n|style="text-align: left;" data-sort-value="'..typeText..'"|'..expIcon..type)
| |
|
| |
| table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="'..qty..'"|'..Shared.formatnum(minqty))
| |
| if qty ~= minqty then table.insert(rowPart, ' - '..Shared.formatnum(qty)) end
| |
| local chance = weight / totalWeight * 100
| |
| -- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
| |
| local fmt = (chance < 0.10 and '%.2g') or '%.2f'
| |
| local chanceStr = string.format(fmt, chance)
| |
| if weight >= totalWeight then
| |
| -- Fraction would be 1/1, so only show the percentage
| |
| chanceStr = '100'
| |
| table.insert(rowPart, '\r\n|colspan="2" ')
| |
| else
| |
| local fraction = Shared.fraction(weight, totalWeight)
| |
| if Shared.contains(fraction, '%.') then
| |
| --If fraction contains decimals, something screwy happened so just show only percentage
| |
| --(happens sometimes with the rare thieving items)
| |
| table.insert(rowPart, '\r\n|colspan="2" ')
| |
| else
| |
| table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="' .. chanceStr .. '"| ' .. Shared.fraction(weight, totalWeight) .. '\r\n|')
| |
| end
| |
| end
| |
| if weight == -1 then
| |
| --Weight of -1 means this is a weird row that has a variable percentage
| |
| table.insert(rowPart, 'style="text-align: right;" data-sort-value="0"|Varies (see Thieving page)')
| |
| else
| |
| table.insert(rowPart, 'style="text-align: right;" data-sort-value="'.. chanceStr .. '"|'..chanceStr..'%')
| |
| end
| |
| return table.concat(rowPart)
| |
| end
| |
| local dropRows = {}
| |
|
| |
| --Alright, time to go through a few ways to get the item
| |
| --First up: Can we kill somebody and take theirs?
| |
| for i, drop in ipairs(p._getItemMonsterSources(item)) do
| |
| local monster = GameData.getEntityByID('monsters', drop.id)
| |
| if monster ~= nil then
| |
| table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = drop.minQty, qty = drop.maxQty, weight = drop.dropWt, totalWeight = drop.totalWt, expIcon = Icons.getExpansionIcon(drop.id)})
| |
| end
| |
| end
| |
|
| |
| -- Is the item dropped from any dungeon?
| |
| for i, dungeon in ipairs(GameData.rawData.dungeons) do
| |
| if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or
| |
| (type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then
| |
| table.insert(dropRows, {source = Icons.Icon({dungeon.name, type='dungeon'}), type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1, expIcon = Icons.getExpansionIcon(dungeon.id)})
| |
| elseif dungeon.eventID ~= nil then
| |
| -- Is the item dropped from a combat event (e.g. Impending Darkness event)?
| |
| local event = GameData.getEntityByID('combatEvents', dungeon.eventID)
| |
| if type(event) == 'table' and type(event.itemRewardIDs) == 'table' then
| |
| for eventCycle, itemRewardID in ipairs(event.itemRewardIDs) do
| |
| if item.id == itemRewardID then
| |
| local sourceTxt = Icons.Icon({dungeon.name, type='dungeon'}) .. (eventCycle == Shared.tableCount(event.itemRewardIDs) and '' or ', Cycle ' .. eventCycle)
| |
| table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
| |
| break
| |
| end
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- Can we find it in an openable item?
| |
| for i, item2 in ipairs(GameData.rawData.items) do
| |
| if item2.dropTable ~= nil then
| |
| local minQty, maxQty, wt, totalWt = 1, 1, 0, 0
| |
| for j, loot in ipairs(item2.dropTable) do
| |
| totalWt = totalWt + loot.weight
| |
| if loot.itemID == item.id then
| |
| wt = loot.weight
| |
| minQty = loot.minQuantity
| |
| maxQty = loot.maxQuantity
| |
| end
| |
| end
| |
|
| |
| if wt > 0 then
| |
| local sourceTxt = Icons.Icon({item2.name, type='item'})
| |
| table.insert(dropRows, {source = sourceTxt, type = '[[Chest]]', minqty = minQty, qty = maxQty, weight = wt, totalWeight = totalWt, expIcon = Icons.getExpansionIcon(item2.id)})
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- Can it be obtained from Thieving?
| |
| local thiefItems = Skills.getThievingSourcesForItem(item.id)
| |
| for i, thiefRow in ipairs(thiefItems) do
| |
| local sourceTxt = ''
| |
| if thiefRow.npc == 'all' then
| |
| sourceTxt = 'Thieving Rare Drop'
| |
| else
| |
| sourceTxt = Icons.Icon({thiefRow.npc, type='thieving'})
| |
| end
| |
| table.insert(dropRows, {source = sourceTxt, type = Icons.Icon({SkillData.Thieving.name, type='skill'}), minqty = thiefRow.minQty, qty = thiefRow.maxQty, weight = thiefRow.wt, totalWeight = thiefRow.totalWt, expIcon = Icons.getExpansionIcon(thiefRow.npcID)})
| |
| end
| |
|
| |
| -- Fishing: Junk & Specials
| |
| if Shared.contains(SkillData.Fishing.junkItems, item.id) then
| |
| local fishSource = '[[Fishing#Junk|Junk]]'
| |
| local fishType = Icons.Icon({'Fishing', type='skill'})
| |
| local fishTotWeight = Shared.tableCount(SkillData.Fishing.JunkItems)
| |
| table.insert(dropRows, {source = fishSource, type = fishType, minqty = 1, qty = 1, weight = 1, totalWeight = fishTotWeight})
| |
| else
| |
| local fishTotWeight, fishItem = 0, nil
| |
| for i, specialItem in ipairs(SkillData.Fishing.specialItems) do
| |
| if specialItem.itemID == item.id then
| |
| fishItem = specialItem
| |
| end
| |
| fishTotWeight = fishTotWeight + specialItem.weight
| |
| end
| |
| if fishItem ~= nil then
| |
| local fishSource = '[[Fishing#Special|Special]]'
| |
| local fishType = Icons.Icon({SkillData.Fishing.name, type='skill'})
| |
| table.insert(dropRows, {source = fishSource, type = fishType, minqty = fishItem.minQuantity, qty = fishItem.maxQuantity, weight = fishItem.weight, totalWeight = fishTotWeight})
| |
| end
| |
| end
| |
|
| |
| -- Mining: Gems, and also Alt. Magic spells producing random gems
| |
| if Shared.contains({'Gem', 'Superior Gem'}, item.type) then
| |
| local gemKeys = { 'randomGems', 'randomSuperiorGems' }
| |
| for i, gemKey in ipairs(gemKeys) do
| |
| local thisGem, totalGemWeight = nil, 0
| |
| for j, gem in ipairs(GameData.rawData[gemKey]) do
| |
| totalGemWeight = totalGemWeight + gem.weight
| |
| if gem.itemID == item.id then
| |
| thisGem = gem
| |
| end
| |
| end
| |
| if thisGem ~= nil then
| |
| local mineType = Icons.Icon({SkillData.Mining.name, type='skill'})
| |
| local expIcon = ''
| |
| local sourceTxt
| |
| if item.type == 'Superior Gem' then
| |
| expIcon = Icons.TotH()
| |
| sourceTxt = '[[Mining#Superior Gems|Superior Gem]]'
| |
| else
| |
| sourceTxt = '[[Mining#Gems|Gem]]'
| |
| end
| |
| table.insert(dropRows, {source = sourceTxt, type = mineType, minqty = thisGem.minQuantity, qty = thisGem.maxQuantity, weight = thisGem.weight, totalWeight = totalGemWeight, expIcon = expIcon})
| |
|
| |
| -- Check for Alt. Magic spells also
| |
| local magicType = Icons.Icon({SkillData.Magic.name, type = 'skill'})
| |
| local producesKey = (gemKey == 'randomGems' and 'RandomGem') or 'RandomSuperiorGem'
| |
| for j, spell in ipairs(Magic.getSpellsBySpellBook('altMagic')) do
| |
| if spell.produces ~= nil and spell.produces == producesKey then
| |
| table.insert(dropRows, {source = Icons.Icon({spell.name, type=Magic._getSpellIconType(spell)}), type = magicType, minqty = thisGem.minQuantity, qty = thisGem.maxQuantity, weight = thisGem.weight, totalWeight = totalGemWeight, expIcon = Icons.getExpansionIcon(spell.id)})
| |
| end
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| --Make sure to return nothing if there are no drop sources
| |
| if Shared.tableIsEmpty(dropRows) then return '' end
| |
|
| |
| table.sort(dropRows, function(a, b)
| |
| if a.weight / a.totalWeight == b.weight / b.totalWeight then
| |
| if a.minqty + a.qty == b.minqty + b.qty then
| |
| return (a.type == b.type and a.source < b.source) or a.type < b.type
| |
| else
| |
| return a.minqty + a.qty > b.minqty + b.qty
| |
| end
| |
| else
| |
| return a.weight / a.totalWeight > b.weight / b.totalWeight
| |
| end
| |
| end)
| |
| for i, data in ipairs(dropRows) do
| |
| table.insert(resultPart, buildRow(data.source, data.type, data.minqty, data.qty, data.weight, data.totalWeight, data.expIcon))
| |
| end
| |
|
| |
| table.insert(resultPart, '\r\n|}')
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.getItemLootSourceTable(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._getItemLootSourceTable(item)
| |
| end
| |
|
| |
| function p._getItemUpgradeTable(item)
| |
| local resultPart = {}
| |
| local upgrade = GameData.getEntityByProperty('itemUpgrades', 'upgradedItemID', item.id)
| |
| if upgrade ~= nil then
| |
| local upgradeCost = {}
| |
| for i, itemCost in ipairs(upgrade.itemCosts) do
| |
| local costItem = Items.getItemByID(itemCost.id)
| |
| if costItem ~= nil then
| |
| table.insert(upgradeCost, Icons.Icon({costItem.name, type='item', qty=itemCost.quantity}))
| |
| end
| |
| end
| |
| if type(upgrade.gpCost) == 'number' and upgrade.gpCost > 0 then
| |
| table.insert(upgradeCost, Icons.GP(upgrade.gpCost))
| |
| end
| |
| if type(upgrade.scCost) == 'number' and upgrade.scCost > 0 then
| |
| table.insert(upgradeCost, Icons.SC(upgrade.scCost))
| |
| end
| |
| table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="2"|[[Upgrading Items|Item Upgrade]]')
| |
| table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials\r\n|')
| |
| table.insert(resultPart, table.concat(upgradeCost, '<br/>'))
| |
| table.insert(resultPart, '\r\n|}')
| |
| end
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.getItemUpgradeTable(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._getItemUpgradeTable(item)
| |
| end
| |
|
| |
| function p._getSuperheatSmithRecipe(item)
| |
| local smithRecipe = GameData.getEntityByProperty(SkillData.Smithing.recipes, 'productID', item.id)
| |
| if smithRecipe ~= nil and smithRecipe.categoryID == 'melvorD:Bars' then
| |
| return smithRecipe
| |
| end
| |
| end
| |
|
| |
| function p._getItemSuperheatTable(item)
| |
| --Manually build the Superheat Item table
| |
| -- Validate that the item can be superheated
| |
| local smithRecipe = p._getSuperheatSmithRecipe(item)
| |
| if smithRecipe == nil then
| |
| return Shared.printError('The item "' .. item.name .. '" cannot be superheated')
| |
| end
| |
|
| |
| local oreStringPart, coalString = {}, ''
| |
| for i, mat in ipairs(smithRecipe.itemCosts) do
| |
| local matItem = Items.getItemByID(mat.id)
| |
| if mat.id == 'melvorD:Coal_Ore' then
| |
| coalString = Icons.Icon({matItem.name, type='item', notext='true', qty=mat.quantity})
| |
| else
| |
| table.insert(oreStringPart, Icons.Icon({matItem.name, type='item', notext='true', qty=mat.quantity}))
| |
| end
| |
| end
| |
|
| |
| --Set up the header
| |
| local superheatTable = {}
| |
| table.insert(superheatTable, '{|class="wikitable"\r\n!colspan="2"|Spell')
| |
| table.insert(superheatTable, '!!'..Icons.Icon({'Smithing', type='skill', notext='true'})..' Level')
| |
| table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' Level')
| |
| table.insert(superheatTable, '!!'..Icons.Icon({'Magic', type='skill', notext='true'})..' XP')
| |
| table.insert(superheatTable, '!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars')
| |
| table.insert(superheatTable, '!!Ore!!Runes')
| |
|
| |
| --Loop through all the variants
| |
| local spells = Magic.getSpellsProducingItem(item.id)
| |
| for i, spell in ipairs(spells) do
| |
| if spell.specialCost ~= nil and Shared.contains({ 'BarIngredientsWithCoal', 'BarIngredientsWithoutCoal' }, spell.specialCost.type) then
| |
| local imgType = Magic._getSpellIconType(spell)
| |
| table.insert(superheatTable, '\r\n|-\r\n|'..Icons.Icon({spell.name, type=imgType, notext=true, size=50}))
| |
| table.insert(superheatTable, '||'..Icons.Icon({spell.name, type=imgType, noicon=true})..'||style="text-align:right;"|'..smithRecipe.level)
| |
| table.insert(superheatTable, '||style="text-align:right;"|'..spell.level..'||style="text-align:right;"|'..spell.baseExperience)
| |
| table.insert(superheatTable, '||style="text-align:right;"|'..spell.productionRatio)
| |
| table.insert(superheatTable, '|| '..table.concat(oreStringPart, ', '))
| |
| if spell.specialCost.type == 'BarIngredientsWithCoal' and coalString ~= '' then
| |
| table.insert(superheatTable, (not Shared.tableIsEmpty(oreStringPart) and ', ' or '') .. coalString)
| |
| end
| |
| table.insert(superheatTable, '||style="text-align:center"| ' .. Magic._getSpellRunes(spell))
| |
| end
| |
| end
| |
|
| |
| --Add the table end and add the table to the result string
| |
| table.insert(superheatTable, '\r\n|}')
| |
| return table.concat(superheatTable)
| |
| end
| |
|
| |
| function p.getItemSuperheatTable(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._getItemSuperheatTable(item)
| |
| end
| |
|
| |
| function p._getTownshipTraderTable(item)
| |
| for i, tsResource in ipairs(SkillData.Township.itemConversions.fromTownship) do
| |
| for j, tradeDef in ipairs(tsResource.items) do
| |
| if tradeDef.itemID == item.id then
| |
| -- Item found, build table
| |
| local res = GameData.getEntityByID(SkillData.Township.resources, tsResource.resourceID)
| |
| local resName = (res ~= nil and res.name) or 'Unknown'
| |
| local resQty = math.max(item.sellsFor, 2)
| |
|
| |
| local resultPart = {}
| |
| table.insert(resultPart, '{| class="wikitable"\n|-')
| |
| table.insert(resultPart, '\n!colspan="2"| ' .. Icons.Icon({'Township', 'Trader', type='skill'}))
| |
| table.insert(resultPart, '\n|-\n!style="text-align:right;"| Cost')
| |
| table.insert(resultPart, '\n| ' .. Icons.Icon({resName, qty=resQty, type='resource'}))
| |
| table.insert(resultPart, '\n|-\n!style="text-align:right;| Requirements')
| |
| table.insert(resultPart, '\n| ' .. Shop.getRequirementString(tradeDef.unlockRequirements))
| |
| table.insert(resultPart, '\n|}')
| |
|
| |
| return table.concat(resultPart)
| |
| end
| |
| end
| |
| end
| |
| return ''
| |
| end
| |
|
| |
| function p._getItemSourceTables(item)
| |
| local resultPart = {}
| |
| local shopTable = Shop._getItemShopTable(item)
| |
| if shopTable ~= '' then
| |
| table.insert(resultPart, '===Shop===\r\n'..shopTable)
| |
| end
| |
|
| |
| local creationTable = p._getCreationTable(item)
| |
| if creationTable ~= '' then
| |
| if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\r\n') end
| |
| table.insert(resultPart, '===Creation===\r\n'..creationTable)
| |
| end
| |
|
| |
| local upgradeTable = p._getItemUpgradeTable(item)
| |
| if upgradeTable ~= '' then
| |
| if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\r\n') end
| |
| if creationTable ~= '' then table.insert(resultPart, '===Creation===\r\n') end
| |
| table.insert(resultPart, upgradeTable)
| |
| end
| |
|
| |
| local townshipTable = p._getTownshipTraderTable(item)
| |
| if townshipTable ~= '' then
| |
| if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\n') end
| |
| table.insert(resultPart, '===Township===\n' .. townshipTable)
| |
| end
| |
|
| |
| if p._getSuperheatSmithRecipe(item) ~= nil then
| |
| table.insert(resultPart, '\r\n==='..Icons.Icon({'Alt. Magic', type='skill'})..'===\r\n'..p._getItemSuperheatTable(item))
| |
| end
| |
|
| |
| local lootTable = p._getItemLootSourceTable(item)
| |
| if lootTable ~= '' then
| |
| if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\r\n') end
| |
| table.insert(resultPart, '===Loot===\r\n'..lootTable)
| |
| end
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.getItemSourceTables(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._getItemSourceTables(item)
| |
| end
| |
|
| |
| function p.getCombatPassiveSlotItems(frame)
| |
| local resultPart = {}
| |
| table.insert(resultPart, '{| class="wikitable"\r\n')
| |
| table.insert(resultPart, '|-\r\n')
| |
| table.insert(resultPart, '!colspan="2"|Item\r\n! Passive\r\n')
| |
|
| |
| local itemArray = Items.getItems(function(item) return item.validSlots ~= nil and (item.golbinRaidExclusive == nil or not item.golbinRaidExclusive) and Shared.contains(item.validSlots, 'Passive') end)
| |
|
| |
| for i, item in ipairs(itemArray) do
| |
| local passiveDesc = item.customDescription or Constants.getModifiersText(item.modifiers, false)
| |
| table.insert(resultPart, '|-\r\n')
| |
| table.insert(resultPart, '! '..Icons.Icon({item.name, type='item', notext='true'})..'\r\n! '..Icons.Icon({item.name, type='item', noicon=true})..'\r\n')
| |
| table.insert(resultPart, '| '..passiveDesc..'\r\n')
| |
| end
| |
|
| |
| table.insert(resultPart, '|}')
| |
|
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p._getItemMonsterSources(item)
| |
| local resultArray = {}
| |
| for i, monster in ipairs(GameData.rawData.monsters) do
| |
| local chance = 0
| |
| local weight = 0
| |
| local minQty = 1
| |
| local maxQty = 1
| |
| if monster.bones ~= nil and monster.bones.itemID == item.id and Monsters._getMonsterBones(monster) ~= nil then
| |
| -- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table
| |
| maxQty = (monster.bones.quantity ~= nil and monster.bones.quantity) or 1
| |
| minQty = maxQty
| |
| chance = 1
| |
| weight = 1
| |
| elseif monster.lootTable ~= nil and not Monsters._isDungeonOnlyMonster(monster) then
| |
| -- If the monster has a loot table, check if the item we are looking for is in there
| |
| -- Dungeon exclusive monsters don't count as they are either:
| |
| -- - A monster before the boss, which doesn't drop anything except shards (checked above)
| |
| -- - A boss monster, whose drops are accounted for in data from Areas instead
| |
| for j, loot in ipairs(monster.lootTable) do
| |
| weight = weight + loot.weight
| |
| if loot.itemID == item.id then
| |
| chance = loot.weight
| |
| minQty = loot.minQuantity
| |
| maxQty = loot.maxQuantity
| |
| end
| |
| end
| |
| local lootChance = monster.lootChance ~= nil and (monster.bones == nil or monster.bones.itemID ~= item.id) and monster.lootChance or 100
| |
| chance = chance * lootChance
| |
| weight = weight * 100
| |
| chance, weight = Shared.fractionpair(chance, weight)
| |
| end
| |
| if chance > 0 then
| |
| -- Item drops when the monster is killed
| |
| table.insert(resultArray, {id = monster.id, dropWt = chance, totalWt = weight, minQty = minQty, maxQty = maxQty})
| |
| end
| |
| end
| |
| return resultArray
| |
| end
| |
|
| |
| function p.getItemMonsterSources(itemName)
| |
| local item = Items.getItem(itemName)
| |
| return p._getItemMonsterSources(item)
| |
| end
| |
|
| |
| function p.getItemFarmTable(frame)
| |
| local itemName = frame.args ~= nil and frame.args[1] or frame[1]
| |
| local quantity = frame.args ~= nil and frame.args[2] or frame[2]
| |
| local item = Items.getItem(itemName)
| |
| if item == nil then
| |
| return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
| |
| end
| |
|
| |
| return p._getItemFarmTable(itemName, quantity)
| |
| end
| |
|
| |
| function p._getItemFarmTable(itemName, quantity)
| |
|
| |
| local monsterResults = {}
| |
| local output = {}
| |
|
| |
| table.insert(output, '{| class="wikitable" style="width: 100%;"')
| |
| table.insert(output, '{| class="wikitable sortable" style="width: 100%;"')
| |
| table.insert(output, '! style="width: 25%;"|'..Icons.Icon({itemName, notext=true, size=50, type="item"})..' x'..quantity)
| |
| table.insert(output, '! colspan="3"|[['..itemName..']]')
| |
| table.insert(output, '!')
| |
| table.insert(output, '|-')
| |
| table.insert(output, '! Monster')
| |
| table.insert(output, '! '..Icons.Icon({"HitPoints", notext=true, size=50, type="skill"})..' Effective HP<br> (including DR)')
| |
| table.insert(output, '! Drop Chance')
| |
| table.insert(output, '! Avg. Kills')
| |
| table.insert(output, '! Avg. Damage Required')
| |
|
| |
|
| |
| for i, v in ipairs(p.getItemMonsterSources(itemName)) do
| |
| local monster = Monsters.getMonsterByID(v.id)
| |
| local effectiveHp = Monsters.getMonsterEffectiveHP(monster.name)
| |
| local dropChance = Monsters.getItemDropChance({monster.name, itemName})
| |
| table.insert(monsterResults, {monster = monster, effectiveHp = effectiveHp, dropChance = dropChance})
| |
| end
| |
|
| |
| for i, v in ipairs(monsterResults) do
| |
| table.insert(output, '|-')
| |
| table.insert(output, '| {{MonsterIcon|'..v.monster.name..'}}')
| |
| table.insert(output, '| {{#invoke:Shared|formatNumber|'..v.effectiveHp..'}}')
| |
| table.insert(output, '| '..v.dropChance..'%')
| |
| table.insert(output, '| {{#invoke:Shared|formatNumber|{{#expr:'..(quantity/(v.dropChance/100))..' round 0}}}}')
| |
| table.insert(output, '| {{#invoke:Shared|formatNumber|{{#expr:('..(quantity/(v.dropChance/100))..' round 0) *'..v.effectiveHp..'}}}}')
| |
| end
| |
|
| |
| table.insert(output, '|-')
| |
| table.insert(output, '|}')
| |
|
| |
| return table.concat(output, '\r\n')
| |
| 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',
| |
| 'Yew Longbow',
| |
| 'Water Rune',
| |
| 'Steam Rune',
| |
| 'Controlled Heat Potion II',
| |
| 'Wolf',
| |
| 'Cyclops',
| |
| 'Leprechaun',
| |
| 'Redwood Logs',
| |
| 'Carrot Cake',
| |
| 'Carrot Cake (Perfect)',
| |
| 'Mantalyme Herb',
| |
| 'Carrot',
| |
| 'Topaz',
| |
| 'Rune Essence',
| |
| 'Sanguine Blade',
| |
| 'Ring of Power',
| |
| 'Infernal Claw',
| |
| 'Chapeau Noir',
| |
| 'Stardust',
| |
| 'Rope',
| |
| 'Ancient Ring of Mastery',
| |
| 'Mysterious Stone',
| |
| 'Mastery Token (Cooking)',
| |
| 'Gem Gloves',
| |
| "Thief's Moneysack",
| |
| "Golden Stardust",
| |
| "Golden Star",
| |
| "Slayer Deterer"
| |
| }
| |
| local checkFuncs = {
| |
| p.getItemSourceTables,
| |
| --p.getCreationTable,
| |
| p.getItemSources,
| |
| --p.getItemLootSourceTable,
| |
| }
| |
| 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
| |
| --]==]
| |
|
| |
| return p
| |