Home
Random
Log in
Settings
About Brighter Shores Wiki
Disclaimers
Search
Editing
Module:Skill calc
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
--[=[ dependencies [[Module:Addcommas]] [[Module:Tables]] [[Module:Coins]] [[Module:GETotal]] [[Module:Number]] [[Module:Experience]] [[Module:Skill_calc/eltGenerator]] [[Module:Skill_calc/bonusGenerator]] TODO Get input on bonus stacking Making a module to handle bxp jericowrahl 11:25 am MST 11/02/18 Note: Once all existing calculators have been converted, this Module will be cleaned up to remove any spaghetti code. To change data in this calculator, navigate to its appropriate page: e.g. Module:Skill calc/SKILLNAME/data Follow the guides on that page and try to emulate the items around it. --]=] -- <nowiki> local p = {} -- done local commas = require('Module:Addcommas')._add local tables = require('Module:Tables') local range = require('Module:Enum').range --todo local coins = require('Module:Coins')._amount local gePrice = require('Module:GETotal')._quantity local numbers = require('Module:Number')._round local level = require('Module:Experience').level_at_xp local xp = require('Module:Experience').xp_at_level -- This houses most of the processing power local eltGenerator = require('Module:Skill_calc/eltGenerator') local bonusGenerator = require('Module:Skill_calc/bonusGenerator') function p.noValue(frame) local args = frame:getParent().args local pctExpBoost = 0 -- Account for outfits, avatar, tools, etc local flatExpBoost = 0 -- Account for flat experience boosts local currLv, goalLv, currXP, goalXP, remaining local links,elts local prayerBoost -- This is needed to account for popular methods local message, testMessage if args.testing == "active" then testMessage = "This calculator is being used to test new features." end -- These sub-sections have different table elts than their parent skill local exceptions = {"Agility-Other", "Divination-Boons", "Divination-Dungeoneering", "Divination-Hall of Memories", "Divination-Hall of Memories (2 tick)", "Divination-Memory-storage bots", "Divination-Other", "Farming-Curing Animals", "Farming-Raising Animals", "Farming-Manure", "Farming-Testing", "Firemaking-Barbarian", "Firemaking-Char", "Firemaking-Other", "Firemaking-Incense Sticks (scratch + ashing)", "Prayer-Incense sticks", "Fishing-Dungeoneering", "Flatpacks", "Forging", "Invention-Manufacturing", "Invention-Manufacturing - Ancient", "Masters", "Milestones", "Mining-Gems", "Multiples", "Rooms", "Runespan - Free", "Runespan - Members", "Scrolls", "Summoning-Pets", "Summoning-Dungeoneering - Pouches", "Slayer-Assignments", "Tiaras", "Thieving-Sorceress Garden", "Thieving-Safes", "Woodcutting-Other"} local exceptionsAgility = {"Milestones", "Multiples", "Other"} local exceptionsDiv = {"Conversion", "Conversion /w Energy", "Divine locations", "Milestones", "Signs and Portents", "Transmutation"} local exceptionsSlayer = {"Items", "Masters", "Milestones"} local exceptionsSmithing = {"Kethsian", "Masterwork (unfinished)", "Obsidian"} local bonusExceptions = {"Urns", "Dungeoneering", "Blast furnace"} -- These skills have no special considerations in Dungeoneering local basicDungeons = {"Mining", "Woodcutting"} -- elite skills local eliteSkills = { ['Invention'] = true } local isEliteSkill = eliteSkills[args.skill] or false -- Hold bonuses and boosts local bonusPct = pctExpBoost -- Gather all relative experience boosts to find new base experience if not (args.avatar == nil) and not (args.disp == "Urns") then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, object = "avatar", pieces = tonumber(args.avatar) } end -- Only elite that gives xp boost is dungeoneering if not (args.elite == nil) and not (args.disp == "Urns") and args.skill == "Dungeoneering" then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, object = "elite", pieces = tonumber(args.elite) } end -- Some bonuses can not be used on some sub-sections (E.G. Dungeoneering) if not findItem(bonusExceptions, args.disp) then if not (args.abyss == nil) and not (args.abyss == 'No') then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, object = "abyss", item = args.abyss } end if not (args.extra == nil) and args.abyss == nil then if not (args.skill == "Divination" and args.disp == "Harvest") then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, category = args.disp, object = "extra", item = args.extra } end end if not (args.outfit == nil) then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, object = "outfit", pieces = tonumber(args.outfit) } end if not (args.portable == nil) and (args.portable == "Yes") then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, object = "portable", pieces = 1 } end if not (args.tool == nil) then flatExpBoost = flatExpBoost + bonusGenerator { skill = args.skill, object = "tool", item = args.tool } end --[=[ -- Anachronia spa if not (args.spa == nil) and args.disp == "Courses" then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, category = args.disp, object = "spa", item = args.spa } end --]=] if not (tonumber(args.custom) == nil) then if args.abyss == nil or args.abyss == 'No' then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, category = args.disp, object = "custom", item = args.custom } end end if not (args.wild == nil) then pctExpBoost = pctExpBoost + bonusGenerator { skill = args.skill, object = "wild", item = args.wild } end --[=[ if not (args.age == nil) and args.disp == "Curing Animals" then pctExpBoost = bonusGenerator { skill = args.skill, category = args.disp, object = "age", item = args.age } end --]=] end -- Translate goals into experience comparisons -- Calculate iterations to goal currLv, currXP, goalLv, goalXP, remaining = remainingExp(args.current, args.goal, args.currToggle, args.goalToggle, isEliteSkill) -- Prevent the Wilderness Agility Course with no Demonic Skull bonus from displaying a table (same XP for every level) --if args.disp == "Wilderness Agility Course" and (args.wild == "No" or args.wild == "Wilderness Sword 2+") then if (args.testing == "active" and args.disp == "Wilderness Agility Course") then if (args.wild == "No" or args.wild == "Wilderness Sword 2+") then local message = wildernessAC(currLv, goalLv, currXP, goalXP, remaining, pctExpBoost) local msgRet = mw.html.create('div'):css({['font-size'] = "16px", ['font-weight'] = "normal"}):wikitext(message) return tostring(msgRet) end elseif args.disp == "Wilderness Agility Course" then local message = wildernessAC(currLv, goalLv, currXP, goalXP, remaining, pctExpBoost) local msgRet = mw.html.create('div'):css({['font-size'] = "16px", ['font-weight'] = "normal"}):wikitext(message) return tostring(msgRet) end -- Hold skill-related data local dataRet, data -- Grab Sub-Category Table Data if args.skill == "Slayer" and args.disp == "Assignments" then dataRet = require('Module:Skill calc/Slayer/Assignments/data') data = dataRet(args.disp,args.creature) elseif args.skill == "Smithing" and args.disp == "Equipment" then dataRet = require('Module:Skill calc/' .. args.skill .. '/data') data = dataRet(args.equipment) else dataRet = require('Module:Skill calc/' .. args.skill .. '/data') data = dataRet(args.disp) end table.sort(data, function(a,b) return sortTable(a,b) end ) local ret = mw.html.create('table'):addClass('wikitable sortable sticky-header') --[=[ -- Find columns from pool -- Some require specific parameters due to common phrases --]=] local eltsRet = require('Module:Skill calc/elts') elts = eltsRet[args.skill] -- Filter out exceptions (re-organized to allow skill-specific filters to catch some exceptions early) if args.skill == "Agility" and findItem(exceptionsAgility, args.disp) then elts = eltsRet[args.skill .. "-Basic"] elseif args.skill == "Construction" and args.disp == "Other" then elts = eltsRet[args.skill .. "-Other"] elseif args.skill == "Divination" and findItem(exceptionsDiv, args.disp) then elts = eltsRet[args.skill .. "-Profit"] elseif args.skill == "Slayer" and findItem(exceptionsSlayer, args.disp) then elts = eltsRet[args.skill .. "-Basic"] elseif args.skill == "Smithing" and args.disp == "Equipment" and findItem(exceptionsSmithing, args.equipment) then elts = eltsRet["Smithing-Equipment"] elseif findItem(exceptions, args.disp) or findItem(exceptions, args.skill .. '-' .. args.disp) then elts = eltsRet[args.skill .. "-" .. args.disp] -- All urns share the same format; special exception elseif args.disp == "Urns" then elts = eltsRet[args.disp] -- Cooking in Daemonheim elseif args.skill == "Cooking" and args.disp == "Dungeoneering" then elts = eltsRet["Cooking-Dungeoneering"] -- Basic dungeon format elseif args.disp == "Dungeons" then elts = eltsRet["Dungeons"] -- Dungeons with materials elseif string.find(args.disp, "Dungeoneering - ") then elts = eltsRet["Dungeons-Materials"] -- Hunter methods with bait elseif args.skill == "Hunter" and (args.disp == "Nets and Sprites" or args.disp == "Deadfall and Pitfall" or args.disp == "Box Trapping") then elts = eltsRet["Hunter-Bait"] elseif args.skill == "Hunter" and args.disp == "Big Game Hunting" then elts = eltsRet["Hunter-BGH"] elseif args.testing == "active" and (args.skill == "Hunter" and args.disp == "Box Trapping") then elts = eltsRet["Hunter-Anachronia"] elseif args.skill == "Fishing" and args.disp == "Deep Sea" then elts = eltsRet["Fishing-Deep Sea"] end tables._row(ret:tag('tr'), elts, true) for _, v in ipairs(data) do --Leave common calculations outside of the function calls --Material count local mcount = 1 if v.mcount then mcount = v.mcount end --Get total cost of materials local cost = 0 local productCost = 0 if v.material then if ((v.mtrade ~= 0) or ( (args.disp == "Urns") and not (v.currency) ) ) then cost = gePrice(v.material, mcount) end elseif (args.skill == "Prayer" and args.disp ~= 'Incense sticks') then if not (v.currency) then cost = gePrice({1, v.name}, 1) else cost = v.value end elseif args.disp == "Scrolls" then cost = gePrice({1, v.familiarIcon}, 1) end if v.trade ~= 0 and not v.currency and (args.skill ~= "Agility" and args.skill ~= "Construction" and args.skill ~= "Farming" and args.skill ~= "Dungeoneering" and not ( args.skill == "Prayer" and args.disp ~= 'Incense sticks' ) and args.skill ~= "Invention" and args.skill ~= "Slayer") then if (args.skill == "Woodcutting" or args.skill == "Mining" or args.skill == "Runecrafting" or args.skill == "Divination") and not (v.icon == nil) then productCost = gePrice({1, v.icon}, 1) elseif args.skill == "Hunter" then if v.product then productCost = gePrice({1, v.product}, 1) end else productCost = gePrice({1, v.name}, 1) end end -- If a multiplier is set, it is applied to the product's value for profit calculations if v.multiplier then productCost = productCost * v.multiplier end -- Check for other currencies if (v.currency or v.currency2) and not (args.skill == "Prayer" and args.disp ~= 'Incense sticks') then productCost = v.value if v.materialCost then cost = v.materialCost end end -- Brewing makes two batches if args.disp == "Brewing" then productCost = productCost * 2 end --Establish any experience boosts local abyss = false if not (args.abyss == nil) and (args.abyss == "Yes" or args.abyss == "Demonic Skull") then abyss = true end local unitExp = calculateBonus { base = v.xp, currLv = currLv, boost = pctExpBoost, boostSw = pctExpBoostSw, flatBoost = flatExpBoost, abyss = abyss, settings = args, item = v } -- Calculate needed iterations local needed if not (unitExp == 0 or unitExp == nil) then needed = tonumber(math.ceil(remaining / unitExp)) else needed = 0 end if ( args.skill == "Fletching" and string.find(args.disp, "Darts") ) then if v.name == "Prototype Throwing Dart" then needed = 0 local diffLv = goalLv - currLv local remainingLevels = range(currLv, goalLv) for j,w in ipairs( remainingLevels ) do if w == currLv then local lvlXPdiff if diffLv > 0 then lvlXPdiff = xp{ args = { w + 1 } } - currXP else lvlXPdiff = goalXP - currXP end needed = needed + lvlXPdiff / ( v.xp * w ) elseif remainingLevels[j+1] then local lvlXPdiff = xp{ args = { w + 1 } } - xp{ args = { w } } needed = needed + lvlXPdiff / ( v.xp * w ) else local lvlXPdiff = goalXP - xp{ args = { w } } needed = needed + lvlXPdiff / ( v.xp * w ) end end needed = math.ceil(needed) end end -- Keep this as the first check to prevent double generation if (args.testing == "active") then -- Pass the current elts as a variable for elt generation generatedElts = eltGenerator.generate_elts( { args = { v, args, unitExp, needed, remaining, cost, productCost, elts, currLv } }) -- No Profit, No Loss skills else if (args.skill == "Agility" or args.skill == "Thieving" or args.skill == "Slayer" or args.skill == "Invention") then elts = eltGenerator.generate_NoProfitNoLoss({args = {v,unitExp,needed,args,remaining,currLv}}) -- No Loss, Profit skills (Gathering) elseif (args.skill == "Mining" or args.skill == "Fishing" or args.skill == "Woodcutting" or args.skill == "Runecrafting" or args.skill == "Divination" or args.skill == "Hunter") then elts = eltGenerator.generate_ProfitNoLoss({args = {v,unitExp,needed,cost,args,productCost,remaining,currLv}}) -- No Profit, Loss skills (Survival) elseif ( (args.skill == "Firemaking" and not string.find(args.disp, 'Incense') ) or (args.skill == "Prayer" and args.disp ~= 'Incense sticks') or args.skill == "Construction" or args.skill == "Magic") then elts = eltGenerator.generate_NoProfitLoss({args = {v,unitExp,needed,cost,args,productCost,remaining,currLv}}) -- Profit and Loss skills (Artisan) -- Fletching, Cooking, Farming, Smithing, Herblore, Summoning else elts = eltGenerator.generate_ProfitLoss({args = {v,unitExp,needed,cost,args,productCost,remaining,currLv}}) end end -- Allow for items with no level requirement local levelRequired = 1 if args.skill == "Slayer" and (args.disp == "Assignments" or args.disp == "Monsters") then if v.level2 then levelRequired = v.level2 end else if v.level then levelRequired = v.level end end levelRequired = math.floor(levelRequired) local class = 'table-bg-yellow sortbottom' if levelRequired > goalLv then class = 'table-bg-red sortbottom' elseif levelRequired <= currLv then class = 'table-bg-green' end if args.testing == "active" then tables._row(ret:tag('tr'):addClass(class), generatedElts, false) else tables._row(ret:tag('tr'):addClass(class), elts, false) end end message = displayExp{display=args.disp, skill=args.skill, remaining=remaining, goalLv=goalLv, goalXP=goalXP, currLv = currLv, currXP = currXP} if (args.testing == "active") then local testNotice = mw.html.create('div'):css({['font-size'] = "16px", ['font-weight'] = "bold"}):wikitext(testMessage) return tostring(testNotice) .. tostring(message) .. tostring(ret) else return tostring(message) .. tostring(ret) end end --[=[ displayExp -- Creates a text string output for the goal calculations -- Inputs: -- params Incoming parameters to generate string -- - display Current set of sub-categorical data -- - skill Current skill -- - remaining Experience needed for goal -- - goalXP Expected experience -- - goalLv Expected level -- - currXP Current experience -- - currLv Current level -- Returns: -- msg String created from params -- - this may be appended with a warning specifically for flatpacks --]=] function displayExp(params) local msg -- Converted from message to avoid conflict local display = params.display local skill = params.skill local remaining = params.remaining local goalXP = params.goalXP local goalLv = params.goalLv local currXP = params.currXP local currLv = params.currLv msg = "To train " .. skill .. " from " .. commas(currXP) .. " experience (level " .. currLv .. ") to " .. commas(goalXP) .. " experience (level " .. goalLv .. "), " .. commas(remaining) .. " experience is required." if display == "Flatpacks" then msg = msg .. "<div style='color:red; font-size:0.9em;'>Levels refer to the minimum needed to use the associated workbench if otherwise lower.</div>" elseif display == "Boons" then msg = msg .. "<div style='color:red; font-size:0.9em;'>Each boon can only be made once.</div>" end local ret = mw.html.create('div'):css({['font-size'] = "1.1em", ['font-weight'] = "bold"}):wikitext(msg) return tostring(ret) end --[=[ remainingExp -- Finds and returns experiences and levels based on inputs -- Inputs: -- curr current value -- goal goal value -- curr_intent what the current is (level/experience) -- goal_intent what the goal is (level/experience) -- Returns: -- current level, -- current experience, -- goal level, -- goal experience, -- experience remaining --]=] function remainingExp(curr, goal, curr_intent, goal_intent, isElite) local goalLevel, currLevel, goalXP, currXP local elite = nil if isElite then elite = '1' end if curr_intent == "Level" and tonumber(curr) <= 120 then currLevel = tonumber(curr) currXP = xp({args = {curr, elite = elite}}) else currLevel = level({args = {curr, elite = elite}}) currXP = tonumber(curr) end if goal_intent == "Level" and tonumber(goal) <= 120 then goalLevel = tonumber(goal) goalXP = xp({args = {goal, elite = elite}}) else goalLevel = level({args = {goal, elite = elite}}) goalXP = tonumber(goal) end -- Prevent negative values local remaining = math.ceil(goalXP - currXP) if remaining < 0 then remaining = 0 end return currLevel, currXP, goalLevel, goalXP, remaining end --[=[ calculateBonus -- Inputs: -- source Incoming data -- - base Base experience for item -- - boost Percent experience boost, expressed as a decimal percentage -- - ava Avatar bonus -- - outfit Outfit bonus -- - tools Extra bonuses -- - flatBoost Flat experience boost -- Returns: -- Numeric value of new base experience including bonuses --]=] function calculateBonus(source) local total = source.base -- base experience local currLv = source.currLv local boost = source.boost -- bonus percentage local boostSw = source.boostSw -- value not being set? local flatBoost = source.flatBoost local abyss = source.abyss local settings = source.settings -- calculator local item = source.item -- ../data local cLv -- holder for current level if settings.disp == "Wilderness Agility Course" or (item.page == "Wilderness Agility Course" and settings.disp ~= "Milestones") then if (settings.disp == "Wilderness Agility Course") then cLv = item.level else cLv = currLv end if settings.wild == "Demonic Skull" then total = total + 498.9 if (cLv > 50) then total = (((cLv-21)*boost)*(total)) end elseif settings.wild == "Both" then total = ((total*((cLv-21)*boost))+((((cLv-21)*boost)+0.05)*498.9)) --total = ((total*((cLv-21)*boost))+((((cLv-21)*boost)+boostSw)*498.9)) elseif settings.wild == "Wilderness Sword 2+" then --total = total + (498.9*(1+boostSw)) total = total + (498.9*1.05) -- 1+0.05 else total = total + 498.9 end --elseif settings.skill == "Agility" and item.name == "Anachronia Agility Course" then --total = total * (1 + boost) elseif item.skill == "Firemaking" and item.bonus ~= nil then total = total + (item.bonus * (1 + boost)) --elseif settings.skill == "Farming" and settings.disp == "Curing Animals" then --total = math.floor((total * source.boost)*10)/10 --total = total * source.boost else total = total * (1 + source.boost) end if not (settings == nil) then -- Check for additional modifiers. These must be done on an item to item basis -- to filter out items that may not be affected by certain boosts local potionSetting, vosSetting, auraSetting, aotSetting, altarSetting if settings.potion then potionSetting = settings.potion end if settings.vos then if settings.vos == "Yes" then vosSetting = "VoiceOfSeren" else vosSetting = "No" end end if settings.aura then auraSetting = settings.aura end if settings.aot then if settings.aot == "Yes" then aotSetting = "AvgOverTime" else aotSetting = "No" end end if settings.altar and item.name ~= "Cleansing crystal" then altarSetting = settings.altar else altarSetting = "None" end -- Check for JuJu potion modifier if potionSetting ~= nil then total = total * bonusGenerator { skill = settings.skill, name = item.name, object = "potion", item = potionSetting, setting = vosSetting, subSetting = aotSetting } end -- Check for VoS modifier if vosSetting ~= "No" then total = total * bonusGenerator { skill = settings.skill, name = item.name, object = "VoiceOfSeren", setting = potionSetting, subSetting = aotSetting } end -- Check for aura modifier if auraSetting ~= "No" then total = total * bonusGenerator { skill = settings.skill, name = item.name, object = "aura", item = auraSetting } end -- Check for altar modifier if altarSetting ~= "None" then total = total * bonusGenerator { skill = settings.skill, object = "altar", item = altarSetting, setting = settings.disp } end end -- If the abyss is active, do not add 1 -- Not sure how it all stacks :: NEED INPUT if source.abyss == true then total = source.base * source.boost end total = total + source.flatBoost return numbers(total,1) end function wildernessAC(currLv, goalLv, currXP, goalXP, remaining, pctExpBoost) local nXP = numbers(571.4 * (1 + pctExpBoost), 1) local lapsToDo = numbers(remaining / nXP) return "To train from '''" .. commas(currXP) .. " experience''' (level " .. currLv .. ") to '''" .. commas(goalXP) .. " experience''' (level " .. goalLv .. "), '''" .. commas(remaining) .. " experience''' is needed.<br/>This requires '''" .. commas(lapsToDo) .. " laps''' to be completed at '''" .. nXP .. " XP''' per lap." end -- Make it easier to find items in a set -- A little heavy : if someone can find a better way, replace. function findItem (list, item) local status = false for _,v in pairs(list) do if v == item then status = true break end end return status end function sortTable(a, b) local value = false if a.level and b.level and a.level ~= b.level then value = a.level < b.level elseif a.name and b.name and a.name == b.name and (a.title or b.title) then if a.title and not b.title then value = a.title < b.name elseif not a.title and b.title then value = a.name < b.title else value = a.title < b.title end else value = a.xp < b.xp end return value end --[[ Modified version of GETotal. Will add together the price of a * 4 and b / 4 Can recognize and parse vulgar fractions All fractions and decimals will be truncated off the final price Technically unlimited To use: variable 'a' = array of {quantity, value, "item name", etc...} variable 'b' = number of unique items to be included --]] function getPrice(a,b) local values = a or {} local count = b or 0 local price = 0 local prices = {} for i=1 , (count*3) , 3 do local valuex = a[i+1] local itemx = a[i+2] if itemx then local qtyx = a[i] or 1 local qtyret = tonumber(qtyx) or frac(qtyx) or 1 table.insert(prices,math.floor(valuex * qtyret)) end end for _, v in ipairs(prices) do price = price + v end return price end return p
Summary:
Please note that all contributions to Brighter Shores Wiki are considered to be released under the CC BY-NC-SA 3.0 (see
Brighter Shores:Copyrights
for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource.
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Preview page with this template
Templates used on this page:
Template:DependencyList
(
edit
)
Template:Documentation
(
edit
)
Template:Extension DPL
(
edit
)
Template:Invokes
(
edit
)
Template:Invokes/doc
(
edit
)
Template:No documentation
(
edit
)
Template:PageType
(
edit
)
Module:Addcommas
(
edit
)
Module:Array
(
edit
)
Module:DPLlua
(
edit
)
Module:DependencyList
(
edit
)
Module:Documentation
(
edit
)
Module:Enum
(
edit
)
Module:Paramtest
(
edit
)
Module:Skill calc
(
edit
)
Module:Skill calc/doc
(
edit
)
Module:Tables
(
edit
)
Module:Tooltip
(
edit
)
Module:Yesno
(
view source
) (semi-protected)
This page is a member of a hidden category:
Category:Pages using DynamicPageList3 parser function