Melvor Idle Corruption Helper

Automate rolling for specific modifiers in Corruption Mode (April Fools 2021 event)

// ==UserScript==
// @name         Melvor Idle Corruption Helper
// @namespace    http://tampermonkey.net/
// @version      1.0.2
// @description  Automate rolling for specific modifiers in Corruption Mode (April Fools 2021 event)
// @author       Cyrogem
// @helpedby     Erik Gillespie, Kad
// @site         https://www.cyrogemgames.com/
// @match        https://*.melvoridle.com/*
// @exclude      https://wiki.melvoridle.com/*
// @grant        none
// ==/UserScript==

const skillSpecificModifiers = [
  // inc skill hidden level
  40,
  // dec skill interval
  92,
  // dec skill interval %
  93,
  // inc skill preserve chance
  95,
  // inc mastery exp
  103,
  // inc skill exp
  104,
  // (old) probably inc skill interval
  107,
  // (old) probably inc skill interval %
  108,
  // inc chance to double in skill
  132,
]

let allTraitsWanted = {}
let newDesireTrait = -1
let newDesireValue = -1
let newDesireSlot = -1
let newDesireSkill = -1
let iterations = 0
let lostItems = 0
let rolls = 0

let activeModifiers = [];

/**
 * Roll for corruption based on the previously input information
 */
function CyrogemRollCorruption(){
  if (newDesireSlot < 0){
    window.alert('Please select a slot to roll for')
    return;
  }
  let equipmentSlot = newDesireSlot
  let cost = getRandomModifierCost(equipmentSlot);
  let keepTrying = true
  let mods = {}
  lostItems = 0
  rolls = 0

  // Test to make sure we have the things
  let atLeastOneKey = false
  for(var key in allTraitsWanted) {
    var value = allTraitsWanted[key];
    if (typeof value === 'object' && value !== null){
      atLeastOneKey = Object.keys(value).length > 0 || atLeastOneKey
    } else {
      atLeastOneKey = true
    }
    //console.log(key + ' ' + value)
  }

  if (!atLeastOneKey){
    window.alert('Please have at least one desire')
    return;
  }

  // Re-roll
  for(let i = 0; i < iterations && keepTrying; i++){
    rolls++
    //console.log(cost + ' ' + equippedItems[equipmentSlot])
    if (gp >= cost && equippedItems[equipmentSlot] > 0) {
      gp -= cost;
      updateGP();
      let tier = getRandomModifierTier(equipmentSlot);
      let chanceToDestroy = getRandomModifiersDestroyChance(tier);
      // Do we lose the item
      if (rollPercentage(chanceToDestroy)) {
        lostItems++
        let qty = getItemQtyRandomModifier(equippedItems[equipmentSlot]);
        if (qty[0] > 1) {
            if (equipmentSlot === CONSTANTS.equipmentSlot.Quiver && ammo > 1) {
                ammo--;
                equipmentSets[selectedEquipmentSet].ammo--;
                updateAmmo();
            } else
                updateItemInBank(qty[1], equippedItems[equipmentSlot], -1);
        } else {
            equippedItems[equipmentSlot] = 0;
            equipmentSets[selectedEquipmentSet].equipment[equipmentSlot] = 0;
            setEquipmentSet(selectedEquipmentSet);
        }
      } else {
        mods = rollRandomModifiers(tier, "equipment", equipmentSlot);
        keepTrying = !CyrogemCheckCorruption(mods)
      }
    } else {
      rolls--
      break;
    }
  }
  updateRandomModifierInfo(equipmentSlot);
  updateHTMLRandomMod(equipmentSlot, mods);
  window.alert('We ' + (keepTrying ? 'failed' : 'succeeded') + ' after ' + rolls + ' re-rolls and lost ' + lostItems + ' to the void')
}

/**
 * Are these mods what we desire
 * @param {*} mods The newly rolled mods
 * @returns True if a mod matches a desire
 */
