Module:Constants: Difference between revisions

From Melvor Idle
(Add more modifier types)
(Added Combat Triangle numbers and getTriangleDamageModifier and getTriangleDRModifier for use in building various other functions in other modules eventually)
Line 122: Line 122:
     [5] = 'Elite',
     [5] = 'Elite',
     [6] = 'Insane'}
     [6] = 'Insane'}
--07/03/21: Hardcoding in Combat Triangle Modifiers
local CombatTriangle = {
  damageBonus = 1.1,
  drBonus = 1.25,
  damagePenalty = { Normal = 0.85,
                    Hardcore = 0.75 },
  drPenalty = { Melee = { Normal = 0.5,
                          Hardcore = 0.25 },
                Ranged = { Normal = 0.95,
                          Hardcore = 0.75 },
                Magic = { Normal = 0.85,
                          Hardcore = 0.75 }},
  Melee = { bonus = "Ranged", penalty = "Magic" },
  Ranged = { bonus = "Magic", penalty = "Melee" },
  Magic = { bonus = "Melee", penalty = "Ranged" },
}
function p.getTriangleDamageModifier(playerStyle, enemyStyle, mode)
  if CombatTriangle[playerStyle].bonus == enemyStyle then
    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
--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(playerStyle, enemyStyle, mode)
  if CombatTriangle[playerStyle].bonus == enemyStyle then
    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


function p.getDifficultyString(difficulty)
function p.getDifficultyString(difficulty)

Revision as of 22:55, 3 July 2021

Documentation for this module may be created at Module:Constants/doc

local p = {}

local ConstantData = mw.loadData('Module:Constants/data')
local ItemData = mw.loadData('Module:Items/data')

local Shared = require('Module:Shared')

