|
|
(2 intermediate revisions by one other user not shown) |
Line 1: |
Line 1: |
| --Splitting some functions into here to avoid bloating a single file
| |
| local p = {}
| |
|
| |
|
| local SkillData = mw.loadData('Module:Sandbox/Skills/data')
| |
| local ShopData = mw.loadData('Module:Shop/data')
| |
|
| |
| local Constants = require('Module:Constants')
| |
| local Shared = require('Module:Shared')
| |
| local Items = require('Module:Sandbox/Items')
| |
| local Icons = require('Module:Icons')
| |
|
| |
| local thievingNormalLootChance = 75
| |
| local thievingAreaLootChance = 0.2
| |
|
| |
| function p.getConstellationByID(constID)
| |
| return SkillData.Astrology.Constellations[constID]
| |
| end
| |
|
| |
| function p.getConstellation(constName)
| |
| for i, const in ipairs(SkillData.Astrology.Constellations) do
| |
| if const.name == constName then
| |
| return const
| |
| end
| |
| end
| |
| return nil
| |
| end
| |
|
| |
| function p.getConstellations(checkFunc)
| |
| local result = {}
| |
| for i, const in ipairs(SkillData.Astrology.Constellations) do
| |
| if checkFunc(const) then
| |
| table.insert(result, const)
| |
| end
| |
| end
| |
| return result
| |
| end
| |
|
| |
| function p.getAxeTable(frame)
| |
| local toolArray = {}
| |
| for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
| |
| if Shared.contains(upgrade.name, 'Axe') then
| |
| table.insert(toolArray, upgrade)
| |
| end
| |
| end
| |
|
| |
| local result = '{| class="wikitable"'
| |
| result = result..'\r\n!colspan="4"| !!colspan="2"|Cut Time Decrease'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
| |
| result = result..'!!Cost!!This Axe!!Total'
| |
|
| |
| local total = 0
| |
|
| |
| for i, tool in Shared.skpairs(toolArray) do
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
| |
| result = result..'||'..tool.name
| |
| local level = 1
| |
| if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
| |
| --Gonna be lazy and assume there's only the one skill level and it's the one we care about
| |
| level = tool.unlockRequirements.skillLevel[1][2]
| |
| end
| |
| result = result..'||style="text-align:right"|'..level
| |
| result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
| |
|
| |
| local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
| |
| total = total + cutTime
| |
| result = result..'||style="text-align:right"|-'..cutTime..'%'
| |
| result = result..'||style="text-align:right"|-'..total..'%'
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getPickaxeTable(frame)
| |
| local toolArray = {}
| |
| for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
| |
| if Shared.contains(upgrade.name, 'Pickaxe') then
| |
| table.insert(toolArray, upgrade)
| |
| end
| |
| end
| |
|
| |
| local result = '{| class="wikitable"'
| |
| result = result..'\r\n!colspan="4"| !!colspan="2"|Mine Time Decrease!!colspan="2"|2x Ore Chance'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
| |
| result = result..'!!Cost!!This Pick!!Total!!This Pick!!Total'
| |
|
| |
| local total = 0
| |
| local total2 = 0
| |
|
| |
| for i, tool in Shared.skpairs(toolArray) do
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
| |
| result = result..'||'..tool.name
| |
| local level = 1
| |
| if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
| |
| --Gonna be lazy and assume there's only the one skill level and it's the one we care about
| |
| level = tool.unlockRequirements.skillLevel[1][2]
| |
| end
| |
| result = result..'||style="text-align:right"|'..level
| |
| result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
| |
|
| |
| local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
| |
| total = total + cutTime
| |
|
| |
| result = result..'||style="text-align:right"|-'..cutTime..'%'
| |
| result = result..'||style="text-align:right"|-'..total..'%'
| |
|
| |
| local OreDouble = tool.contains.modifiers.increasedChanceToDoubleOres
| |
| total2 = total2 + OreDouble
| |
|
| |
| result = result..'||style="text-align:right"|+'..OreDouble..'%'
| |
| result = result..'||style="text-align:right"|+'..total2..'%'
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getRodTable(frame)
| |
| local toolArray = {}
| |
| for i, upgrade in Shared.skpairs(ShopData.Shop.SkillUpgrades) do
| |
| if Shared.contains(upgrade.name, 'Fishing Rod') then
| |
| table.insert(toolArray, upgrade)
| |
| end
| |
| end
| |
|
| |
| local result = '{| class="wikitable"'
| |
| result = result..'\r\n!colspan="4"| !!colspan="2"|Catch Time Decrease'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan="2"|Name!!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level'
| |
| result = result..'!!Cost!!This Rod!!Total'
| |
|
| |
| local total = 0
| |
|
| |
| for i, tool in Shared.skpairs(toolArray) do
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n|style="min-width:25px" data-sort-value="'..tool.name..'"|'..Icons.Icon({tool.name, type='upgrade', size='50', notext=true})
| |
| result = result..'||'..tool.name
| |
| local level = 1
| |
| if tool.unlockRequirements ~= nil and tool.unlockRequirements.skillLevel ~= nil then
| |
| --Gonna be lazy and assume there's only the one skill level and it's the one we care about
| |
| level = tool.unlockRequirements.skillLevel[1][2]
| |
| end
| |
| result = result..'||style="text-align:right"|'..level
| |
| result = result..'||style="text-align:right" data-sort-value="'..tool.cost.gp..'"|'..Icons.GP(tool.cost.gp)
| |
|
| |
| local cutTime = tool.contains.modifiers.decreasedSkillIntervalPercent[1][2]
| |
| total = total + cutTime
| |
| result = result..'||style="text-align:right"|-'..cutTime..'%'
| |
| result = result..'||style="text-align:right"|-'..total..'%'
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getTreesTable(frame)
| |
| local result = '{| class="wikitable sortable"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan="2"|Tree!!colspan="2"|Logs!!'..Icons.Icon({'Woodcutting', type='skill', notext=true})..' Level'
| |
| result = result..'!!XP!!Cut Time!!XP/s!!GP/s'
| |
|
| |
| for i, tree in Shared.skpairs(SkillData.Woodcutting.Trees) do
| |
| result = result..'\r\n|-'
| |
| local treeName = Shared.titleCase(tree.type..' tree')
| |
| local logName = Shared.titleCase(tree.type..' logs')
| |
| result = result..'\r\n|style="min-width:25px" data-sort-value="'..treeName..'"|'..Icons.Icon({logName, img=treeName, type='tree', notext=true, size=50})
| |
| result = result..'||'..treeName..''
| |
| result = result..'||style="min-width:25px" data-sort-value="'..logName..'"|'..Icons.Icon({logName, type='item', notext=true, size=50})
| |
| result = result..'||'..Icons.Icon({logName, type='item', noicon=true})
| |
| result = result..'||style="text-align:right"|'..tree.levelRequired
| |
| result = result..'||style="text-align:right"|'..tree.baseExperience
| |
| result = result..'||style="text-align:right" data-sort-value="'..tree.baseInterval..'"|'..Shared.timeString(tree.baseInterval/1000, true)
| |
| local XPs = tree.baseExperience / (tree.baseInterval / 1000)
| |
| local Log = Items.getItemByID(tree.logID)
| |
| local GPs = Log.sellsFor / (tree.baseInterval / 1000)
| |
| result = result..'||style="text-align:right"|'..Shared.round(XPs, 2, 2)
| |
| result = result..'||style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2))
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getSpecialFishingTable(frame)
| |
| local lootValue = 0
| |
| local totalWt = Items.specialFishWt
| |
|
| |
| local result = ''
| |
| result = result..'\r\n{|class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!Item'
| |
| result = result..'!!Price!!colspan="2"|Chance'
| |
|
| |
| --Sort the loot table by weight in descending order
| |
| table.sort(Items.specialFishLoot, function(a, b) return a[2] > b[2] end)
| |
| for i, row in pairs(Items.specialFishLoot) do
| |
| local thisItem = Items.getItemByID(row[1])
| |
| result = result..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
| |
| result = result..'||style="text-align:left" data-sort-value="'..thisItem.sellsFor..'"'
| |
| result = result..'|'..Icons.GP(thisItem.sellsFor)
| |
|
| |
| local dropChance = (row[2] / totalWt) * 100
| |
| -- If chance is less than 01.0% then show 2 significant figures, otherwise 2 decimal places
| |
| local fmt = (dropChance < 0.10 and '%.2g') or '%.2f'
| |
| result = result..'||style="text-align:right" data-sort-value="'..row[2]..'"'
| |
| result = result..'|'..Shared.fraction(row[2], totalWt)
| |
| result = result..'||style="text-align:right"|'..string.format(fmt, dropChance)..'%'
| |
| lootValue = lootValue + (dropChance * 0.01 * thisItem.sellsFor)
| |
| end
| |
| result = result..'\r\n|}'
| |
| result = result..'\r\nThe average value of a roll on the special fishing loot table is '..Icons.GP(Shared.round(lootValue, 2, 0))
| |
|
| |
| return result
| |
| end
| |
|
| |
| function p.getFishingJunkTable(frame)
| |
| local result = '{| class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan="2"|Item!!Value'
| |
|
| |
| local itemArray = Items.getItems(function(item) return item.type == "Junk" end)
| |
|
| |
| table.sort(itemArray, function(a, b) return a.name < b.name end)
| |
|
| |
| for i, item in Shared.skpairs(itemArray) do
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext=true, size='50'})
| |
| result = result..'||'..Icons.Icon({item.name, type='item', noicon=true})
| |
| result = result..'||style="text-align:right;" data-sort-value="'..item.sellsFor..'"|'..Icons.GP(item.sellsFor)
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
|
| |
| return result
| |
| end
| |
|
| |
| function p.getMiningOresTable(frame)
| |
| local result = '{|class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan=2|Ore!!'..Icons.Icon({'Mining', type='skill', notext=true})..' Level'
| |
| result = result..'!!XP!!Respawn Time!!Ore Value'
| |
|
| |
| local mineData = Shared.clone(SkillData.Mining.Rocks)
| |
|
| |
| table.sort(mineData, function(a, b) return a.levelRequired < b.levelRequired end)
| |
|
| |
| for i, oreData in Shared.skpairs(mineData) do
| |
| local ore = Items.getItemByID(oreData.oreID)
| |
| result = result..'\r\n|-\r\n|style="min-width:25px"|'..Icons.Icon({ore.name, type='item', size='50', notext=true})
| |
| result = result..'||'..Icons.Icon({ore.name, type='item', noicon=true})
| |
| result = result..'||style="text-align:right"|'..oreData.levelRequired..'||style="text-align:right"|'..oreData.baseExperience
| |
| result = result..'||style="text-align:right" data-sort-value="'..oreData.baseRespawnInterval..'"|'
| |
| result = result..Shared.timeString(oreData.baseRespawnInterval / 1000, true)
| |
| result = result..'||data-sort-value="'..ore.sellsFor..'"|'..Icons.GP(ore.sellsFor)
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getMiningGemsTable(frame)
| |
| local result = '{|class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan=2|Gem!!Gem Chance!!Gem Price'
| |
|
| |
| -- Sort gems by ID order
| |
| for i, gemData in Shared.spairs(Items.GemTable, function(t,a,b) return t[a].id < t[b].id end) do
| |
| local gem = Items.getItemByID(gemData.id)
| |
| result = result..'\r\n|-\r\n|style="min-width:25px"|'
| |
| result = result..Icons.Icon({gem.name, type='item', size='50', notext=true})
| |
| result = result..'||'..Icons.Icon({gem.name, type='item', noicon=true})
| |
| result = result..'||style="text-align:right"|'..string.format("%.1f%%", gemData.chance)
| |
| result = result..'||data-sort-value="'..gem.sellsFor..'"|'..Icons.GP(gem.sellsFor)
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getFishTable(frame)
| |
| local data = Items.getItems(function(item) return item.fishingID ~= nil end)
| |
|
| |
| table.sort(data, function(a, b) return a.fishingID < b.fishingID end)
| |
|
| |
| local result = '{| class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!Fish\r\n!Name\r\n!'..Icons.Icon({'Fishing', type='skill', notext=true})..' Level\r\n!Catch Time'
| |
| result = result..'\r\n!Experience\r\n!Fish Price\r\n!XP/s\r\n!GP/s\r\n!'
| |
| result = result..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
| |
|
| |
| for i, fish in Shared.skpairs(data) do
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', size='50', notext=true})
| |
| result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fish.name, type='item', noicon=true})
| |
| result = result..'\r\n| style="text-align:right"|'..fish.fishingLevel
| |
|
| |
| local timeSortVal = (fish.minFishingInterval + fish.maxFishingInterval) / 2
| |
| local timeStr = string.format("%.1fs-%.1fs", (fish.minFishingInterval/1000), (fish.maxFishingInterval/1000))
| |
| result = result..'\r\n| style="text-align:right" data-sort-value="'..timeSortVal..'"|'..timeStr
| |
| result = result..'\r\n| style="text-align:right"|'..fish.fishingXP
| |
| result = result..'\r\n| style="text-align:right"|'..fish.sellsFor
| |
| local XPs = fish.fishingXP / (timeSortVal / 1000)
| |
| local GPs = fish.sellsFor / (timeSortVal / 1000)
| |
| result = result..'\r\n| style="text-align:right"|'..Shared.round(XPs, 2, 2)
| |
| result = result..'\r\n| style="text-align:right" data-sort-value="'..GPs..'"|'..Icons.GP(Shared.round(GPs, 2, 2))
| |
|
| |
| local cookStr = "N/A"
| |
| if fish.cookingLevel ~= nil then
| |
| cookStr = fish.cookingLevel
| |
| end
| |
| result = result..'\r\n| style="text-align:right"|'..cookStr
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getFishingAreasTable(frame)
| |
|
| |
| local result = '{| class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!Name\r\n!Fish\r\n!Fish Chance'
| |
| result = result..'\r\n!Junk Chance\r\n!Special Chance'
| |
|
| |
| for i, area in ipairs(SkillData.Fishing.Areas) do
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n| style ="text-align: left;" |'..area.name
| |
|
| |
| local fishArray = {}
| |
| for j, fish in ipairs(area.fish) do
| |
| local fishItem = Items.getItemByID(fish.itemID)
| |
| if fishItem ~= nil then
| |
| table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
| |
| end
| |
| end
| |
| result = result..'\r\n|'..table.concat(fishArray, '<br/>')
| |
|
| |
| result = result..'\r\n| style="text-align:right"|'..area.fishChance..'%'
| |
| result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%'
| |
| result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%'
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getThievingNPC(npcName)
| |
| local result = nil
| |
| for i, npc in Shared.skpairs(SkillData.Thieving.NPCs) do
| |
| if npc.name == npcName then
| |
| result = Shared.clone(npc)
| |
| break
| |
| end
| |
| end
| |
| return result
| |
| end
| |
|
| |
| function p.getThievingNPCArea(npc)
| |
| if type(npc) == 'string' then
| |
| npc = p.getThievingNPC(npc)
| |
| end
| |
|
| |
| local result = nil
| |
| for i, area in Shared.skpairs(SkillData.Thieving.Areas) do
| |
| for j, npcID in pairs(area.npcs) do
| |
| if npcID == npc.id then
| |
| result = area
| |
| break
| |
| end
| |
| end
| |
| end
| |
| return result
| |
| end
| |
|
| |
| function p._getThievingNPCStat(npc, statName)
| |
| local result = nil
| |
|
| |
| if statName == 'level' then
| |
| result = Icons._SkillReq('Thieving', npc.level)
| |
| elseif statName == 'maxHit' then
| |
| result = npc.maxHit * 10
| |
| elseif statName == 'area' then
| |
| local area = p.getThievingNPCArea(npc)
| |
| result = area.name
| |
| else
| |
| result = npc[statName]
| |
| end
| |
|
| |
| if result == nil then
| |
| result = ''
| |
| end
| |
|
| |
| return result
| |
| end
| |
|
| |
| function p.getThievingNPCStat(frame)
| |
| local npcName = frame.args ~= nil and frame.args[1] or frame[1]
| |
| local statName = frame.args ~= nil and frame.args[2] or frame[2]
| |
| local npc = p.getThievingNPC(npcName)
| |
| if npc == nil then
| |
| return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
| |
| end
| |
|
| |
| return p._getThievingNPCStat(npc, statName)
| |
| end
| |
|
| |
| function p.getThievingGeneralRareTable(frame)
| |
| local rareTxt = '{|class="wikitable sortable"'
| |
| rareTxt = rareTxt..'\r\n!Item!!Qty'
| |
| rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
| |
| for i, drop in pairs(SkillData.Thieving.RareItems) do
| |
| local thisItem = Items.getItemByID(drop.itemID)
| |
| local odds = drop.chance
| |
|
| |
| rareTxt = rareTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
| |
| rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
| |
| rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(1, Shared.round2(1/(odds/100), 0))
| |
| rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.round(odds, 4, 4)..'%'
| |
| end
| |
| rareTxt = rareTxt..'\r\n|}'
| |
| return rareTxt
| |
| end
| |
|
| |
| function p._getThievingNPCLootTables(npc)
| |
| local result = ''
| |
| local sectionTxt = {}
| |
|
| |
| --Five sections here: GP, normal loot, area loot, rare loot, and unique item
| |
| --First up, GP:
| |
| local gpTxt = 'Successfully pickpocketing the '..npc.name..' will always give '..Icons.GP(1, npc.maxGP)
| |
| table.insert(sectionTxt, gpTxt)
| |
|
| |
| --Next up, normal loot:
| |
| --(Skip if no loot)
| |
| if npc.lootTable ~= nil and Shared.tableCount(npc.lootTable) > 0 then
| |
| local normalTxt = '===Possible Common Drops:===\r\nUp to one of these will be received on a successful pickpocket:'
| |
| local totalWt = 0
| |
| local lootChance = thievingNormalLootChance
| |
| local lootValue = 0
| |
|
| |
| --First loop through to get the total weight so we have it for later
| |
| for i, loot in pairs(npc.lootTable) do
| |
| totalWt = totalWt + loot[2]
| |
| end
| |
|
| |
| normalTxt = normalTxt..'\r\n{|class="wikitable sortable"'
| |
| normalTxt = normalTxt..'\r\n!Item!!Qty'
| |
| normalTxt = normalTxt..'!!Price!!colspan="2"|Chance'
| |
|
| |
| --Then sort the loot table by weight
| |
| table.sort(npc.lootTable, function(a, b) return a[2] > b[2] end)
| |
| for i, row in Shared.skpairs(npc.lootTable) do
| |
| local thisItem = Items.getItemByID(row[1])
| |
| local maxQty = row[3]
| |
| if thisItem ~= nil then
| |
| normalTxt = normalTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
| |
| else
| |
| normalTxt = normalTxt..'\r\n|-\r\n|Unknown Item[[Category:Pages with script errors]]'
| |
| end
| |
| normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..maxQty..'"|'
| |
|
| |
| if maxQty > 1 then
| |
| normalTxt = normalTxt.. '1 - '
| |
| end
| |
| normalTxt = normalTxt..Shared.formatnum(row[3])
| |
|
| |
| --Adding price columns
| |
| local itemPrice = 0
| |
| if thisItem == nil then
| |
| normalTxt = normalTxt..'||data-sort-value="0"|???'
| |
| else
| |
| itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
| |
| if itemPrice == 0 or maxQty == 1 then
| |
| normalTxt = normalTxt..'||'..Icons.GP(itemPrice)
| |
| else
| |
| normalTxt = normalTxt..'||'..Icons.GP(itemPrice, itemPrice * maxQty)
| |
| end
| |
| end
| |
|
| |
| --Getting the drop chance
| |
| local dropChance = (row[2] / totalWt * lootChance)
| |
| if dropChance ~= 100 then
| |
| --Show fraction as long as it isn't going to be 1/1
| |
| normalTxt = normalTxt..'||style="text-align:right" data-sort-value="'..row[2]..'"'
| |
| normalTxt = normalTxt..'|'..Shared.fraction(row[2] * lootChance, totalWt * 100)
| |
| normalTxt = normalTxt..'||'
| |
| else
| |
| normalTxt = normalTxt..'||colspan="2" data-sort-value="'..row[2]..'"'
| |
| end
| |
| normalTxt = normalTxt..'style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%'
| |
|
| |
| --Adding to the average loot value based on price & dropchance
| |
| lootValue = lootValue + (dropChance * 0.01 * itemPrice * ((1 + maxQty) / 2))
| |
| end
| |
| if Shared.tableCount(npc.lootTable) > 1 then
| |
| normalTxt = normalTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:'
| |
| if lootChance < 100 then
| |
| normalTxt = normalTxt..'\r\n|style="text-align:right"|'..Shared.fraction(lootChance, 100)..'||'
| |
| else
| |
| normalTxt = normalTxt..'\r\n|colspan="2" '
| |
| end
| |
| normalTxt = normalTxt..'style="text-align:right"|'..Shared.round(lootChance, 2, 2)..'%'
| |
| end
| |
| normalTxt = normalTxt..'\r\n|}'
| |
| table.insert(sectionTxt, normalTxt)
| |
| end
| |
|
| |
| --After normal drops, add in rare drops
| |
| local rareTxt = '===Possible Rare Drops:===\r\nAny of these can be received after a successful pickpocket'
| |
| rareTxt = rareTxt..'\r\n'..p.getThievingGeneralRareTable()
| |
| table.insert(sectionTxt, rareTxt)
| |
|
| |
| local areaTxt = '===Possible Area Unique Drops==='
| |
| areaTxt = areaTxt..'\r\nAny Area Unique Drop is equally likely to be obtained after a successful pickpocket. '
| |
| areaTxt = areaTxt..'\r\nEach Area Unique Drop is rolled for separately, so it is possible to receive multiple Area Unique Drops from a single action. '
| |
| areaTxt = areaTxt..'The chance of receiving an Area Unique drop is tripled if the 95% Thieving Mastery Pool checkpoint is active.'
| |
|
| |
| local area = p.getThievingNPCArea(npc)
| |
| areaTxt = areaTxt..'\r\n{|class="wikitable sortable"'
| |
| areaTxt = areaTxt..'\r\n!Item!!Qty'
| |
| areaTxt = areaTxt..'!!Price!!colspan="2"|Chance'
| |
| local dropCount = Shared.tableCount(area.uniqueDrops)
| |
| local dropLines = {}
| |
| for i, drop in pairs(area.uniqueDrops) do
| |
| local thisItem = Items.getItemByID(drop.itemID)
| |
| local lineTxt = ''
| |
| lineTxt = lineTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
| |
| lineTxt = lineTxt..'||'..drop.qty..'||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
| |
| lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(thievingAreaLootChance/100))
| |
| lineTxt = lineTxt..'||'..Shared.round(thievingAreaLootChance, 2, 2)..'%'
| |
| dropLines[thisItem.name] = lineTxt
| |
| end
| |
| for i, txt in Shared.skpairs(dropLines) do
| |
| areaTxt = areaTxt..txt
| |
| end
| |
| areaTxt = areaTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:'
| |
| areaTxt = areaTxt..'\r\n|style="text-align:right"|'..Shared.fraction(1, 1/(thievingAreaLootChance/100))..'||'
| |
| areaTxt = areaTxt..'style="text-align:right"|'..Shared.round(thievingAreaLootChance, 2, 2)..'%'
| |
| areaTxt = areaTxt..'\r\n|}'
| |
| table.insert(sectionTxt, areaTxt)
| |
|
| |
| if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
| |
| local uniqueTxt = '===Possible NPC Unique Drop==='
| |
| uniqueTxt = uniqueTxt..'\r\nThe chance of receiving the unique drop for an NPC is based on a combination of several factors.'
| |
| uniqueTxt = uniqueTxt..' The unique drop chance for an NPC is included in the tooltip for your Stealth against that NPC.'
| |
| local thisItem = Items.getItemByID(npc.uniqueDrop.itemID)
| |
| uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
| |
| if npc.uniqueDrop.qty > 1 then
| |
| uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.qty})
| |
| else
| |
| uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'})
| |
| end
| |
|
| |
| table.insert(sectionTxt, uniqueTxt)
| |
| end
| |
|
| |
| return table.concat(sectionTxt, '\r\n')
| |
| end
| |
|
| |
| function p.getThievingNPCLootTables(frame)
| |
| local npcName = frame.args ~= nil and frame.args[1] or frame
| |
| local npc = p.getThievingNPC(npcName)
| |
| if npc == nil then
| |
| return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
| |
| end
| |
|
| |
| return p._getThievingNPCLootTables(npc)
| |
| end
| |
|
| |
| function p.getThievingNPCTable()
| |
| local result = '{| class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan="2"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!GP!!Unique Drop'
| |
| local npcArray = Shared.clone(SkillData.Thieving.NPCs)
| |
| table.sort(npcArray, function(a, b) return a.level < b.level end)
| |
| for i, npc in Shared.skpairs(npcArray) do
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true})
| |
| result = result..'||'..Icons.Icon({npc.name, type='thieving', noicon=true})
| |
|
| |
| local area = p.getThievingNPCArea(npc)
| |
| result = result..'||'..area.name
| |
| result = result..'||'..Icons._SkillReq('Thieving', npc.level)
| |
| result = result..'||style="text-align:right"|'..npc.xp
| |
| result = result..'||style="text-align:right"|'..(npc.maxHit * 10)
| |
| result = result..'||style="text-align:right"|'..npc.perception
| |
| result = result..'||data-sort-value="' .. npc.maxGP .. '"|'..Icons.GP(1, npc.maxGP)
| |
| if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID > -1 then
| |
| local uniqueDrop = Items.getItemByID(npc.uniqueDrop.itemID)
| |
| if npc.uniqueDrop.qty > 1 then
| |
| result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item', qty = npc.uniqueDrop.qty})
| |
| else
| |
| result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item'})
| |
| end
| |
| else
| |
| result = result..'|| '
| |
| end
| |
| end
| |
| result = result..'\r\n|}'
| |
|
| |
| return result
| |
| end
| |
|
| |
| function p.getThievingAreaTable(frame)
| |
| local resultPart = {}
| |
| table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
| |
| table.insert(resultPart, '\r\n|- class="headerRow-0"')
| |
| table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops')
| |
|
| |
| local areaArray = Shared.clone(SkillData.Thieving.Areas)
| |
| table.sort(areaArray, function(a, b) return a.id < b.id end)
| |
| for i, area in ipairs(areaArray) do
| |
| local minLevel, npcList, areaItemList = nil, {}, {}
| |
| -- Build NPC list & determine level for area, this is the minimum
| |
| -- Thieving level required for all NPCs within that area
| |
| if area.npcs ~= nil and Shared.tableCount(area.npcs) > 0 then
| |
| for j, npcID in ipairs(area.npcs) do
| |
| -- Don't bother cloning the NPC below since we aren't modifying any part of it
| |
| local npc = SkillData.Thieving.NPCs[npcID + 1]
| |
| if minLevel == nil or npc.level < minLevel then
| |
| minLevel = npc.level
| |
| end
| |
| table.insert(npcList, Icons.Icon({npc.name, type='thieving'}))
| |
| end
| |
| else
| |
| table.insert(npcList, '')
| |
| end
| |
|
| |
| -- Build area unique item list
| |
| if area.uniqueDrops ~= nil and Shared.tableCount(area.uniqueDrops) > 0 then
| |
| for k, drop in ipairs(area.uniqueDrops) do
| |
| local areaItem = Items.getItemByID(drop.itemID)
| |
| if areaItem == nil then
| |
| table.insert(areaItemList, 'Unknown[[Category:Pages with script errors]]')
| |
| else
| |
| local iconDef = {areaItem.name, type='item'}
| |
| if drop.qty > 1 then
| |
| iconDef.qty = drop.qty
| |
| end
| |
| table.insert(areaItemList, Icons.Icon(iconDef))
| |
| end
| |
| end
| |
| else
| |
| table.insert(areaItemList, '')
| |
| end
| |
|
| |
| -- Generate table row
| |
| table.insert(resultPart, '\r\n|-')
| |
| table.insert(resultPart, '\r\n|' .. area.name)
| |
| table.insert(resultPart, '\r\n|' .. Icons._SkillReq('Thieving', minLevel))
| |
| table.insert(resultPart, '\r\n|' .. table.concat(npcList, '<br/>'))
| |
| table.insert(resultPart, '\r\n|' .. table.concat(areaItemList, '<br/>'))
| |
| end
| |
| table.insert(resultPart, '\r\n|}')
| |
|
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.getThievingSourcesForItem(itemID)
| |
| local resultArray = {}
| |
|
| |
| local areaNPCs = {}
| |
|
| |
| --First check area unique drops
| |
| --If an area drops the item, add all the NPC ids to the list so we can add them later
| |
| if not result then
| |
| for i, area in pairs(SkillData.Thieving.Areas) do
| |
| for j, drop in pairs(area.uniqueDrops) do
| |
| if drop.itemID == itemID then
| |
| for k, npcID in pairs(area.npcs) do
| |
| areaNPCs[npcID] = drop.qty
| |
| end
| |
| break
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| --Now go through and get drop chances on each NPC if needed
| |
| for i, npc in pairs(SkillData.Thieving.NPCs) do
| |
| local totalWt = 0
| |
| local dropWt = 0
| |
| local dropQty = 0
| |
| for j, drop in pairs(npc.lootTable) do
| |
| totalWt = totalWt + drop[2]
| |
| if drop[1] == itemID then
| |
| dropWt = drop[2]
| |
| dropQty = drop[3]
| |
| end
| |
| end
| |
| if dropWt > 0 then
| |
| table.insert(resultArray, {npc = npc.name, minQty = 1, maxQty = dropQty, wt = dropWt * thievingNormalLootChance, totalWt = totalWt * 100, level = npc.level})
| |
| end
| |
|
| |
| --Chance of -1 on unique drops is to indicate variable chance
| |
| if npc.uniqueDrop ~= nil and npc.uniqueDrop.itemID == itemID then
| |
| table.insert(resultArray, {npc = npc.name, minQty = npc.uniqueDrop.qty, maxQty = npc.uniqueDrop.qty, wt = -1, totalWt = -1, level = npc.level})
| |
| end
| |
|
| |
| if areaNPCs[npc.id] ~= nil then
| |
| table.insert(resultArray, {npc = npc.name, minQty = areaNPCs[npc.id], maxQty = areaNPCs[npc.id], wt = thievingAreaLootChance, totalWt = 100, level = npc.level})
| |
| end
| |
| end
| |
|
| |
| for i, drop in pairs(SkillData.Thieving.RareItems) do
| |
| if drop.itemID == itemID then
| |
| table.insert(resultArray, {npc = 'all', minQty = 1, maxQty = 1, wt = 1, totalWt = Shared.round2(1/(drop.chance/100), 0), level = 1})
| |
| end
| |
| end
| |
|
| |
| return resultArray
| |
| end
| |
|
| |
| -- For a given constellation cons and modifier value modValue, generates and returns
| |
| -- a table of modifiers, much like any other item/object elsewhere in the game.
| |
| -- includeStandard: true|false, determines whether standard modifiers are included
| |
| -- includeUnique: true|false, determines whether unique modifiers are included
| |
| -- isDistinct: true|false, if true, the returned list of modifiers is de-duplicated
| |
| -- asKeyValue: true|false, if true, returns key/value pairs like usual modifier objects
| |
| function p._buildAstrologyModifierArray(cons, modValue, includeStandard, includeUnique, isDistinct, asKeyValue)
| |
| -- Temporary function to determine if the table already contains a given modifier
| |
| local containsMod = function(modList, modNew)
| |
| for i, modItem in ipairs(modList) do
| |
| -- Check mod names & value data types both equal
| |
| if modItem[1] == modNew[1] and type(modItem[2]) == type(modNew[2]) then
| |
| if type(modItem[2]) == 'table' then
| |
| if Shared.tablesEqual(modItem[2], modNew[2]) then
| |
| return true
| |
| end
| |
| elseif modItem[2] == modNew[2] then
| |
| return true
| |
| end
| |
| end
| |
| end
| |
| return false
| |
| end
| |
|
| |
| local addToArray = function(modArray, modNew)
| |
| if not isDistinct or (isDistinct and not containsMod(modArray, modNew)) then
| |
| table.insert(modArray, modNew)
| |
| end
| |
| end
| |
|
| |
| local modTypes = {}
| |
| if includeStandard then
| |
| table.insert(modTypes, 'standardModifiers')
| |
| end
| |
| if includeUnique then
| |
| table.insert(modTypes, 'uniqueModifiers')
| |
| end
| |
|
| |
| local modArray = {}
| |
| local isSkillMod = {}
| |
| for _, modType in ipairs(modTypes) do
| |
| for i, skillMods in ipairs(cons[modType]) do
| |
| local skillID = cons.skills[i]
| |
| if skillID ~= nil then
| |
| for j, modName in ipairs(skillMods) do
| |
| local modBaseName, modText, sign, isNegative, unsign, modBase = Constants.getModifierDetails(modName)
| |
| -- Check if modifier varies by skill, and amend the modifier value accordingly
| |
| local modVal = modValue
| |
| if Shared.contains(modText, '{SV0}') then
| |
| isSkillMod[modName] = true
| |
| modVal = {skillID, modValue}
| |
| end
| |
| addToArray(modArray, {modName, modVal})
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| if asKeyValue then
| |
| local modArrayKV = {}
| |
| for i, modDefn in ipairs(modArray) do
| |
| local modName, modVal = modDefn[1], modDefn[2]
| |
| local isSkill = isSkillMod[modName]
| |
| if modArrayKV[modName] == nil then
| |
| modArrayKV[modName] = (isSkill and { modVal } or modVal)
| |
| elseif isSkill then
| |
| table.insert(modArrayKV[modName], modVal)
| |
| else
| |
| modArrayKV[modName] = modArrayKV[modName] + modVal
| |
| end
| |
| end
| |
| return modArrayKV
| |
| else
| |
| return modArray
| |
| end
| |
| end
| |
|
| |
| function p._buildAstrologyConstellationTable()
| |
| local maxModifier = 5
| |
| local result = '{|class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan="2"|Constellation!!'..Icons.Icon({"Astrology", type='skill', notext='true'})..' Level'
| |
| result = result..'!!XP!!Skills!!Standard Modifiers!!Unique Modifiers'
| |
|
| |
| for i, cons in ipairs(SkillData.Astrology.Constellations) do
| |
| local name = cons.name
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='constellation', size='50', notext=true})..'||'..name
| |
| result = result..'||'..cons.level..'||'..cons.provides.xp
| |
|
| |
| local skillIconArray = {}
| |
| for j, skillID in ipairs(cons.skills) do
| |
| table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
| |
| end
| |
| result = result..'||'..table.concat(skillIconArray, '<br/>')
| |
|
| |
| local standModsRaw = p._buildAstrologyModifierArray(cons, maxModifier, true, false, false, false)
| |
| local standMods = {}
| |
| --Building the list of Standard modifiers:
| |
| for j, modifier in ipairs(standModsRaw) do
| |
| table.insert(standMods, Constants._getModifierText(modifier[1], modifier[2], false))
| |
| end
| |
| result = result..'|| '..table.concat(standMods, '<br/>')
| |
|
| |
| --Building the list of all Unique Modifiers
| |
| local uModsRaw = p._buildAstrologyModifierArray(cons, maxModifier, false, true, false, false)
| |
| local uMods = {}
| |
| for j, modifier in ipairs(uModsRaw) do
| |
| table.insert(uMods, Constants._getModifierText(modifier[1], modifier[2], false))
| |
| end
| |
| result = result..'||'..table.concat(uMods, '<br/>')
| |
| end
| |
| result = result..'\r\n|}'
| |
|
| |
| return result
| |
| end
| |
|
| |
| function p.buildAstrologyConstellationTable(frame)
| |
| return p._buildAstrologyConstellationTable()
| |
| end
| |
|
| |
| function p.buildAstrologyValueTable()
| |
| local result = '{|class="wikitable sortable"'
| |
| result = result..'\r\n!rowspan="2"| Value!!colspan="2"| Chance'
| |
| result = result..'\r\n|-\r\n! This Value!! This Value or Greater'
| |
| local cumulativeChance = 100
| |
| for i, chance in ipairs(SkillData.Astrology.ModifierMagnitudeChances) do
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n|style="text-align:right"| '..i
| |
| result = result..'\r\n|style="text-align:right"| ' .. chance .. '%'
| |
| result = result..'\r\n|style="text-align:right"| ' .. cumulativeChance .. '%'
| |
| cumulativeChance = cumulativeChance - chance
| |
| end
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| return p
| |