function CyrogemCheckCorruption(mods){
  let success = false
  for (let i = 0; i < mods.length && !success; i++) {
    let theMod = mods[i].modifier
    let theValue = mods[i].value
    //console.log(theMod)
    //console.log(theValue)
    // Is the value a list
    if (theValue.length) {
      // This is a niche event, is this something we are looking for
      // Get it out of it's inner shell
      theValue = theValue[0]
      let theSkillName = skillName[theValue[0]]
      //console.log('skill name = ' + theSkillName)
      // Check if we have this defined as a desire
      if (allTraitsWanted[theMod] !== undefined && allTraitsWanted[theMod][theSkillName] !== undefined){
        // We have it defined, is it high enough of a value
        if (allTraitsWanted[theMod][theSkillName] <= theValue[1]){
          success = true
        }
      }
    }
    else{
      if (allTraitsWanted[theMod] !== undefined && allTraitsWanted[theMod] <= theValue) {
        success = true
      }
    }
  }
  // If we have one of the ones we want, return true
  return success
}

/**
 * Remember current desire to check against when we roll
 *
 * Also creates button to remove desire
 */
function CyrogemAddDesire(){
  if (newDesireValue === -1 || newDesireValue === '' || newDesireTrait === -1 || (skillSpecificModifiers.includes(newDesireTrait) && newDesireSkill === -1)){
    window.alert('Please Select a Slot, Desired Trait(s), Desired Value(s), and a Desired Skill if aplicable')
    return;
  }



  let newmod = activeModifiers[newDesireTrait]
  // Is this a special case where we need a skill specified
  if(skillSpecificModifiers.includes(newDesireTrait)){
    // Initial setup in case this is our first one
    if (allTraitsWanted[newmod] === undefined){
      allTraitsWanted[newmod] = {}
    }
    // Assign this with the skillName
    allTraitsWanted[newmod][skillName[newDesireSkill]] = newDesireValue
  } else {
    allTraitsWanted[newmod] = newDesireValue
  }

  // Erik Gillespie solved this issue
  const displayElement = document.getElementById('cyrogem-current-desires')
  const buttonTemplate = document.getElementById('cyrogem-button-template')
  const newButton = document.importNode(buttonTemplate.content.firstElementChild, true)

  // Is this a skill specific desire
  if(skillSpecificModifiers.includes(newDesireTrait)){
    let trait = activeModifiers[newDesireTrait]
    let skill = skillName[newDesireSkill]
    newButton.id = 'kill-desire-' + trait + '-' + skill
    newButton.innerHTML = printPlayerModifier(trait, [newDesireSkill, newDesireValue])[0]
    newButton.addEventListener('click', () => CyrogemRemoveDesire(trait, skill))
  } else {
    let trait = activeModifiers[newDesireTrait]
    newButton.id = 'kill-desire-' + trait
    newButton.innerHTML = printPlayerModifier(trait, newDesireValue)[0]
    newButton.addEventListener('click', () => CyrogemRemoveDesire(trait))
  }

  displayElement.appendChild(newButton)

  CyrogemUpdatePrediction()

  // Debugging
  /*
  for(var key in allTraitsWanted) {
    var value = allTraitsWanted[key];
    if(typeof value === 'object' && value !== null){
      for(var key2 in value){
        console.log(
          'My path is: allTraitsWanted[' + key + '][' + key2 + ']' +
          '\nand I equal ' + allTraitsWanted[key][key2]
        )
      }
    } else {
      console.log(key + ' ' + value)
    }
  }
  */
}

/**
 * Remove a currently listed desire
 * @param {*} trait What desire are we removing
 * @param {*} skill What skill, if any, are we removing from this trait
 */
function CyrogemRemoveDesire(trait, skill=null){
  // Easy one first
  //console.log(allTraitsWanted[trait] + ' before')
  if (skill === null){
    delete allTraitsWanted[trait];
    document.getElementById('kill-desire-' + trait).remove()
  } else {
    var value = allTraitsWanted[trait];
    if(typeof value === 'object' && value !== null){
      delete value[skill];
      document.getElementById('kill-desire-' + trait + '-' + skill).remove()
    }
  }
  //console.log(allTraitsWanted.trait + ' after')
  CyrogemUpdatePrediction()
}

// Erik Gillespie provided the solution to buttons losing eventHandlers
// A lot of old code was removed thanks to him
const ErikTactic = true

/**
 * Update the display for the player showing the expected outcome
 */
