Anonymous

Module:Constants: Difference between revisions

From Melvor Idle
Updated Arch Tool Level display, Fixed error on nil modifiers in getModifiersDifference
(returning Modifier as another part of getModifierDetails)
(Updated Arch Tool Level display, Fixed error on nil modifiers in getModifiersDifference)
 
(102 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 = {
  ["MeleeStrengthBonus"] = { text = "{V}% Melee Strength Bonus", skills = {'Combat'} },
["MeleeStrengthBonus"] = { text = "{V}% Melee Strength Bonus from Equipment", skills = {'Combat'} },
  ["DamageToDungeonMonsters"] = { text = "{V}% Damage To Dungeon Monsters", skills = {'Combat'} },
["DamageToDungeonMonsters"] = { text = "{V}% Damage To Dungeon Monsters", skills = {'Combat'} },
  ["GlobalMasteryXP"] = { text = "{V}% Global Mastery XP", skills = {'Woodcutting', 'Fishing', 'Firemaking', 'Cooking', 'Mining', 'Smithing', 'Thieving', 'Farming', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Agility', 'Summoning', 'Astrology'} },
["GlobalMasteryXP"] = { text = "{V}% Global Mastery XP", skills = {'Woodcutting', 'Fishing', 'Firemaking', 'Cooking', 'Mining', 'Smithing', 'Thieving', 'Farming', 'Fletching', 'Crafting', 'Runecrafting', 'Herblore', 'Agility', 'Summoning', 'Astrology', 'Archaeology'} },
  ["ChanceRandomPotionHerblore"] = { text = "{V}% chance to gain a second potion of a random tier", skills = {'Herblore'} },
["ChanceRandomPotionHerblore"] = { text = "{V}% chance to gain a second potion of a random tier", skills = {'Herblore'} },
  ["FlatPrayerCostReduction"] = { text = "{V} Prayer Point Cost for Prayers", inverseSign = true, skills = {'Prayer'} },
["FlatPrayerCostReduction"] = { text = "{V} Prayer Point Cost for Prayers (Prayer Point cost cannot go below 1)", inverseSign = true, skills = {'Prayer'} },
  ["MinEarthSpellDmg"] = { text = "{VX} Min Earth Spell Dmg", skills = {'Magic'} },
["MinEarthSpellDmg"] = { text = "{VX} Min Earth Spell Dmg", skills = {'Magic'} },
  ["SlayerTaskLength"] = { text = "{V}% Slayer Task Length/Qty", skills = {'Slayer'} },
["SlayerTaskLength"] = { text = "{V}% Slayer Task Length/Qty", skills = {'Slayer'} },
  ["ChanceToDoubleLootCombat"] = { text = "{V}% Chance To Double Loot in Combat", skills = {'Combat'
["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'} },
["
}
}


--Difficulties are hard coded which is dumb but means hardcoding them here too
local Difficulties = {
    [0] = 'Very Easy',
    [1] = 'Easy',
    [2] = 'Medium',
    [3] = 'Hard',
    [4] = 'Very Hard',
    [5] = 'Elite',
    [6] = 'Insane'}


--07/03/21: Hardcoding in Combat Triangle Modifiers
function p.getTriangleAttribute(attribute, attackerStyle, targetStyle, modeName)
local CombatTriangle = {
if type(attribute) ~= 'string' then
  damageBonus = 1.1,  
error("Parameter 'attribute' must be a string", 2)
  drBonus = 1.25,
elseif type(attackerStyle) ~= 'string' then
  damagePenalty = { Normal = 0.85,
error("Parameter 'attackerStyle' must be a string", 2)
                    Hardcore = 0.75 },
elseif type(targetStyle) ~= 'string' then
  drPenalty = { Melee = { Normal = 0.5,
error("Parameter 'targetStyle' must be a string", 2)
                          Hardcore = 0.25 },
elseif type(modeName) ~= 'string' then
                Ranged = { Normal = 0.95,
error("Parameter 'modeName' must be a string", 2)
                          Hardcore = 0.75 },
end
                Magic = { Normal = 0.85,
                          Hardcore = 0.75 }},
local mode = GameData.getEntityByName('gamemodes', modeName)
  Melee = { bonus = "Ranged", penalty = "Magic" },
if mode == nil then
  Ranged = { bonus = "Magic", penalty = "Melee" },
error("Invalid gamemode '" .. modeName .. "'", 2)
  Magic = { bonus = "Melee", penalty = "Ranged" },
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(playerStyle, enemyStyle, mode)
function p.getTriangleDamageModifier(attackerStyle, targetStyle, mode)
  if CombatTriangle[playerStyle].bonus == enemyStyle then
return p.getTriangleAttribute('damageModifier', attackerStyle, targetStyle, mode)
    return CombatTriangle.damageBonus
  elseif CombatTriangle[playerStyle].penalty == enemyStyle then
    if mode == 'Hardcore' or mode == 'Adventure' then
      return CombatTriangle.damagePenalty.Hardcore
    else
      return CombatTriangle.damagePenalty.Normal
    end
  else
    return 1
  end
end
end


--Syntax is like p.getTriangleDRModifier('Melee', 'Ranged', 'Normal')
--Syntax is like p.getTriangleDRModifier('Melee', 'Ranged', 'Normal')
--Returns a multiplier that can be multiplied with base DR to get the post-Triangle result
--Returns a multiplier that can be multiplied with base DR to get the post-Triangle result
function p.getTriangleDRModifier(playerStyle, enemyStyle, mode)
function p.getTriangleDRModifier(attackerStyle, targetStyle, mode)
  if CombatTriangle[playerStyle].bonus == enemyStyle then
return p.getTriangleAttribute('reductionModifier', attackerStyle, targetStyle, mode)
    return CombatTriangle.drBonus
  elseif CombatTriangle[playerStyle].penalty == enemyStyle then
    if mode == 'Hardcore' or mode == 'Adventure' then
      return CombatTriangle.drPenalty[playerStyle].Hardcore
    else
      return CombatTriangle.drPenalty[playerStyle].Normal
    end
  else
    return 1
  end
end
end


function p.getDifficultyString(difficulty)
function p.getDifficultyString(difficulty)
  return Difficulties[difficulty]
return GameData.rawData.combatAreaDifficulties[difficulty + 1]
end
end


function p.getSkillName(skillID)
function p.getSkillName(skillID)
return ConstantData.skill[skillID]
local skill = GameData.getSkillData(skillID)
if skill ~= nil then
return skill.name
end
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)
  return type(id) == 'number' and ConstantData.equipmentSlot[id] or 'Invalid'
local slotData = GameData.getEntityByID('equipmentSlots', id)
if slotData ~= nil then
return slotData.name
end
end
end


function p.getEquipmentSlotID(name)
function p.getEquipmentSlotID(name)
  return ConstantData.equipmentSlot[name]
local slotData = GameData.getEntityByName('equipmentSlots', name)
if slotData ~= nil then
return slotData.id
end
end
end


function p.getCombatStyleName(styleNum)
function p.getCombatStyleName(styleNum)
  if type(styleNum) == 'number' then
if type(styleNum) == 'number' then
  local styleName = ConstantData.attackType[styleNum]
local styleName = GameData.rawData.attackTypes[styleNum]
  if styleName ~= nil then
if styleName ~= nil then
    return Shared.titleCase(styleName)
return Shared.titleCase(styleName)
    end
end
  end
elseif type(styleNum) == 'string' and type(GameData.rawData.attackTypes[string.lower(styleNum)]) == 'number' then
  return "ERROR: Invalid combat style[[Category:Pages with script errors]]"
return Shared.titleCase(styleNum)
end
return Shared.printError('Invalid combat style')
end
end


function p.getSlayerTierName(tier)
 
  return type(tier) == 'number' and ConstantData.slayerTier[tier] or "ERROR: Invalid Slayer tier[[Category:Pages with script errors]]"
--- 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.getSlayerTierNameByLevel(lvl)
function p.getSlayerTier(name)
  for i, tier in Shared.skpairs(ConstantData.Slayer.Tiers) do
return GameData.getEntityByProperty('slayerTiers', 'display', name)
    if tier.minLevel <= lvl and (tier.maxLevel == nil or tier.maxLevel >= lvl) then
      return tier.display
    end
  end
  return 'N/A'
end
end


function p.getSlayerTier(name)
function p.getSlayerTierByLevel(level)
  for i, tier in Shared.skpairs(ConstantData.Slayer.Tiers) do
if type(level) ~= 'number' or level < 1 then
    if tier.display == name then
return Shared.printError('Invalid Slayer level')
      local result = Shared.clone(tier)
end
      result.id = i - 1
      return result
for i, tier in ipairs(GameData.rawData.slayerTiers) do
    end
if tier.minLevel <= level and (tier.maxLevel == nil or tier.maxLevel >= level) then
  end
return tier
end
end
end
end


function p.getSlayerTierByID(tierID)
--
  if ConstantData.Slayer.Tiers[tierID + 1] == nil then
-- the following functions just return subsets of the slayer functions above
    return nil
--
  end
 
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


  local result = Shared.clone(ConstantData.Slayer.Tiers[tierID + 1])
function p.getSlayerTierNameByLevel(lvl)
  result.id = tierID
local tier = p.getSlayerTierByLevel(lvl)
  return result
if type(tier) == 'table' then
return tier.display
else
return Shared.printError('Invalid Slayer tier')
end
end
end


--Turns a modifier name like 'increasedMeleeAccuracyBonus' into several pieces of data:
--
--Base Name, Text, Sign, and IsNegative
--- End of slayer functions
--ex. "MeleeAccuracyBonus", "+{V}% Melee Accuracy", "+", false
 
--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)
function p.getModifierDetails(modifierName)
  local baseName = modifierName
local baseName = modifierName
  local isIncrease = true
local modifier = GameData.rawData.modifierData[modifierName]
  local isNegative = false
 
  local valueUnsigned = false
if modifier == nil then
return nil
end


  if Shared.startsWith(modifierName, "increased") or Shared.startsWith(modifierName, "decreased") then
if Shared.startsWith(modifierName, "increased") or Shared.startsWith(modifierName, "decreased") then
    baseName = string.sub(modifierName, 10)
baseName = string.sub(modifierName, 10)
    isIncrease = Shared.startsWith(modifierName, "increased")
end
  end


  local modifier = modifierTypes[baseName]
return baseName, modifier.description, modifier.isNegative, modifier.modifyValue
  if modifier == nil then
end
    return nil
  end


  local isPositive = isIncrease
function p._getModifierText(modifier, value, doColor)
  if modifier.isIncreaseNegative then
if doColor == nil then doColor = true end
    isPositive = not isPositive
local modName, modText, isNegative, modifyValue = p.getModifierDetails(modifier)
  end


  local sign = "+"
if modName == nil then
  if (not isIncrease and not modifier.inverseSign) or (isIncrease and modifier.inverseSign) then
return Shared.printError('Invalid modifier type for "' .. modifier .. '"')
    sign = "-"
end
  end


  if type(modifier.unsigned) == 'boolean' then valueUnsigned = modifier.unsigned end
if modifyValue ~= nil and string.match(modifyValue, 'ToolLevels') then
modifyValue = 'ArchaeologyToolLevels'
end


  return baseName, modifier.text, sign, not isPositive, valueUnsigned, modifier
local formatModValue = function(value, rule)
end
local ruleFunctions = {
['value'] = function(val) return val end,
['multiplyByNumberMultiplier'] = function(val) return val * 10 end,
['divideByNumberMultiplier'] = function(val) return val / 10 end,
['milliToSeconds'] = function(val) return val / 1000 end,
['(value)=>value*100'] = function(val) return val * 100 end,
['(value)=>100+value'] = function(val) return val + 100 end,
['(value)=>value+1'] = function(val) return val + 1 end,
['(value)=>Math.pow(2,value)'] = function(val) return 2^val end,
["(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,
['ArchaeologyToolLevels'] = function(val)
local toolLevel = '+' .. val
if string.match(modName, 'Sieve') then
toolLevel = toolLevel ..  ' level of the Sieve Tool in Archaeology'
elseif string.match(modName, 'Trowel') then
toolLevel = toolLevel ..  ' level of the Trowel Tool in Archaeology'
elseif string.match(modName, 'Brush') then
toolLevel = toolLevel ..  ' level of the Brush Tool in Archaeology'
elseif string.match(modName, 'Shovel') then
toolLevel = toolLevel ..  ' level of the Shovel Tool in Archaeology'
end
if val > 1 then
return string.gsub(toolLevel, 'level', 'levels')
else
return toolLevel
end
end,
['(value)=>game.golbinRaid.startingWeapons[value].name'] = function(val)
-- For golbin raid starting weapons
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']


function p._getModifierText(modifier, value, doColor)
if type(value) == 'table' then
  if doColor == nil then doColor = true end
-- If table is a pair of values then format both & add a separator
  local modName, modText, sign, isNegative, valueUnsigned = p.getModifierDetails(modifier)
return ruleFunc(value[1]) .. '-' .. ruleFunc(value[2])
else
return ruleFunc(value)
end
end


  if modName == nil then
local valueArray, resultArray = nil, {}
    return 'ERROR: Invalid modifier type for ' .. modifier .. '[[Category:Pages with script errors]]'
if type(value) ~= 'table' then
  end
valueArray = {value}
else
valueArray = value
end


  local result = modText
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


  if type(value) == 'table' then
resultText = string.gsub(resultText, '${value}', function(rule) return (formatModValue(modMagnitude, rule) or '') end)
    if Shared.tableCount(value) > 0 and type(value[1]) == 'table' then
      --Potentially return multiple rows if necessary
      local resultArray = {}
      for i, subVal in Shared.skpairs(value) do
        table.insert(resultArray, p._getModifierText(modifier, subVal, doColor))
      end
      return table.concat(resultArray, '<br/>')
    else
      if value[1] ~= nil then
        local skillName = p.getSkillName(value[1])
        if skillName ~= nil then
          result = string.gsub(result, '{SV0}', p.getSkillName(value[1]))
        end
      end
      if value[2] ~= nil then value = value[2] end
    end
  end
  -- Re-check the type of value, as it may have been modified above even if it was originally a table
  if type(value) ~= 'table' then
    local valSign = (valueUnsigned and '' or sign)
    if string.find(result, '{IV}', 1, true) ~= nil and tonumber(value) ~= nil then
      local item = ItemData.Items[tonumber(value) + 1]
      if item ~= nil then
        result = string.gsub(result, '{IV}', item.name)
      end
    end
    result = string.gsub(result, '{V}', valSign..value)
    result = string.gsub(result, '{VD}', valSign..(value / 10))
    result = string.gsub(result, '{VMS}', valSign..(value / 1000))
    result = string.gsub(result, '{VX}', valSign..(value * 10))
    result = string.gsub(result, '{VX100}', valSign..(value * 100))
    result = string.gsub(result, '{V%+100}', valSign..(value + 100))
    result = string.gsub(result, '{VMUL}', 2^value)
    result = string.gsub(result, '{S}', sign)
  end


  if doColor then
if doColor then
    if isNegative ~= nil and isNegative then
local colorCode = (isNegative ~= nil and isNegative and 'color:red' or 'color:green')
      result = '<span style="color:red">'..result..'</span>'
resultText = '<span style="' .. colorCode .. '">' .. resultText .. '</span>'
    else
end
      result = '<span style="color:green">'..result..'</span>'
table.insert(resultArray, resultText)
    end
end
  end


  return result
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
if doColor ~= nil then
    doColor = string.upper(doColor) ~= 'FALSE'
doColor = string.upper(doColor) ~= 'FALSE'
  end
end


  if skill ~= nil and skill ~= '' then
if skill ~= nil and skill ~= '' then
    value = {p.getSkillID(skill), value}
value = { { ["skillID"] = p.getSkillID(skill), ["value"] = value } }
  end
end


  return p._getModifierText(modifier, value, doColor)
return p._getModifierText(modifier, value, doColor)
end
end


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


  local modArray = {}
local modArray = { ["visible"] = {}, ["overflow"] = {} }
  for bonus, value in Shared.skpairs(modifiers) do
local modCount = { ["visible"] = 0, ["overflow"] = 0 }
    table.insert(modArray, p._getModifierText(bonus, value, doColor))
local insertKey = 'visible'
  end
for bonus, value in pairs(modifiers) do
  return table.concat(modArray, "<br/>")
-- 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
end


function p.getModifierSkills(modifiers)
function p.getModifierSkills(modifiers)
  local skillArray = {}
local skillArray = {}


  for modifier, value in Shared.skpairs(modifiers) do
for modifier, value in pairs(modifiers) do
    if type(value) == 'table' then
if type(value) == 'table' then
      for i, subVal in Shared.skpairs(value) do
for i, subVal in ipairs(value) do
        local skillName = p.getSkillName(subVal[1])
if type(subVal) == 'table' and subVal.skillID ~= nil then
        if not Shared.contains(skillArray, skillName) then
local skillName = p.getSkillName(subVal.skillID)
          table.insert(skillArray, skillName)
if not Shared.contains(skillArray, skillName) then
        end
table.insert(skillArray, skillName)
      end
end
    end
end
end
end


    local baseName = p.getModifierDetails(modifier)
local baseName = p.getModifierDetails(modifier)
    if baseName == nil then
if baseName == nil then
      return { 'ERROR: Modifier '..modifier..' is invalid' }
return { Shared.printError('Modifier "' .. modifier .. '" is invalid') }
    end
end


    if modifierTypes[baseName].skills ~= nil then
if modifierTypes[baseName].skills ~= nil then
      for i, skillName in Shared.skpairs(modifierTypes[baseName].skills) do
for i, skillName in Shared.skpairs(modifierTypes[baseName].skills) do
        if not Shared.contains(skillArray, skillName) then
if not Shared.contains(skillArray, skillName) then
          table.insert(skillArray, skillName)
table.insert(skillArray, skillName)
        end
end
      end
end
    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 skillArray
-- 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