Anonymous

Module:Constants: Difference between revisions

From Melvor Idle
Updated Arch Tool Level display, Fixed error on nil modifiers in getModifiersDifference
(added getSlayerTierName)
(Updated Arch Tool Level display, Fixed error on nil modifiers in getModifiersDifference)
 
(136 intermediate revisions by 7 users not shown)
Line 1: Line 1:
local p = {}
local p = {}
local ConstantData = mw.loadData('Module:Constants/data')
local ItemData = mw.loadData('Module:Items/data')


local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')


--Just hardcoding these because I guess that's where we're at
--Just hardcoding these because I guess that's where we're at
--getModifierSkills still needs skills, otherwise this can be removed
local modifierTypes = {
local modifierTypes = {
  ["increasedGlobalAccuracy"] = { text =  "+{V}% Global Accuracy", },
["MeleeStrengthBonus"] = { text = "{V}% Melee Strength Bonus from Equipment", skills = {'Combat'} },
  ["increasedMeleeAccuracyBonus"] = { text =  "+{V}% Melee Accuracy Bonus", },
["DamageToDungeonMonsters"] = { text = "{V}% Damage To Dungeon Monsters", skills = {'Combat'} },
  ["increasedMeleeStrengthBonus"] = { text =  "+{V}% Melee Strength Bonus", },
["GlobalMasteryXP"] = { text = "{V}% Global Mastery XP", skills = {'Woodcutting', 'Fishing', 'Firemaking', 'Cooking', 'Mining', 'Smithing', 'Thieving', 'Farming', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Agility', 'Summoning', 'Astrology', 'Archaeology'} },
  ["increasedMeleeEvasion"] = { text =  "+{V}% Melee Evasion", },
["ChanceRandomPotionHerblore"] = { text = "{V}% chance to gain a second potion of a random tier", skills = {'Herblore'} },
  ["increasedRangedAccuracyBonus"] = { text =  "+{V}% Ranged Accuracy Bonus", },
["FlatPrayerCostReduction"] = { text = "{V} Prayer Point Cost for Prayers (Prayer Point cost cannot go below 1)", inverseSign = true, skills = {'Prayer'} },
  ["increasedRangedStrengthBonus"] = { text =  "+{V}% Ranged Strength Bonus", },
["MinEarthSpellDmg"] = { text = "{VX} Min Earth Spell Dmg", skills = {'Magic'} },
  ["increasedRangedEvasion"] = { text =  "+{V}% Ranged Evasion", },
["SlayerTaskLength"] = { text = "{V}% Slayer Task Length/Qty", skills = {'Slayer'} },
 
["ChanceToDoubleLootCombat"] = { text = "{V}% Chance To Double Loot in Combat", skills = {'Combat'} },
["GPFromAgility"] = { text = "{V}% GP From Agility", skills = {'Agility'} },
["SkillXP"] = { text = "{V}% {SV0} Skill XP" },
["MiningNodeHP"] = { text = "{V} Mining Node HP", skills = {'Mining'} },
["FarmingYield"] = { text = "{V}% Farming Yield", skills = {'Farming'} },
["GPFromMonstersFlat"] = { text = "{V} GP From Monsters", skills = {'Combat'} },
["GlobalPreservationChance"] = { text = "{V}% Chance to Preserve Resources in Skills" },
["RunePreservation"] = { text = "{V}% Rune Preservation", skills = {'Magic'} },
["MaxHitpoints"] = { text = "{VX} Maximum Hitpoints", skills = {'Combat', 'Hitpoints'} },
["ChanceToDoubleItemsSkill"] = { text = "{V}% Chance to Double Items in {SV0}" },
["autoSlayerUnlocked"] = { text = "{V} Auto Slayer Unlocked", skills = {'Slayer'} },
["HitpointRegeneration"] = { text = "{V}% Hitpoint Regeneration", skills = {'Combat', 'Hitpoints'} },
["PotionChargesFlat"] = { text = "{V} Charges per Potion" },
["SkillInterval"] = { text = "{VMS}s {SV0} Interval", isIncreaseNegative = true, isSkill = true },
["BankSpace"] = { text = "{V} Bank Space" },
["MinHitBasedOnMaxHit"] = { text = "{V}% of Maximum Hit added to Minimum Hit", skills = {'Combat'} },
["
}
}
function p.getTriangleAttribute(attribute, attackerStyle, targetStyle, modeName)
if type(attribute) ~= 'string' then
error("Parameter 'attribute' must be a string", 2)
elseif type(attackerStyle) ~= 'string' then
error("Parameter 'attackerStyle' must be a string", 2)
elseif type(targetStyle) ~= 'string' then
error("Parameter 'targetStyle' must be a string", 2)
elseif type(modeName) ~= 'string' then
error("Parameter 'modeName' must be a string", 2)
end
local mode = GameData.getEntityByName('gamemodes', modeName)
if mode == nil then
error("Invalid gamemode '" .. modeName .. "'", 2)
end
local attStyle, targStyle = string.lower(attackerStyle), string.lower(targetStyle)
local validStyles = { 'magic', 'melee', 'ranged' }
if not Shared.contains(validStyles, string.lower(attStyle)) then
error("Invalid value for parameter 'attackerStyle'", 2)
elseif not Shared.contains(validStyles, string.lower(targStyle)) then
error("Invalid value for parameter 'targetStyle'", 2)
end
local combatTriangle = GameData.getEntityByID('combatTriangles', mode.combatTriangle)
if combatTriangle == nil then
error("No such combat triangle: " .. mode.combatTriangle)
end
local attrData = combatTriangle[attribute]
if attrData == nil then
error("No such attribute: " .. attribute)
else
return attrData[attStyle][targStyle]
end
end
function p.getTriangleDamageModifier(attackerStyle, targetStyle, mode)
return p.getTriangleAttribute('damageModifier', attackerStyle, targetStyle, mode)
end
--Syntax is like p.getTriangleDRModifier('Melee', 'Ranged', 'Normal')
--Returns a multiplier that can be multiplied with base DR to get the post-Triangle result
function p.getTriangleDRModifier(attackerStyle, targetStyle, mode)
return p.getTriangleAttribute('reductionModifier', attackerStyle, targetStyle, mode)
end
function p.getDifficultyString(difficulty)
return GameData.rawData.combatAreaDifficulties[difficulty + 1]
end


function p.getSkillName(skillID)
function p.getSkillName(skillID)
  for skName, ID in Shared.skpairs(ConstantData.skill) do
local skill = GameData.getSkillData(skillID)
    if ID == skillID then
if skill ~= nil then
      return skName
return skill.name
    end
end
  end
  return nil
end
end


function p.getSkillID(skillName)
function p.getSkillID(skillName)
  return ConstantData.skill[skillName]
for i, skillData in ipairs(GameData.rawData.skillData) do
if skillData.data.name == skillName then
return skillData.skillID
end
end
end
end


function p.getEquipmentSlotName(id)
function p.getEquipmentSlotName(id)
  for slotName, i in Shared.skpairs(ConstantData.equipmentSlot) do
local slotData = GameData.getEntityByID('equipmentSlots', id)
    if i == id then
if slotData ~= nil then
      return slotName
return slotData.name
    end
end
  end
end
  return 'Invalid'
 
function p.getEquipmentSlotID(name)
local slotData = GameData.getEntityByName('equipmentSlots', name)
if slotData ~= nil then
return slotData.id
end
end
 
function p.getCombatStyleName(styleNum)
if type(styleNum) == 'number' then
local styleName = GameData.rawData.attackTypes[styleNum]
if styleName ~= nil then
return Shared.titleCase(styleName)
end
elseif type(styleNum) == 'string' and type(GameData.rawData.attackTypes[string.lower(styleNum)]) == 'number' then
return Shared.titleCase(styleNum)
end
return Shared.printError('Invalid combat style')
end
 
 
--- Slayer functions
--
function p.getSlayerTierByID(tierID)
if type(tierID) ~= 'number' then
return nil
else
return GameData.rawData.slayerTiers[tierID + 1]
end
end
end


function p.getSlayerTierName(tier)
function p.getSlayerTier(name)
  for name, num in Shared.skpairs(ConstantData.slayerTier) do
return GameData.getEntityByProperty('slayerTiers', 'display', name)
    if num == tier then
end
      return name
 
    end
function p.getSlayerTierByLevel(level)
  end
if type(level) ~= 'number' or level < 1 then
  return "ERROR: Invalid Slayer tier[[Category:Pages with script errors]]"
return Shared.printError('Invalid Slayer level')
end
for i, tier in ipairs(GameData.rawData.slayerTiers) do
if tier.minLevel <= level and (tier.maxLevel == nil or tier.maxLevel >= level) then
return tier
end
end
end
 
--
-- the following functions just return subsets of the slayer functions above
--
 
function p.getSlayerTierName(tierID)
if type(tierID) == 'number' then
local tier = p.getSlayerTierByID(tierID)
if tier ~= nil then
return tier.display
end
end
return Shared.printError('Invalid Slayer tier')
end
 
function p.getSlayerTierNameByLevel(lvl)
local tier = p.getSlayerTierByLevel(lvl)
if type(tier) == 'table' then
return tier.display
else
return Shared.printError('Invalid Slayer tier')
end
end
 
--
--- End of slayer functions
 
--Turns a modifier name like 'increasedHPRegenFlat' into several pieces of data:
--Base Name, Description, IsNegative, and modifyValue
--ex. "HPRegenFlat", "+${value} Flat Hitpoints Regeneration", false, "multiplyByNumberMultiplier"
function p.getModifierDetails(modifierName)
local baseName = modifierName
local modifier = GameData.rawData.modifierData[modifierName]
 
if modifier == nil then
return nil
end
 
if Shared.startsWith(modifierName, "increased") or Shared.startsWith(modifierName, "decreased") then
baseName = string.sub(modifierName, 10)
end
 
return baseName, modifier.description, modifier.isNegative, modifier.modifyValue
end
end


function p._getModifierText(modifier, value, doColor)
function p._getModifierText(modifier, value, doColor)
  if doColor == nil then doColor = true end
if doColor == nil then doColor = true end
  local template = modifierTypes[modifier]
local modName, modText, isNegative, modifyValue = p.getModifierDetails(modifier)


  if template == nil then
if modName == nil then
    return 'ERROR: Invalid modifier type [[Category:Pages with script errors]]'
return Shared.printError('Invalid modifier type for "' .. modifier .. '"')
  end
end


  local result = template.text
if modifyValue ~= nil and string.match(modifyValue, 'ToolLevels') then
modifyValue = 'ArchaeologyToolLevels'
end


  if type(value) == 'table' then
local formatModValue = function(value, rule)
    if Shared.tableCount(value) > 0 and type(value[1]) == 'table' then
local ruleFunctions = {
      --Potentially return multiple rows if necessay
['value'] = function(val) return val end,
      local resultArray = {}
['multiplyByNumberMultiplier'] = function(val) return val * 10 end,
      for i, subVal in Shared.skpairs(value) do
['divideByNumberMultiplier'] = function(val) return val / 10 end,
        table.insert(resultArray, p._getModifierText(modifier, subVal, doColor))
['milliToSeconds'] = function(val) return val / 1000 end,
      end
['(value)=>value*100'] = function(val) return val * 100 end,
      return table.concat(resultArray, '<br/>')
['(value)=>100+value'] = function(val) return val + 100 end,
    else
['(value)=>value+1'] = function(val) return val + 1 end,
      if value[1] ~= nil then
['(value)=>Math.pow(2,value)'] = function(val) return 2^val end,
        local skillName = p.getSkillName(value[1])
["(value)=>{if(value>=2){return getLangString('ALLOW_UNHOLY_PRAYERS');}\nelse if(value>=1){return getLangString('ALLOW_UNHOLY_PRAYERS_WITH_EQUIPMENT');}\nelse{return 'Invalid modifier value.';}}"] = function(val) return 'Allows for Unholy Prayers to be used' end,
        if skillName ~= nil then
['ArchaeologyToolLevels'] = function(val)
          result = string.gsub(result, '{SV0}', p.getSkillName(value[1]))
local toolLevel = '+' .. val
        end
if string.match(modName, 'Sieve') then
      end
toolLevel = toolLevel ..  ' level of the Sieve Tool in Archaeology'
      if value[2] ~= nil then
elseif string.match(modName, 'Trowel') then
        result = string.gsub(result, '{V1}', value[2])
toolLevel = toolLevel ..  ' level of the Trowel Tool in Archaeology'
        result = string.gsub(result, '{VMS1}', value[2] / 1000)
elseif string.match(modName, 'Brush') then
      end
toolLevel = toolLevel ..  ' level of the Brush Tool in Archaeology'
    end
elseif string.match(modName, 'Shovel') then
  else
toolLevel = toolLevel .. ' level of the Shovel Tool in Archaeology'
    if string.find(result, '{IV}', 1, true) ~= nil and tonumber(value) ~= nil then
end
      local item = ItemData.Items[tonumber(value) + 1]
if val > 1 then
      if item ~= nil then
return string.gsub(toolLevel, 'level', 'levels')
        result = string.gsub(result, '{IV}', item.name)
else
      end
return toolLevel
    end
end
    result = string.gsub(result, '{V}', value)
end,
    result = string.gsub(result, '{VMS}', value / 1000)
['(value)=>game.golbinRaid.startingWeapons[value].name'] = function(val)
    result = string.gsub(result, '{VX}', value * 10)
-- For golbin raid starting weapons
  end
local startingWeapons = { 'melvorD:Bronze_Scimitar', 'melvorD:Adamant_Scimitar' }
local itemID = startingWeapons[val + 1]
local item = GameData.getEntityByID('items', itemID)
if item ~= nil then
return item.name
else
return 'Unknown'
end
end
}
local ruleFunc = ruleFunctions[modifyValue] or ruleFunctions['value']


  if doColor then
if type(value) == 'table' then
    if template.isNegative ~= nil and template.isNegative then
-- If table is a pair of values then format both & add a separator
      result = '<span style="color:red">'..result..'</span>'
return ruleFunc(value[1]) .. '-' .. ruleFunc(value[2])
    else
else
      result = '<span style="color:green">'..result..'</span>'
return ruleFunc(value)
    end
end
  end
end


  return result
local valueArray, resultArray = nil, {}
if type(value) ~= 'table' then
valueArray = {value}
else
valueArray = value
end
 
for i, subVal in ipairs(valueArray) do
local resultText = modText
-- A few modifiers don't have official descriptions; Fallback to manual ones instead
if string.match(resultText, 'UNDEFINED TRANSLATION') then
resultText = modifierTypes[modName].text
end
local modMagnitude = nil
if type(subVal) == 'table' and subVal.skillID ~= nil then
-- Modifier value is skill specific
modMagnitude = subVal.value
local skillName = p.getSkillName(subVal.skillID)
if skillName ~= nil then
resultText = string.gsub(resultText, '${skillName}', skillName)
end
else
-- Modifier value is the magnitude only
modMagnitude = subVal
end
 
resultText = string.gsub(resultText, '${value}', function(rule) return (formatModValue(modMagnitude, rule) or '') end)
 
if doColor then
local colorCode = (isNegative ~= nil and isNegative and 'color:red' or 'color:green')
resultText = '<span style="' .. colorCode .. '">' .. resultText .. '</span>'
end
table.insert(resultArray, resultText)
end
 
return table.concat(resultArray, '<br/>')
end
end


function p.getModifierText(frame)
function p.getModifierText(frame)
  local modifier = frame.args ~= nil and frame.args[1] or frame[1]
local modifier = frame.args ~= nil and frame.args[1] or frame[1]
  local value = frame.args ~= nil and frame.args[2] or frame[2]
local value = frame.args ~= nil and frame.args[2] or frame[2]
  local skill = frame.args ~= nil and frame.args.skill or frame.skill
local skill = frame.args ~= nil and frame.args.skill or frame.skill
  local doColor = frame.args ~= nil and frame.args[3] or frame[3]
local doColor = frame.args ~= nil and frame.args[3] or frame[3]
 
if doColor ~= nil then
doColor = string.upper(doColor) ~= 'FALSE'
end
 
if skill ~= nil and skill ~= '' then
value = { { ["skillID"] = p.getSkillID(skill), ["value"] = value } }
end
 
return p._getModifierText(modifier, value, doColor)
end


  if doColor ~= nil then
function p.getModifiersText(modifiers, doColor, inline, maxVisible)
    doColor = string.upper(doColor) ~= 'FALSE'
if inline == nil then inline = false end
  end
if type(maxVisible) ~= 'number' then maxVisible = nil end
if modifiers == nil or Shared.tableIsEmpty(modifiers) then
return ''
end


  if skill ~= nil and skill ~= '' then
local modArray = { ["visible"] = {}, ["overflow"] = {} }
    value = {p.getSkillID(skill), value}
local modCount = { ["visible"] = 0, ["overflow"] = 0 }
  end
local insertKey = 'visible'
for bonus, value in pairs(modifiers) do
-- If there is a single by skill modifier with multiple values, this will
-- appear as multiple rows. Split these so the number of visible lines is
-- always accurate
local valueArray = nil
if type(value) == 'table' then
valueArray = value
else
valueArray = {value}
end
for i, subVal in ipairs(valueArray) do
if type(subVal) == 'table' and subVal.skillID ~= nil then
subVal = {subVal}
end
if maxVisible ~= nil and not inline and insertKey == 'visible' and modCount[insertKey] >= maxVisible then
insertKey = 'overflow'
end
table.insert(modArray[insertKey], p._getModifierText(bonus, subVal, doColor))
modCount[insertKey] = modCount[insertKey] + 1
end
end
if inline then
return table.concat(modArray['visible'], ' and ')
else
if modCount['overflow'] == 1 then
table.insert(modArray['visible'], modArray['overflow'][1])
end
local overflowText = ''
if modCount['overflow'] > 1 then
-- Number of other modifiers has exceeded the specified maximum
overflowText = table.concat({
'<br/><span class="mw-collapsible mw-collapsed" ',
'data-expandtext="Show ' .. Shared.formatnum(modCount['overflow']) .. ' more modifiers" ',
'data-collapsetext="Hide">',
table.concat(modArray['overflow'], '<br/>'),
'</span>'
})
end
return table.concat(modArray['visible'], '<br/>') .. overflowText
end
end
 
function p.getModifierSkills(modifiers)
local skillArray = {}
 
for modifier, value in pairs(modifiers) do
if type(value) == 'table' then
for i, subVal in ipairs(value) do
if type(subVal) == 'table' and subVal.skillID ~= nil then
local skillName = p.getSkillName(subVal.skillID)
if not Shared.contains(skillArray, skillName) then
table.insert(skillArray, skillName)
end
end
end
end
 
local baseName = p.getModifierDetails(modifier)
if baseName == nil then
return { Shared.printError('Modifier "' .. modifier .. '" is invalid') }
end
 
if modifierTypes[baseName].skills ~= nil then
for i, skillName in Shared.skpairs(modifierTypes[baseName].skills) do
if not Shared.contains(skillArray, skillName) then
table.insert(skillArray, skillName)
end
end
end
end
 
return skillArray
end
 
-- Returns a modifiers table indicating modifiersNew less modifiersOld
-- The returned table can be passed to getModifiersText to obtain the
-- result in a human readable format
function p.getModifiersDifference(modifiersOld, modifiersNew)
local modHasPrefix = {}
local modDiff, modDiffBase = {}, {}
local allMods = {
{ ["mods"] = (modifiersNew or {}), ["mult"] = 1 },
{ ["mods"] = (modifiersOld or {}), ["mult"] = -1 }
}
 
-- Generate modifiers table containing only variances
-- Create modDiffBase with mod base names (less 'increased'/'decreased' prefixes),
for i, modDef in ipairs(allMods) do
for modName, value in pairs(modDef.mods) do
local modBaseName, modIsIncrease = modName, true
if Shared.startsWith(modName, "increased") or Shared.startsWith(modName, "decreased") then
modBaseName = string.sub(modName, 10)
modIsIncrease = Shared.startsWith(modName, "increased")
modHasPrefix[modBaseName] = true
end
local modMult = (modIsIncrease and 1 or -1) * modDef.mult
 
if type(value) == 'table' then
-- Skill specific modifier
if modDiffBase[modBaseName] == nil then
modDiffBase[modBaseName] = {}
end
for j, subVal in ipairs(value) do
if type(subVal) == 'table' and subVal.skillID ~= nil then
modDiffBase[modBaseName][subVal.skillID] = (modDiffBase[modBaseName][subVal.skillID] or 0) + subVal.value * modMult
end
end
else
modDiffBase[modBaseName] = (modDiffBase[modBaseName] or 0) + value * modMult
end
end
end
 
-- Transform modDiffBase into modDiff with the appropriate mod name within the return value
for modBaseName, value in pairs(modDiffBase) do
if type(value) == 'table' then
-- Skill specific modifier
for skillID, subVal in pairs(value) do
if subVal ~= 0 then
local modName = nil
if modHasPrefix[modBaseName] then
modName = (subVal < 0 and 'decreased' or 'increased') .. modBaseName
else
modName = modBaseName
end
if modDiff[modName] == nil then
modDiff[modName] = {}
end
table.insert(modDiff[modName], { ["skillID"] = skillID, ["value"] = math.abs(subVal) })
end
end
elseif value ~= 0 then
local modName = nil
if modHasPrefix[modBaseName] then
modName = (value < 0 and 'decreased' or 'increased') .. modBaseName
else
modName = modBaseName
end
modDiff[modName] = (modDiff[modName] or 0) + math.abs(value)
if GameData.rawData.modifierData[modName] == nil then
modDiff[modName] = nil
end
end
end
 
return modDiff
end


  return p._getModifierText(modifier, value, doColor)
-- From game version 1.1 onwards, some entities have custom descriptions, while
-- many are generated based on the modifiers associated to that entity. This
-- function handles that logic given a custom description (may be nil) and
-- a modifiers object
function p.getDescription(customDescription, modifiers)
if customDescription ~= nil then
return customDescription
else
return p.getModifiersText(modifiers, false)
end
end
end


return p
return p
463

edits