function CyrogemUpdatePrediction(){
  let predictionHTML = ''
  // Assemble info only needed here
  let costPerRoll = newDesireSlot >= 0 ? getRandomModifierCost(newDesireSlot) : 0
  let tierOfRoll = newDesireSlot >= 0 ? getRandomModifierTier(newDesireSlot) : 0
  let maxValue = getRandomModifierMaxValue(tierOfRoll) - 1
  let items = newDesireSlot >= 0 ? getItemQtyRandomModifier(equippedItems[newDesireSlot])[0] : 0
  let predictedNumberOfActualRolls = iterations
  let rollsUntilNoItems = tierOfRoll <= 0 ? 0 : Math.floor(items / (getRandomModifiersDestroyChance(tierOfRoll) / 100))

  // Find our limiting factor, either attempts, items, or gold
  if (rollsUntilNoItems < predictedNumberOfActualRolls){
    predictedNumberOfActualRolls = rollsUntilNoItems
  }
  if (gp / costPerRoll < predictedNumberOfActualRolls){
    predictedNumberOfActualRolls = Math.floor(gp / costPerRoll)
  }

  // Calculate odds
  let oddsOfGettingWhatYouWant = 0.0

  for(var trait in allTraitsWanted){
    let value = allTraitsWanted[trait]
    // It's not simple we need to take value into account
    if(typeof value === 'object' && value !== null){
      for(var skill in value){
        oddsOfGettingWhatYouWant += Math.max((((maxValue - allTraitsWanted[trait][skill]) / maxValue) / skillName.length) / activeModifiers.length, 0)
      }
    } else {
      oddsOfGettingWhatYouWant += Math.max(((maxValue - allTraitsWanted[trait]) / maxValue) / activeModifiers.length, 0)
    }
  }
  let oddsOfFailure = Math.pow((1.0 - oddsOfGettingWhatYouWant), tierOfRoll)

  let finalSuccessChance = 1 - Math.pow(oddsOfFailure, predictedNumberOfActualRolls)
  const headerOpen = '<h5 class="font-w400 font-size-sm text-center text-combat-smoke m-1 mb-2">'
  // Time to assemble the information to the user
  predictionHTML += '<h3 class="font-w600 font-size-sm text-center text-danger m-1 mb-2">Corruption Prediction</h3>' +
  headerOpen + 'Predicted rolls accounting for gold and item loss: <span class="font-w600 text-warning">' + predictedNumberOfActualRolls + '</span></h5>' +
  headerOpen + 'Chance of success in those rolls: ' + CyrogemRollChanceColor(finalSuccessChance) + '</h5>' +
  headerOpen + 'Predicted Max Cost: <img src="assets/media/main/coins.svg" class="skill-icon-xs mr-2">' + (costPerRoll * predictedNumberOfActualRolls) + '</h5>' +
  headerOpen + '50% success rate achieved at: <span class="font-w600 text-warning">' + CyrogemRollsForFiftyPercent(oddsOfFailure) + '</h5>'

  //document.getElementById('cyrogem-predictive-display').insertAdjacentHTML('beforeend', predictionHTML)
  document.getElementById('cyrogem-predictive-display').innerHTML = predictionHTML
}

/**
 * Calculates how many rolls are required to get a 50% chance or better of success
 * @param {*} failChancePerRoll Odds of not getting what you want every roll
 * @returns Roll count and closing span tag
 */
function CyrogemRollsForFiftyPercent(failChancePerRoll){
  if(failChancePerRoll < 0.0 || failChancePerRoll >= 1.0){
    return 'Never</span>';
  } else {
    let loops = 1
    let failChance = failChancePerRoll
    while (loops < 10000 && failChance > 0.5){
      loops++
      failChance = failChance * failChancePerRoll
    }
    if (failChance > 0.5){
      return 'Too Many</span>';
    } else {
      let value = loops + ' rolls</span>'
      return value;
    }
  }
}

/**
 * Color and format the success chance for players
 * @param {*} successChance Odds of success for all rolls
 * @returns formatted HTML
 */
function CyrogemRollChanceColor(successChance){
  let textMod = ''
  if(successChance <= 0.33){
    textMod = 'text-danger'
  } else if (successChance <= 0.66){
    textMod = 'text-warning'
  } else {
    textMod = 'text-success'
  }
  let output = '<span class="font-w600 ' + textMod + '">' + (Math.round(successChance * 10000) / 100) + '%</span>'
  return output;
}