--Just hardcoding these because I guess that's where we're at
local modifierTypes = {
  ["MeleeStrengthBonus"] = { text = "{V}% Melee Strength Bonus", 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'} },
  ["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'} },
  ["MinEarthSpellDmg"] = { text = "{VX} Min Earth Spell Dmg", skills = {'Magic'} },
  ["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 = "{V1}% {SV0} Skill XP" },
  ["MiningNodeHP"] = { text = "{V} Mining Node HP", skills = {'Mining'} },
  ["StaminaPerObstacle"] = { text = "{V} Stamina per Agility Obstacle Completion", skills = {'Agility'} },
  ["ChanceToDoubleItems"] = { text = "{V} Chance To Double Items" },
  ["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'} },
  ["ChanceToDoubleItemsSkill"] = { text = "{V1}% Chance to Double Items in {SV0}" },
  ["autoSlayerUnlocked"] = { text = "{V} Auto Slayer Unlocked", skills = {'Slayer'} },
  ["HitpointRegeneration"] = { text = "{V}% Hitpoint Regeneration", skills = {'Combat'} },
  ["SlayerXP"] = { text = "{V}% Slayer XP", skills = {'Slayer'} },
  ["PotionChargesFlat"] = { text = "{V} Charges per Potion" },
  ["SkillInterval"] = { text = "{VMS1}s {SV0} Interval", isIncreaseNegative = true },
  ["BankSpace"] = { text = "{V} Bank Space" },
  ["MinHitBasedOnMaxHit"] = { text = "{V}% of Maximum Hit added to Minimum Hit", skills = {'Combat'} },
  ["DamageToSlayerTasks"] = { text = "{V}% Damage To Slayer Tasks", skills = {'Combat'} },
  ["Lifesteal"] = { text = "{V}% Lifesteal", skills = {'Combat'} },
  ["HPRegenFlat"] = { text = "{V} Flat HP Regen", skills = {'Combat'} },
  ["ChanceToDoubleOres"] = { text = "{V}% Chance to Double Ores in Mining", skills = {'Combat'} },
  ["MaxStamina"] = { text = "{V} Max Stamina" },
  ["MonsterRespawnTimer"] = { text = "{VMS}s Monster Respawn Timer", isIncreaseNegative = true, skills = {'Combat'} },
  ["SkillPreservationChance"] = { text = "{V1}% Chance to Preserve Resources in {SV0}" },
  ["DamageToCombatAreaMonsters"] = { text = "{V}% Damage To Combat Area Monsters", skills = {'Combat'} },
  ["TreeCutLimit"] = { text = "{V} Tree Cut Limit", skills = {'Woodcutting'} },
  ["EquipmentSets"] = { text = "{V} Equipment Sets" },
  ["HiddenSkillLevel"] = { text = "{V1} Hidden {SV0} Level" },
  ["ChanceToPreservePrayerPoints"] = { text = "{V}% Chance To Preserve Prayer Points", skills = {'Prayer'} },
  ["ReflectDamage"] = { text = "{V}% Reflect Damage", skills = {'Combat'} },
  ["MeleeEvasion"] = { text = "{V}% Melee Evasion", skills = {'Combat'} },
  ["DamageToSlayerAreaMonsters"] = { text = "{V}% Damage To Slayer Area Monsters", skills = {'Combat'} },
  ["GPFromMonsters"] = { text = "{V}% GP From Monsters", skills = {'Combat'} },
  ["MagicEvasion"] = { text = "{V}% Magic Evasion", skills = {'Combat'} },
  ["PlayerAttackSpeedPercent"] = { text = "{V}% Player Attack Speed", isIncreaseNegative = true, skills = {'Combat'} },
  ["PreservationChance"] = { text = "{V}% Chance to Preserve Resources" },
  ["DamageReduction"] = { text = "{V}% Damage Reduction", skills = {'Combat'} },
  ["MinWaterSpellDmg"] = { text = "{VX} Min Water Spell Dmg", skills = {'Magic'} },
  ["DamageToAllMonsters"] = { text = "{V}% Damage To All Monsters", skills = {'Combat'} },
  ["golbinRaidIncreasedStartingRuneCount"] = { text = "{V} to starting Elemental Rune count" },
  ["FoodHealingValue"] = { text = "{V}% Food Healing Value", skills = {'Combat'} },
  ["MinFireSpellDmg"] = { text = "{VX} Min Fire Spell Dmg", skills = {'Magic'} },
  ["SlayerCoins"] = { text = "{V}% Slayer Coins", skills = {'Slayer'} },
  ["GPFromThievingFlat"] = { text = "{V} GP From Thieving", skills = {'Thieving'} },
  ["GlobalAccuracy"] = { text = "{V}% Global Accuracy", skills = {'Combat'} },
  ["ChanceToDoubleLootThieving"] = { text = "{V}% Chance To Double Loot in Thieving", skills = {'Thieving'} },
  ["SlayerAreaEffectNegationFlat"] = { text = "{V}% Flat Slayer Area Effect Negation", skills = {'Combat'} },
  ["MagicAccuracyBonus"] = { text = "{V}% Magic Accuracy Bonus", skills = {'Combat'} },
  ["SkillIntervalPercent"] = { text = "{V1}% {SV0} Interval", isIncreaseNegative = true },
  ["GlobalSkillXP"] = { text = "{V}% Global Skill XP" },
  ["MeleeAccuracyBonus"] = { text = "{V}% Melee Accuracy Bonus", skills = {'Combat'} },
  ["DamageToBosses"] = { text = "{V}% Damage To Bosses", skills = {'Combat'} },
  ["ChanceToPreservePotionCharge"] = { text = "{V}% Chance To Preserve Potion Charge" },
  ["MaxHitPercent"] = { text = "{V}% Max Hit", skills = {'Combat'} },
  ["AltMagicSkillXP"] = { text = "{V}% Alt. Magic Skill XP", skills = {'Magic'} },
  ["StaminaPreservationChance"] = { text = "{V}% Chance to Preserve Stamina" },
  ["MinAirSpellDmg"] = { text = "{VX} Min Air Spell Dmg", skills = {'Combat'} },
  ["AutoEatEfficiency"] = { text = "{V}% Auto Eat Efficiency", skills = {'Combat'} },
  ["GPFromThieving"] = { text = "{V}% GP From Thieving", skills = {'Thieving'} },
  ["ChanceToDoubleItemsGlobal"] = { text = "{V}% Chance to Double Items Globally" },
  ["GPGlobal"] = { text = "{V}% GP from all sources (Except Item Selling)", skills = {'Combat', 'Thieving', 'Agility'} },
  ["RangedAccuracyBonus"] = { text = "{V} Ranged Accuracy Bonus", skills = {'Combat'} },
  ["AutoEatThreshold"] = { text = "{V}% Auto Eat Threshold", skills = {'Combat'} },
  ["PlayerAttackSpeed"] = { text = "{VMS}s Player Attack Speed", isIncreaseNegative = true, skills = {'Combat'} },
  ["freeBonfires"] = { text = "+ Automatically relight bonfires for free", skills = {'Firemaking'} },
  ["AutoEatHPLimit"] = { text = "{V}% Auto Eat HP Limit", skills = {'Combat'} },
  ["BankSpaceShop"] = { text = "{V} Bank Space from Shop" },
  ["BirdNestDropRate"] = { text = "{V}% Bird Nest drop rate", skills = {'Woodcutting'} },
  ["RangedEvasion"] = { text = "{V}% Ranged Evasion", skills = {'Combat'} },
  ["ChanceDoubleHarvest"] = { text = "{V}% chance for double harvest", skills = {'Farming'} },
  ["golbinRaidStartingWeapon"] = { text = "Start the Golbin Raid with an {IV}" },
  ["AttackRolls"] = { text = "+Lucky Hit Chance (Roll twice, take the better result)", skills = {'Combat'} },
  ["AmmoPreservation"] = { text = "{V}% Ammo Preservation", skills = {'Ranged'} },
  ["RangedStrengthBonus"] = { text = "{V}% Ranged Strength Bonus", skills = {'Combat'} },
  ["MagicDamageBonus"] = { text = "{V}% Magic Damage Bonus", skills = {'Combat'} },
  ["MasteryXP"] = { text = "{V1}% {SV0} Mastery XP" },
  ["dungeonEquipmentSwapping"] = { text = "{V} Dungeon Equipment Swapping", skills = {'Combat'} },
  ["SeeingGoldChance"] = { text = "{V}% chance for Silver Ore to also produce a Gold Bar when smithed", skills = {'Smithing'} },
  ["ElementalRuneGain"] = { text = "{V} runes received when generating random elemental runes", skills = {'Runecrafting'} },
  ["StaminaCost"] = { text = "{V} Stamina Cost per action", isIncreaseNegative = true },
  ["GPFromSales"] = { text = "{V}% GP From Sales" },
  ["MaxHitFlat"] = { text = "{VX} Max Hit", skills = {'Combat'} },
  ["ChanceNoDamageMining"] = { text = "{V}% chance to do zero damage to Ores and Rune Essence", skills = {'Mining'} },
  ["ChanceForElementalRune"] = { text = "{V}% chance to receive random elemental runes per Runecraft", skills = {'Runecrafting'} },
  ["ChanceToApplyBurn"] = { text = "{V}% Chance to apply Burn to Enemy in Combat", skills = {'Combat'} },
  ["SummoningShardCost"] = { text = "{V} Shard Cost when creating Familiars in Summoning", skills = {'Summoning'} },
  ["SummoningCreationCharges"] = { text = "{V} Base Quantity for Summoning Tablet Creation", skills = {'Summoning'} },
  ["SummoningChargePreservation"] = { text = "{V}% Chance to preserve Summoning Charges", skills = {'Summoning'} },
  ["GPOnEnemyHit"] = { text = "{V} GP Gained on successful Enemy Hit", skills = {'Combat'} },
  ["AdditionalRunecraftCountRunes"] = { text = "{V} Additional Runes of the same type in Runecrafting", skills = {'Runecrafting'} },
  ["ChanceAdditionalSkillResource"] = { text = "{V1}% Chance to gain +1 additional resource in {SV0}. Cannot be doubled" },
  ["EnemyMeleeEvasion"] = { text = "{V}% Enemy Melee Evasion", isIncreaseNegative = true, skills = {'Combat'} },
  ["EnemyRangedEvasion"] = { text = "{V}% Enemy Ranged Evasion", isIncreaseNegative = true, skills = {'Combat'} },
  ["EnemyMagicEvasion"] = { text = "{V}% Enemy Magic Evasion", isIncreaseNegative = true, 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
local CombatTriangle = {
  damageBonus = 1.1, 
  drBonus = 1.25,
  damagePenalty = { Normal = 0.85,
                    Hardcore = 0.75 },
  drPenalty = { Melee = { Normal = 0.5,
                          Hardcore = 0.25 },
                Ranged = { Normal = 0.95,
                           Hardcore = 0.75 },
                Magic = { Normal = 0.85,
                          Hardcore = 0.75 }},
  Melee = { bonus = "Ranged", penalty = "Magic" },
  Ranged = { bonus = "Magic", penalty = "Melee" },
  Magic = { bonus = "Melee", penalty = "Ranged" },
}

function p.getTriangleDamageModifier(playerStyle, enemyStyle, mode)
  if CombatTriangle[playerStyle].bonus == enemyStyle then
    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

--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(playerStyle, enemyStyle, mode)
  if CombatTriangle[playerStyle].bonus == enemyStyle then
    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

function p.getDifficultyString(difficulty)
  return Difficulties[difficulty]
end

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

function p.getSkillID(skillName)
  return ConstantData.skill[skillName]
end

function p.getEquipmentSlotName(id)
  for slotName, i in Shared.skpairs(ConstantData.equipmentSlot) do
    if i == id then
      return slotName
    end
  end
  return 'Invalid'
end

function p.getEquipmentSlotID(name)
  return ConstantData.equipmentSlot[name]
end

function p.getCombatStyleName(styleNum)
  for name, num in Shared.skpairs(ConstantData.attackType) do
    if num == styleNum then
      return name
    end
  end
  return "ERROR: Invalid combat style[[Category:Pages with script errors]]"
end

function p.getSlayerTierName(tier)
  for name, num in Shared.skpairs(ConstantData.slayerTier) do
    if num == tier then
      return name
    end
  end
  return "ERROR: Invalid Slayer tier[[Category:Pages with script errors]]"
end

function p.getSlayerTierNameByLevel(lvl)
  for i, tier in Shared.skpairs(ConstantData.Slayer.Tiers) do
    if tier.minLevel <= lvl and (tier.maxLevel >= lvl or tier.maxLevel == -1) then
      return tier.display
    end
  end
  return 'N/A'
end

function p.getSlayerTier(name)
  for i, tier in Shared.skpairs(ConstantData.Slayer.Tiers) do
    if tier.display == name then
      local result = Shared.clone(tier)
      result.id = i - 1
      return result
    end
  end
end

function p.getSlayerTierByID(tierID)
  if ConstantData.Slayer.Tiers[tierID + 1] == nil then
    return nil
  end

  local result = Shared.clone(ConstantData.Slayer.Tiers[tierID + 1])
  result.id = tierID
  return result
end

--Turns a modifier name like 'increasedMeleeAccuracyBonus' into several pieces of data:
--Base Name, Text, Sign, and IsNegative
--ex. "MeleeAccuracyBonus", "+{V}% Melee Accuracy", "+", false
function p.getModifierDetails(modifierName)
  local baseName = modifierName
  local isIncrease = true
  local isNegative = false

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

  local modifier = modifierTypes[baseName]
  if modifier == nil then
    mw.log(baseName)
    return nil
  end

  local isPositive = isIncrease
  if modifier.isIncreaseNegative then
    isPositive = not isPositive
  end

  local sign = "+"
  if (not isIncrease and not modifier.inverseSign) or (isIncrease and modifier.inverseSign) then
    sign = "-"
  end

  return baseName, modifier.text, sign, not isPositive
end

function p._getModifierText(modifier, value, doColor)
  if doColor == nil then doColor = true end
  local modName, modText, sign, isNegative = p.getModifierDetails(modifier)

  if modName == nil then
    return 'ERROR: Invalid modifier type [[Category:Pages with script errors]]'
  end

  local result = modText

  if type(value) == 'table' then
    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
        result = string.gsub(result, '{V1}', sign..value[2])
        result = string.gsub(result, '{VMS1}', sign..(value[2] / 1000))
      end
    end
  else
    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}', sign..value)
    result = string.gsub(result, '{VMS}', sign..(value / 1000))
    result = string.gsub(result, '{VX}', sign..(value * 10))
  end

  if doColor then
    if isNegative ~= nil and isNegative then
      result = '<span style="color:red">'..result..'</span>'
    else
      result = '<span style="color:green">'..result..'</span>'
    end
  end

  return result
end

function p.getModifierText(frame)
  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 skill = frame.args ~= nil and frame.args.skill or frame.skill
  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 = {p.getSkillID(skill), value}
  end

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

function p.getModifiersText(modifiers, doColor)
  if modifiers == nil or Shared.tableCount(modifiers) == 0 then
    return ''
  end

  local modArray = {}
  for bonus, value in Shared.skpairs(modifiers) do
    table.insert(modArray, p._getModifierText(bonus, value, doColor))
  end
  return table.concat(modArray, "<br/>")
end

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

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

    local baseName = p.getModifierDetails(modifier)
    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

return p