/**
 * Set the desired Trait
 * @param {*} index index of the trait from activeModifiers
 */
function CyrogemDesiredTrait(index){
  newDesireTrait = index
  document.getElementById('cyrogem-desire-display-span').textContent = activeModifiers[newDesireTrait]
  // Does this target specific skills
  //console.log(newDesireTrait)
  if (skillSpecificModifiers.includes(newDesireTrait)){
    console.log('more info needed')
    document.getElementById('cyrogem-desire-extra').className = ""
  } else {
    document.getElementById('cyrogem-desire-extra').className = "d-none"
  }
}

/**
 * Set the desired Skill
 * @param {*} index index of the skill from skillName
 */
function CyrogemDesiredSkill(index){
  //console.log(index)
  newDesireSkill = index
  document.getElementById('cyrogem-desire-extra-span').textContent = skillName[newDesireSkill]
}

/**
 * Set the desired value to the input field value
 */
function CyrogemDesiredValue(){
  const valueHTML = document.getElementById('cyrogem-desire-value')
  newDesireValue = valueHTML.value
  document.getElementById('cyrogem-desire-display-value').textContent = newDesireValue
}

/**
 * Set the desired tries to the input field value
 */
function CyrogemDesiredTries(){
  const valueHTML = document.getElementById('cyrogem-iterations')
  iterations = valueHTML.value
  document.getElementById('cyrogem-iterations-display').textContent = iterations
  CyrogemUpdatePrediction()
}

/**
 * Choose an equipment slot to roll for
 * @param {*} slot index of slot
 */
function CyrogemSelectSlot(slot){
  newDesireSlot = slot
  let slotName = 'Slot'
  switch(slot){
    case 0: slotName = 'Helmet'; break;
    case 1: slotName = 'Body'; break;
    case 2: slotName = 'Legs'; break;
    case 3: slotName = 'Boots'; break;
    case 4: slotName = 'Weapon'; break;
    case 5: slotName = 'Shield'; break;
    case 6: slotName = 'Amulet'; break;
    case 7: slotName = 'Ring'; break;
    case 8: slotName = 'Gloves'; break;
    case 9: slotName = 'Quiver'; break;
    case 10: slotName = 'Cape'; break;
  }
  document.getElementById('cyrogem-desire-slot-span').textContent = slotName
  CyrogemUpdatePrediction()
}

function CyrogemSkipModifierInDesireList(modifier) {
  if (modifier.includes('aprilFools')) {
      return true;
  }

  let decreaseGood = false;
  if (modifier.includes('PlayerAttackSpeed') || modifier.includes('MonsterRespawnTimer') || modifier.includes('SkillInterval')) {
      decreaseGood = true;
  };

  if (decreaseGood) {
      return modifier.includes('increase');
  } else {
      return modifier.includes('decrease');
  }
}

/**
 * Load the tool, setup references, inject HTML
 */
function CyrogemLoadCorruptionHelper () {
  let bannedModifiers = ["golbinRaidWaveSkipCostReduction", "golbinRaidIncreasedMinimumFood", "golbinRaidIncreasedMaximumAmmo", "golbinRaidIncreasedMaximumRunes", "golbinRaidPrayerUnlocked", "golbinRaidIncreasedPrayerLevel", "golbinRaidIncreasedPrayerPointsStart", "golbinRaidIncreasedPrayerPointsWave", "golbinRaidPassiveSlotUnlocked", "golbinRaidIncreasedStartingRuneCount", "golbinRaidStartingWeapon", "freeBonfires", "autoSlayerUnlocked", "increasedEquipmentSets", "dungeonEquipmentSwapping", "increasedTreeCutLimit", "increasedAttackRolls", "decreasedAttackRolls", "increasedBankSpaceShop", "decreasedBankSpaceShop", "increasedGPFromSales", "decreasedGPFromSales", ];
  activeModifiers = [];
  for (let i = 0; i < Object.keys(playerModifiersTemplate).length; i++) {
    if (!bannedModifiers.includes(Object.keys(playerModifiersTemplate)[i]))
    activeModifiers.push(Object.keys(playerModifiersTemplate)[i]);
  }

  // Setup the entire block
  let playerDesiresHTML =
  '<div class="block block-rounded block-link-pop border-top border-info border-4x row no-gutters">'

  // First Column
  playerDesiresHTML +=
  '<div class="col-sm-6 col-lg-4"><div class=block-content>'
  // Setup Desire Dropdown
  playerDesiresHTML +=
  '<div class="dropdown"><button type="button" class="btn btn-secondary dropdown-toggle mt-2" id="cyrogem-desire-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Desired Attribute</button>' +
  '<div class="dropdown-menu font-size-sm" aria-labelledby="cyrogem-desire-dropdown" style="max-height: 800px; overflow-y: scroll;">'
  for (let i = 0; i < activeModifiers.length; i++){
    if (CyrogemSkipModifierInDesireList(activeModifiers[i])) continue;
    playerDesiresHTML += '<a class="dropdown-item" id="cyrogemDesiredTrait' + i + '" style="text-transform: capitalize;">' + activeModifiers[i] + '</a>'
  }
  playerDesiresHTML += '</div></div>' +
  '<span class="font-w400 font-size-sm text-combat-smoke ml-2">Desire: <span id="cyrogem-desire-display-span" style="text-transform: capitalize;">Select One</span></span>'
  // Oh god we have to add another check for specific skill ones like "increasedSmithingMasteryXP"
  playerDesiresHTML +=
  '<div class="d-none" id="cyrogem-desire-extra">' +
  // We need a new dropdown for all the skills
  '<div class="dropdown"><button type="button" class="btn btn-secondary dropdown-toggle mt-2" id="cyrogem-desire-extra-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Skill</button>' +
  '<div class="dropdown-menu font-size-sm" aria-labelledby="cyrogem-desire-extra-dropdown">'
  for (let i = 0; i < skillName.length; i++){
    playerDesiresHTML += '<a class="dropdown-item" id="cyrogemDesiredSkill' + i + '" style="text-transform: capitalize;">' + skillName[i] + '</a>'
  }
  playerDesiresHTML += '</div></div>' +
  '<span class="font-w400 font-size-sm text-combat-smoke ml-2">Target Skill: <span id="cyrogem-desire-extra-span" style="text-transform: capitalize;">Select One</span></span>' +
  '</div>'

  // What value do you want
  playerDesiresHTML += '<div class="col-12"><input type="number" class="form-control m-1" id="cyrogem-desire-value" placeholder="0"></div>' +
  '<span class="font-w400 font-size-sm text-combat-smoke ml-2">Minimum Level: <span id="cyrogem-desire-display-value">Enter above</span></span>'

  // What slot are we changing
  playerDesiresHTML +=
  '<div class="col-12"><button type="button" class="swal2-confirm swal2-styled" id="cyrogem-add-desire" aria-label="" style="display: inline-block; border-left-color: rgb(48, 133, 214); border-right-color: rgb(48, 133, 214);">Add Desire</button></div>'

  // Close the content block and column div
  playerDesiresHTML += '</div></div>'


  // Second column
  playerDesiresHTML += '<div class="col-sm-3"><div class=block-content>' +
  '<div class="col-12"><input type="number" class="form-control m-1" id="cyrogem-iterations" placeholder="0"></div>' +
  '<span class="font-w400 font-size-sm text-combat-smoke ml-2">Try how many times<br>(Assuming you can afford it): <span id="cyrogem-iterations-display">0</span></span>'

  playerDesiresHTML += '<div class="dropdown"><button type="button" class="btn btn-secondary dropdown-toggle mt-2" id="cyrogem-slot-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" onclick="">' +
  'Slot to Roll For</button><div class="dropdown-menu font-size-sm" aria-labelledby="cyrogem-slot-dropdown">' +
  '<a class="dropdown-item" id="cyrogemSelectSlot0">Helmet</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot1">Body</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot2">Legs</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot3">Boots</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot4">Weapon</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot5">Shield</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot6">Amulet</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot7">Ring</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot8">Gloves</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot9">Quiver</a>' +
  '<a class="dropdown-item" id="cyrogemSelectSlot10">Cape</a>' +
  '</div></div>'+
  '<span class="font-w400 font-size-sm text-combat-smoke ml-2">Slot: <span id="cyrogem-desire-slot-span">Select One</span></span>'

  // Close the block and column div
  playerDesiresHTML += '</div></div>'


  // Third column
  playerDesiresHTML += '<div class="col-md-5"><div class=block-content>'

  // Display current wishes
  playerDesiresHTML += '<h5 class="font-w700 font-size-sm text-center text-success m-1 mb-2">Desired Mods, Must have one, select any mod to remove it</h5>' +
  '<div class="text-center" id="cyrogem-current-desires"></div>'

  // Close the block and column div
  playerDesiresHTML += '</div></div>'

  // NEW ROW
  playerDesiresHTML += '<div class="col-12"><div class="block-content text-center">'

  // Display aggregate info
  playerDesiresHTML += '<div class="col-md-10" id="cyrogem-predictive-display"></div>' +
  '<div class="col-md-2">' + // Button to roll
  '<button type="button" class="swal2-confirm swal2-styled" id="cyrogem-roll-button" aria-label="" style="display: inline-block; border-left-color: rgb(48, 133, 214); border-right-color: rgb(48, 133, 214);">Roll For It</button></div>'
  // Close the column, row, and box div
  playerDesiresHTML += '</div></div></div>'

  //document.getElementById('aprilfools2021-container').insertAdjacentHTML('afterbegin', playerDesiresHTML)
  playerDesiresHTML += document.getElementById('aprilfools2021-container').innerHTML
  document.getElementById('aprilfools2021-container').innerHTML = playerDesiresHTML


  // Setup trait links
  for(let i = 0; i < activeModifiers.length; i++){
    //console.log(i)
    let option = document.getElementById('cyrogemDesiredTrait' + i)
    if(option !== null){
      option.addEventListener("click", () => CyrogemDesiredTrait(i))
    }
  }
  // Setup skill links
  for(let i = 0; i < skillName.length; i++){
    let option = document.getElementById('cyrogemDesiredSkill' + i)
    if(option !== null){
      option.addEventListener("click", () =>  CyrogemDesiredSkill(i))
    }
  }
  // Setup slot links
  for(let i = 0; i <= 10; i++){
    //console.log(i)
    document.getElementById('cyrogemSelectSlot' + i).addEventListener("click", () => CyrogemSelectSlot(i))
  }
  // Setup value link
  document.getElementById('cyrogem-desire-value').addEventListener("input", () => CyrogemDesiredValue())
  document.getElementById('cyrogem-desire-value').addEventListener("change", () => CyrogemDesiredValue())
  // Setup quantity link
  document.getElementById('cyrogem-iterations').addEventListener("input", () => CyrogemDesiredTries())
  // Setup roll button
  document.getElementById('cyrogem-roll-button').addEventListener("click", () => CyrogemRollCorruption())
  // Setup desire button
  document.getElementById('cyrogem-add-desire').addEventListener("click", () => CyrogemAddDesire())

  CyrogemUpdatePrediction()
}

/**
 * Inject after page loads
 */
(function () {
  function loadScript() {
      //console.log(window.isLoaded + ' ' + !window.currentlyCatchingUp);// + ' ' + (typeof unsafeWindow !== 'undefined') ? ' ' + unsafeWindow.isLoaded + ' ' + !unsafeWindow.currentlyCatchingUp : '')
      if ((window.isLoaded && !window.currentlyCatchingUp)
          || (typeof unsafeWindow !== 'undefined' && unsafeWindow.isLoaded && !unsafeWindow.currentlyCatchingUp)) {
          // Only load script after game has opened
          clearInterval(scriptLoader);
          document.body.insertAdjacentHTML('beforeend', '<template id="cyrogem-button-template"><button class="btn btn-dark m-1" aria-label="">Button</button></template>')
          CyrogemLoadCorruptionHelper();
      }
  }

  const scriptLoader = setInterval(loadScript, 200);
})();

// Melvor backend code copied for quick reference
/*
function rollRandomModifiers(count=3, key, equipmentSlot=0) {
  if (key === "equipment") {
    if (randomModifiers.equipment[equipmentSlot] === undefined)
    randomModifiers.equipment[equipmentSlot] = {};
    deleteKeysFromObject(randomModifiers.equipment[equipmentSlot]);
  }
  let bannedModifiers = ["golbinRaidWaveSkipCostReduction", "golbinRaidIncreasedMinimumFood", "golbinRaidIncreasedMaximumAmmo", "golbinRaidIncreasedMaximumRunes", "golbinRaidPrayerUnlocked", "golbinRaidIncreasedPrayerLevel", "golbinRaidIncreasedPrayerPointsStart", "golbinRaidIncreasedPrayerPointsWave", "golbinRaidPassiveSlotUnlocked", "golbinRaidIncreasedStartingRuneCount", "golbinRaidStartingWeapon", "freeBonfires", "autoSlayerUnlocked", "increasedEquipmentSets", "dungeonEquipmentSwapping", "increasedTreeCutLimit", "increasedAttackRolls", "decreasedAttackRolls", "increasedBankSpaceShop", "decreasedBankSpaceShop", "increasedGPFromSales", "decreasedGPFromSales", ];
  let activeModifiers = [];
  for (let i = 0; i < Object.keys(playerModifiersTemplate).length; i++) {
    if (!bannedModifiers.includes(Object.keys(playerModifiersTemplate)[i]))
        activeModifiers.push(Object.keys(playerModifiersTemplate)[i]);
  }
  let rng = [];
  for (let i = 0; i < count; i++) {
      let rngMod = Math.floor(Math.random() * activeModifiers.length);
      let value;
      let maxValue = getRandomModifierMaxValue(count);
      if (playerModifiersTemplate[activeModifiers[rngMod]].length)
      // ------------ value is skill index 0, value index 1 ----------------------------------------------------------------------------------------------------------------------
          value = [[Math.floor(Math.random() * 21), Math.floor(Math.random() * maxValue)]];
      else
          value = Math.floor(Math.random() * maxValue);
      if ((activeModifiers[rngMod] === "increasedMaxHitFlat" || activeModifiers[rngMod] === "increasedMaxHitpoints") && value > 10)
          value = 10;
      if ((activeModifiers[rngMod] === "decreasedMaxHitFlat" || activeModifiers[rngMod] === "decreasedMaxHitpoints") && value > 10)
          value = 10;
      rng.push({
          modifier: activeModifiers[rngMod],
          value: value
      });
      if (key === "equipment")
          randomModifiers.equipment[equipmentSlot][activeModifiers[rngMod]] = value;
  }
  return rng;
}

function getEquipmentCorruption2(equipmentSlot) {
  let cost = getRandomModifierCost(equipmentSlot);
  if (gp >= cost && equippedItems[equipmentSlot] > 0) {
      gp -= cost;
      updateGP();
      let tier = getRandomModifierTier(equipmentSlot);
      let chanceToDestroy = getRandomModifiersDestroyChance(tier);
      if (rollPercentage(chanceToDestroy)) {
          let qty = getItemQtyRandomModifier(equippedItems[equipmentSlot]);
          if (qty[0] > 1) {
              if (equipmentSlot === CONSTANTS.equipmentSlot.Quiver && ammo > 1) {
                  ammo--;
                  equipmentSets[selectedEquipmentSet].ammo--;
                  updateAmmo();
              } else
                  updateItemInBank(qty[1], equippedItems[equipmentSlot], -1);
          } else {
              equippedItems[equipmentSlot] = 0;
              equipmentSets[selectedEquipmentSet].equipment[equipmentSlot] = 0;
              setEquipmentSet(selectedEquipmentSet);
          }
          updateRandomModifierInfo(equipmentSlot);
          notifyPlayer(CONSTANTS.skill.Attack, "Your item was destroyed :(", "danger");
      } else {
          let mods = rollRandomModifiers(tier, "equipment", equipmentSlot);
          updateHTMLRandomMod(equipmentSlot, mods);
      }
  }
}
function loadCorruption() {
  for (let i = 0; i < Object.keys(randomModifiers.equipment).length; i++) {
      let html = `<h5 class="font-w600 font-size-sm mb-2">Current Modifiers:</h5>`;
      for (let j = 0; j < Object.keys(randomModifiers.equipment[Object.keys(randomModifiers.equipment)[i]]).length; j++) {
          let modifier = printPlayerModifier(Object.keys(randomModifiers.equipment[Object.keys(randomModifiers.equipment)[i]])[j], randomModifiers.equipment[Object.keys(randomModifiers.equipment)[i]][Object.keys(randomModifiers.equipment[Object.keys(randomModifiers.equipment)[i]])[j]]);
          html += `<h5 class="font-w400 font-size-sm mb-1 ${modifier[1]}">${modifier[0]}</h5>`;
      }
      $("#corruption-equipment-slot-" + Object.keys(randomModifiers.equipment)[i]).html(html);
  }
}
function updateHTMLRandomMod(equipmentSlot, mods) {
  let html = `<h5 class="font-w600 font-size-sm mb-2">Current Modifiers:</h5>`;
  for (let i = 0; i < mods.length; i++) {
      if (mods[i].value.length)
          modifier = printPlayerModifier(mods[i].modifier, mods[i].value[0]);
      else
          modifier = printPlayerModifier(mods[i].modifier, mods[i].value);
      html += `<h5 class="font-w400 font-size-sm mb-1 ${modifier[1]}">${modifier[0]}</h5>`;
  }
  $("#corruption-equipment-slot-" + equipmentSlot).html(html);
  updatePlayerStats();
}
function getRandomModifiersDestroyChance(tier) {
  let chance = 0;
  if (tier >= 4)
      chance = 10;
  else if (tier >= 3)
      chance = 20;
  else if (tier >= 2)
      chance = 30;
  else if (tier >= 1)
      chance = 40;
  return chance;
}
function getRandomModifierMaxValue(tier) {
  let value = 0;
  if (tier >= 4)
      value = 100;
  else if (tier >= 3)
      value = 76;
  else if (tier >= 2)
      value = 51;
  else if (tier >= 1)
      value = 31;
  return value;
}
function getRandomModifierCost(equipmentSlot) {
  let cost = 0;
  if (equippedItems[equipmentSlot] <= 0)
      return cost;
  cost = items[equippedItems[equipmentSlot]].sellsFor;
  return cost;
}
function getRandomModifierTier(equipmentSlot) {
  let tier = 0;
  if (equippedItems[equipmentSlot] <= 0)
      return tier;
  if (items[equippedItems[equipmentSlot]].sellsFor >= 400000)
      tier = 4;
  else if (items[equippedItems[equipmentSlot]].sellsFor >= 10000)
      tier = 3;
  else if (items[equippedItems[equipmentSlot]].sellsFor >= 200)
      tier = 2;
  else
      tier = 1;
  return tier;
}
function updateRandomModifierInfo(equipmentSlot) {
  if (equippedItems[equipmentSlot] > 0) {
      let cost = getRandomModifierCost(equipmentSlot);
      let tier = getRandomModifierTier(equipmentSlot);
      let qty = getItemQtyRandomModifier(equippedItems[equipmentSlot]);
      $("#corruption-equipment-slot-" + equipmentSlot + "-img").attr("src", items[equippedItems[equipmentSlot]].media);
      $("#corruption-equipment-slot-" + equipmentSlot + "-info").html(`Qty: ${qty[0]} | Tier: ${tier}<br>Cost: <img src="assets/media/main/coins.svg" class="skill-icon-xs mr-2">${numberWithCommas(cost)}`);
  } else {
      $("#corruption-equipment-slot-" + equipmentSlot + "-img").attr("src", "assets/media/bank/" + emptyGear[equipmentSlot] + ".svg");
      $("#corruption-equipment-slot-" + equipmentSlot + "-info").html(`Equip an item pls`);
  }
}
function getItemQtyRandomModifier(itemID) {
  let qty = 1;
  let bankID = getBankId(itemID);
  if (bankID >= 0)
      qty += bank[bankID].qty;
  if (items[itemID].equipmentSlot === CONSTANTS.equipmentSlot.Quiver)
      qty += ammo - 1;
  return [qty, bankID];
}
function deleteKeysFromObject(object) {
  Object.keys(object).forEach((el)=>{
      delete object[el];
  }
  );
}

*/

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址