// ==UserScript==
// @name Melvor Action Queue
// @version 0.1.0
// @description Adds an interface to queue up actions based on triggers you set
// @author 8992
// @match https://*.melvoridle.com/*
// @grant none
// @namespace http://tampermonkey.net/
// @noframes
// ==/UserScript==
let isVisible = false;
let currentActionIndex = 0;
let triggerCheckInterval = null;
let nameIncrement = 0;
let queueLoop = false;
let queuePause = false;
const actionQueueArray = [];
function getBankQty(id) {
const item = bank.find((a) => a.id == id);
return item ? item.qty : 0;
}
function checkAmmoQty(id) {
const set = equipmentSets.find((a) => a.equipment[CONSTANTS.equipmentSlot.Quiver] == id);
return set ? set.ammo : 0;
}
function checkFoodQty(id) {
const food = equippedFood.find((a) => a.itemID == id);
return food ? food.qty : 0;
}
function actionTab() {
if (!isVisible) {
changePage(3);
$("#settings-container").attr("class", "content d-none");
$("#header-title").text("Action Queue");
$("#header-icon").attr("src", "assets/media/skills/prayer/mystic_lore.svg");
$("#header-theme").attr("class", "content-header bg-combat");
$("#page-header").attr("class", "bg-combat");
document.getElementById("action-queue-container").style.display = "";
isVisible = true;
}
}
function hideActionTab() {
if (isVisible) {
document.getElementById("action-queue-container").style.display = "none";
isVisible = false;
}
}
const triggers = {
"Item Quantity": {},
"Skill Level": {},
"Skill XP": {},
"Equipped Item Quantity": {},
"Mastery Level": {
Cooking: {},
Crafting: {},
Farming: {},
Firemaking: {},
Fishing: {},
Fletching: {},
Herblore: {},
Mining: {},
Runecrafting: {},
Smithing: {},
Thieving: {},
Woodcutting: {},
},
};
const actions = {
"Start Skill": {
Cooking: {},
Crafting: {},
Firemaking: {},
Fishing: {},
Fletching: {},
Herblore: {},
Magic: {},
Mining: {},
Runecrafting: {},
Smithing: {},
Thieving: {},
Woodcutting: {},
},
"Start Combat": {},
"Switch Equipment Set": { 1: null, 2: null, 3: null },
"Equip Item": {},
"Unequip Item": {},
"Buy Item": {},
"Sell Item": {},
};
for (const a of items) {
triggers["Item Quantity"][a.name] = { "≥": null, "≤": null };
actions["Sell Item"][a.name] = null;
}
Object.keys(CONSTANTS.skill).forEach((a) => {
triggers["Skill Level"][a] = null;
triggers["Skill XP"][a] = null;
});
{
cookingItems.forEach((item) => {
triggers["Mastery Level"]["Cooking"][items[item.itemID].name] = null;
actions["Start Skill"]["Cooking"][items[item.itemID].name] = null;
});
craftingItems.forEach((item) => {
triggers["Mastery Level"]["Crafting"][items[item.itemID].name] = null;
actions["Start Skill"]["Crafting"][items[item.itemID].name] = null;
});
items.forEach((item) => {
if (item.type == "Seeds") {
triggers["Mastery Level"]["Farming"][item.name] = null;
}
});
items.forEach((item) => {
if (item.type == "Logs") {
triggers["Mastery Level"]["Firemaking"][item.name] = null;
actions["Start Skill"]["Firemaking"][item.name] = null;
}
});
fishingItems.forEach((item) => {
triggers["Mastery Level"]["Fishing"][items[item.itemID].name] = null;
actions["Start Skill"]["Fishing"][items[item.itemID].name] = null;
});
fletchingItems.forEach((item) => {
triggers["Mastery Level"]["Fletching"][items[item.itemID].name] = null;
actions["Start Skill"]["Fletching"][items[item.itemID].name] = null;
});
herbloreItemData.forEach((item) => {
triggers["Mastery Level"]["Herblore"][item.name] = null;
actions["Start Skill"]["Herblore"][item.name] = null;
});
miningData.forEach((item) => {
triggers["Mastery Level"]["Mining"][items[item.ore].name] = null;
actions["Start Skill"]["Mining"][items[item.ore].name] = null;
});
runecraftingItems.forEach((item) => {
triggers["Mastery Level"]["Runecrafting"][items[item.itemID].name] = null;
actions["Start Skill"]["Runecrafting"][items[item.itemID].name] = null;
});
smithingItems.forEach((item) => {
triggers["Mastery Level"]["Smithing"][items[item.itemID].name] = null;
actions["Start Skill"]["Smithing"][items[item.itemID].name] = null;
});
thievingNPC.forEach((npc) => {
triggers["Mastery Level"]["Thieving"][npc.name] = null;
actions["Start Skill"]["Thieving"][npc.name] = null;
});
items.forEach((item) => {
if (item.type == "Logs") {
triggers["Mastery Level"]["Woodcutting"][item.name] = null;
actions["Start Skill"]["Woodcutting"][item.name] = {};
}
});
for (const log in actions["Start Skill"]["Woodcutting"]) {
Object.keys(actions["Start Skill"]["Woodcutting"]).forEach((a) => (actions["Start Skill"]["Woodcutting"][log][a] = null));
}
}
ALTMAGIC.forEach((spell) => {
actions["Start Skill"]["Magic"][spell.name] = {};
if (spell.selectItem === 0) {
for (item of smithingItems) {
if (item.category === 0) {
actions["Start Skill"]["Magic"][spell.name][items[item.itemID].name] = null;
}
}
} else if (spell.isJunk) {
junkItems.forEach((a) => (actions["Start Skill"]["Magic"][spell.name][items[a].name] = null));
} else if (spell.selectItem === 1) {
actions["Start Skill"]["Magic"][spell.name] = actions["Sell Item"];
} else actions["Start Skill"]["Magic"][spell.name] = null;
});
for (const a of items.filter((a) => a.canEat)) {
triggers["Equipped Item Quantity"][a.name] = null;
actions["Equip Item"][a.name] = null;
}
for (const a of items.filter((a) => a.equipmentSlot == CONSTANTS.equipmentSlot.Quiver)) triggers["Equipped Item Quantity"][a.name] = null;
{
const monsterIDs = [];
for (const area of combatAreas) monsterIDs.push(...area.monsters);
for (const area of slayerAreas) monsterIDs.push(...area.monsters);
for (const monster of monsterIDs) actions["Start Combat"][MONSTERS[monster].name] = null;
for (const dungeon of DUNGEONS) actions["Start Combat"][dungeon.name] = null;
}
for (const a of items.filter((a) => a.hasOwnProperty("equipmentSlot"))) {
actions["Equip Item"][a.name] = null;
actions["Unequip Item"][a.name] = null;
}
class ShopItem {
constructor(gp, slayerCoins, req, buy) {
this.gp = gp;
this.slayerCoins = slayerCoins;
this.req = req;
this.buy = buy;
}
}
ShopItem.prototype.cost = function () {
return gp >= this.gp && slayerCoins >= this.slayerCoins && baseBankMax + bankMax > bank.length;
};
const shop = {};
{
shop["Upgrade Bank"] = new ShopItem(
0,
0,
() => {
if (currentGamemode === 1 && baseBankMax + bankMax >= 80) return false;
return gp >= Math.min(newNewBankUpgradeCost.level_to_gp(currentBankUpgrade + 1), 5000000);
},
() => upgradeBank(true)
);
shop["Upgrade Auto Eat"] = new ShopItem(
0,
0,
() => currentAutoEat < 3 && gp >= autoEatData[currentAutoEat].cost,
() => upgradeAutoEat(true)
);
shop["Equipment Set (gp)"] = new ShopItem(
equipmentSetData[0].cost,
0,
() => !equipmentSetsPurchased[0],
() => upgradeEquipmentSet(0, true)
);
shop["Equipment Set (slayer coins)"] = new ShopItem(
0,
equipmentSetData[1].cost,
() => !equipmentSetsPurchased[1],
() => upgradeEquipmentSet(1, true)
);
shop["Dungeon Equipment Swapping"] = new ShopItem(
equipmentSwapData[0].cost,
0,
() => !equipmentSetsPurchased[1],
() => upgradeEquipmentSwap(true)
);
shop["Multi-Tree"] = new ShopItem(
multiTreeCost[0],
0,
() => treeCutLimit < 2,
() => upgradeMultiTree(true)
);
godUpgradeData.forEach((upgrade, i) => {
shop[upgrade.name] = new ShopItem(
upgrade.cost,
0,
() => dungeonCompleteCount[upgrade.dungeonID] > 0 && !godUpgrade[i],
() => buyGodUpgrade(i, true)
);
});
shop["Upgrade Axe"] = new ShopItem(
0,
0,
() => currentAxe < 7 && gp >= axeCost[currentAxe + 1],
() => upgradeAxe(true)
);
shop["Upgrade Rod"] = new ShopItem(
0,
0,
() => currentRod < 7 && gp >= rodCost[currentRod + 1],
() => upgradeRod(true)
);
shop["Upgrade Pickaxe"] = new ShopItem(
0,
0,
() => currentPickaxe < 7 && gp >= pickaxeCost[currentPickaxe + 1],
() => upgradePickaxe(true)
);
shop["Upgrade Cooking Fire"] = new ShopItem(
0,
0,
() => {
return (
currentCookingFire < 9 &&
gp >= cookingFireData[currentCookingFire].costGP &&
skillLevel[CONSTANTS.skill.Firemaking] >= cookingFireData[currentCookingFire].fmLevel &&
getBankQty(cookingFireData[currentCookingFire].costLogs[0]) >= cookingFireData[currentCookingFire].costLogs[1]
);
},
() => upgradeCookingFire(true)
);
for (const itemID of slayerItems) {
shop[items[itemID].name] = new ShopItem(
0,
items[itemID].slayerCost,
() => true,
() => buySlayerItem(itemID, true)
);
}
gloveID.forEach((itemID, i) => {
shop[items[itemID].name] = new ShopItem(
glovesCost[i],
0,
() => true,
() => buyGloves(i, true)
);
});
skillcapeItems.forEach((itemID, i) => {
shop[items[itemID].name] = new ShopItem(
items[itemID].buysFor,
0,
() => true,
() => buySkillcape(i, true)
);
});
shop[items[CONSTANTS.item.Feathers].name] = new ShopItem(
0,
0,
(qty) => gp >= items[CONSTANTS.item.Feathers].buysFor * qty,
(qty) => {
updateBuyQty(qty);
buyFeathers(true);
}
);
shop[items[CONSTANTS.item.Compost].name] = new ShopItem(
0,
0,
(qty) => gp >= items[CONSTANTS.item.Compost].buysFor * qty,
(qty) => {
updateBuyQty(qty);
buyCompost(true);
}
);
shop[items[CONSTANTS.item.Weird_Gloop].name] = new ShopItem(
0,
0,
(qty) => {
for (const a of items[CONSTANTS.item.Weird_Gloop].buysForItems) {
if (getBankQty(a[0]) < qty * a[1]) return false;
}
return gp >= items[CONSTANTS.item.Weird_Gloop].buysFor * qty;
},
(qty) => {
updateBuyQty(qty);
buyItem(CONSTANTS.item.Weird_Gloop, true);
}
);
shop[items[CONSTANTS.item.Bowstring].name] = new ShopItem(
0,
0,
(qty) => gp >= items[CONSTANTS.item.Bowstring].buysFor * qty,
(qty) => {
updateBuyQty(qty);
buyBowstring(true);
}
);
shop[items[CONSTANTS.item.Leather].name] = new ShopItem(
0,
0,
(qty) => gp >= items[CONSTANTS.item.Leather].buysFor * qty,
(qty) => {
updateBuyQty(qty);
buyLeather(true);
}
);
[CONSTANTS.item.Green_Dragonhide, CONSTANTS.item.Blue_Dragonhide, CONSTANTS.item.Red_Dragonhide].forEach((itemID) => {
shop[items[itemID].name] = new ShopItem(
0,
0,
(qty) => gp >= items[itemID].buysFor * qty,
(qty) => {
updateBuyQty(qty);
buyDhide(itemID, true);
}
);
});
shop[items[CONSTANTS.item.Red_Party_Hat].name] = new ShopItem(
0,
0,
(qty) => gp >= items[CONSTANTS.item.Red_Party_Hat].buysFor * qty,
(qty) => {
updateBuyQty(qty);
buyPartyHat(true);
}
);
}
for (const name in shop) actions["Buy Item"][name] = null;
function setTrigger(catagory, name, greaterThan, masteryItem, number) {
const itemID = items.findIndex((a) => a.name == name);
number = parseInt(number, 10);
switch (catagory) {
case "Item Quantity":
if (greaterThan == "≥") {
return () => {
return getBankQty(itemID) >= number;
};
}
return () => {
return getBankQty(itemID) <= number;
};
case "Skill Level":
const xp = exp.level_to_xp(number);
return () => {
return skillXP[CONSTANTS.skill[name]] >= xp;
};
case "Skill XP":
return () => {
return skillXP[CONSTANTS.skill[name]] >= number;
};
case "Equipped Item Quantity":
if (items.filter((a) => a.canEat).find((a) => a.name == name)) {
return () => {
return checkFoodQty(itemID) <= number;
};
}
return () => {
return checkAmmoQty(itemID) <= number;
};
case "Mastery Level":
return setMasteryTrigger(name, number, masteryItem);
}
}
function setMasteryTrigger(name, number, masteryItem) {
const itemID = items.findIndex((a) => a.name == masteryItem);
let masteryID = 0;
switch (name) {
case "Cooking":
masteryID = cookingItems.find((a) => a.itemID == itemID).cookingID;
return () => {
return cookingMastery[masteryID].mastery >= number;
};
case "Crafting":
masteryID = craftingItems.find((a) => a.itemID == itemID).craftingID;
return () => {
return craftingMastery[masteryID].mastery >= number;
};
case "Farming":
masteryID = items[itemID].masteryID >= number;
return () => {
return farmingMastery[masteryID].mastery >= number;
};
case "Firemaking":
masteryID = itemID;
return () => {
return logsMastery[masteryID].mastery >= number;
};
case "Fishing":
masteryID = fishingItems.find((a) => a.itemID == itemID).fishingID;
return () => {
return fishMastery[masteryID].mastery >= number;
};
case "Fletching":
masteryID = fletchingItems.find((a) => a.itemID == itemID).fletchingID;
return () => {
return fletchingMastery[masteryID].mastery >= number;
};
case "Herblore":
masteryID = herbloreItemData.findIndex((a) => a.name == masteryItem);
return () => {
return herbloreMastery[masteryID].mastery >= number;
};
case "Mining":
masteryID = miningData.find((a) => a.ore == itemID).masteryID;
return () => {
return miningOreMastery[masteryID].mastery >= number;
};
case "Runecrafting":
masteryID = runecraftingItems.find((a) => a.itemID == itemID).runecraftingID;
return () => {
return runecraftingMastery[masteryID].mastery >= number;
};
case "Smithing":
masteryID = smithingItems.find((a) => a.itemID == itemID).smithingID;
return () => {
return smithingMastery[masteryID].mastery >= number;
};
case "Thieving":
masteryID = thievingNPC.findIndex((a) => a.name == masteryItem);
return () => {
return thievingMastery[masteryID].mastery >= number;
};
case "Woodcutting":
masteryID = itemID;
return () => {
return treeMasteryData[masteryID].mastery >= number;
};
}
}
function setAction(actionCatagory, actionName, skillItem, skillItem2, qty) {
qty = parseInt(qty, 10);
const itemID = items.findIndex((a) => a.name == actionName);
switch (actionCatagory) {
case "Start Skill":
return setSkillAction(actionName, skillItem, skillItem2);
case "Start Combat":
const dungeonIndex = DUNGEONS.findIndex((a) => a.name == actionName);
const monsterIndex = MONSTERS.findIndex((a) => a.name == actionName);
return () => {
if (dungeonIndex >= 0) {
if (
DUNGEONS[dungeonIndex].requiresCompletion === undefined ||
dungeonCompleteCount[DUNGEONS[dungeonIndex].requiresCompletion] >= 1
) {
selectDungeon(dungeonIndex, true);
return true;
}
}
for (const area of combatAreas) {
if (area.monsters.includes(monsterIndex)) {
selectMonster(monsterIndex);
return true;
}
}
for (const area of slayerAreas) {
if (skillLevel[CONSTANTS.skill.Slayer] >= area.slayerLevel && area.monsters.includes(monsterIndex)) {
selectMonster(monsterIndex);
return true;
}
}
return false;
};
case "Switch Equipment Set":
const equipSet = parseInt(actionName) - 1;
return () => {
if (equipmentSetCount >= equipSet) {
setEquipmentSet(equipSet);
return true;
}
return false;
};
case "Equip Item":
return () => {
const bankID = getBankId(itemID);
if (bankID === false) return false;
if (items[itemID].canEat) {
equipFood(bankID, itemID, bank[bankID].qty);
return getBankQty(itemID) > 0; //returns false if there is any of the item left in bank (couldn't equip)
}
if (items[itemID].equipmentSlot == CONSTANTS.equipmentSlot.Quiver) {
equipItem(bankID, itemID, bank[bankID].qty, -1);
return getBankQty(itemID) > 0; //returns false if there is any of the item left in bank (couldn't equip)
}
equipItem(bankID, itemID, 1, selectedEquipmentSet);
return equippedItems[items[itemID].equipmentSlot] === itemID; //returns false if the item is not equipped
};
case "Unequip Item":
return () => {
const i = equippedItems.findIndex((a) => a == itemID);
if (i >= 0) {
unequipItem(i);
return getBankQty(itemID) > 0; //returns false if there is 0 of the items in bank (no space to unequip)
}
return false;
};
case "Buy Item":
return () => {
if (!shop[actionName].req(qty) || !shop[actionName].cost()) return false;
shop[actionName].buy(qty);
return true;
};
case "Sell Item":
return () => {
if (!bank.find((a) => a.id == itemID)) return false;
sellItem(itemID);
if (showSaleNotifications) swal.clickConfirm();
return true;
};
}
}
function setSkillAction(actionName, skillItem, skillItem2) {
const itemID = items.findIndex((a) => a.name == skillItem);
let actionID = 0;
switch (actionName) {
case "Cooking":
actionID = cookingItems.find((a) => a.itemID == itemID).cookingID;
return () => {
if (!skillLevel[CONSTANTS.skill.Cooking] >= items[itemID].cookingLevel) return false;
if (selectedFood !== itemID) selectFood(itemID);
if (!isCooking) startCooking(0, false);
return true;
};
case "Crafting":
actionID = craftingItems.find((a) => a.itemID == itemID).craftingID;
return () => {
if (!skillLevel[CONSTANTS.skill.Crafting] >= craftingItems[actionID].craftingLevel) return false;
if (selectedCraft !== actionID) selectCraft(actionID);
if (!isCrafting) startCrafting(true);
return true;
};
case "Firemaking":
actionID = items[itemID].firemakingID;
return () => {
if (!skillLevel[CONSTANTS.skill.Firemaking] >= logsData[actionID].level) return false;
if (selectedLog !== targetLog) selectLog(targetLog);
if (isBurning) burnLog(false);
if (!isBurning) burnLog(false);
return true;
};
case "Fishing":
const fishID = fishingItems.find((a) => a.itemID == itemID).fishingID;
const areaID = fishingAreas.findIndex((a) => a.fish.includes(fishID));
return () => {
if (
(!equippedItems.includes(CONSTANTS.item.Barbarian_Gloves) && areaID == 6) ||
(!secretAreaUnlocked && areaID == 7) ||
!skillLevel[CONSTANTS.skill.Fishing] >= fishingItems[fishID].fishingLevel
)
return false;
if (!isFishing) {
selectFish(areaID, fishID);
startFishing(areaID, fishID, true);
}
if (isFishing && (areaID != offline.action[0] || fishID != offline.action[1])) {
startFishing(offline.action[0], offline.action[1], true);
selectFish(areaID, fishID);
startFishing(areaID, fishID, true);
}
return true;
};
case "Fletching":
actionID = fletchingItems.find((a) => a.itemID == itemID).fletchingID;
return () => {
if (!skillLevel[CONSTANTS.skill.Fletching] >= fletchingItems[actionID].fletchingLevel) return false;
if (selectedFletch !== actionID) selectFletch(actionID);
if (!isFletching) startFletching(true);
return true;
};
case "Herblore":
actionID = herbloreItemData.findIndex((a) => a.name == skillItem);
return () => {
if (!skillLevel[CONSTANTS.skill.Herblore] >= herbloreItemData[actionID].herbloreLevel) return false;
if (selectedHerblore !== actionID) selectHerblore(actionID);
if (!isHerblore) startHerblore(true);
return true;
};
case "Mining":
actionID = miningData.findIndex((a) => a.ore == itemID);
return () => {
if (
(actionID === 9 && !canMineDragonite()) ||
skillLevel[CONSTANTS.skill.Mining] < miningData[actionID].level ||
rockData[actionID].depleted
)
return false;
mineRock(actionID, true);
};
case "Magic":
actionID = ALTMAGIC.findIndex((a) => a.name == skillItem);
const magicItem = items.findIndex((a) => a.name == skillItem2);
return () => {
if (
skillLevel[CONSTANTS.skill.Magic] < ALTMAGIC[actionID].magicLevelRequired ||
skillLevel[CONSTANTS.skill.Smithing] < smithingItems.find((a) => a.smithingLevel == skillItem2)
)
return false;
if (selectedAltMagic !== actionID) selectMagic(actionID);
if (ALTMAGIC[actionID].selectItem >= 0 && selectedMagicItem[ALTMAGIC[actionID].selectItem] !== magicItem) {
if (getBankQty(magicItem) < 1 || lockedItems.includes(magicItem)) return false;
selectItemForMagic(ALTMAGIC[actionID].selectItem, magicItem, false);
}
if (!isMagic) castMagic(true);
return true;
};
case "Runecrafting":
actionID = runecraftingItems.findIndex((a) => a.itemID == itemID);
return () => {
if (!skillLevel[CONSTANTS.skill.Runecrafting] >= runecraftingItems[actionID].runecraftingLevel) return false;
if (selectedRunecraft !== actionID) selectRunecraft(actionID);
if (!isRunecrafting) startRunecrafting(true);
return true;
};
case "Smithing":
actionID = smithingItems.findIndex((a) => a.itemID == itemID);
return () => {
if (!skillLevel[CONSTANTS.skill.Smithing] >= smithingItems[actionID].smithingLevel) return false;
if (selectedSmith !== actionID) selectSmith(actionID);
if (!isSmithing) startSmithing(true);
return true;
};
case "Thieving":
actionID = thievingNPC.findIndex((a) => a.name == skillItem);
return () => {
if (!skillLevel[CONSTANTS.skill.Thieving] >= thievingNPC[actionID].level) return false;
if (isThieving) pickpocket(npcID);
pickpocket(actionID);
return true;
};
case "Woodcutting":
actionID = [itemID, items.findIndex((a) => a.name == skillItem2)];
return () => {
let result = true;
treeCuttingHandler.forEach((tree, i) => {
if (tree !== null && !actionID.slice(0, treeCutLimit).includes(i)) cutTree(i);
});
for (let i = 0; i < treeCutLimit; i++) {
if (treeCuttingHandler[actionID[i]] === null) {
if (skillLevel[CONSTANTS.skill.Woodcutting] >= trees[actionID[i]].level) {
cutTree(actionID[i]);
} else {
result = false;
}
}
}
return result;
};
}
}
class Action {
constructor(catagory, name, greaterThan, masteryItem, number, actionCatagory, actionName, skillItem, skillItem2, qty) {
switch (catagory) {
case "Item Quantity":
this.description = `If ${name} ${greaterThan} ${number}, `;
break;
case "Skill Level":
this.description = `If ${name} ≥ level ${number}, `;
break;
case "Skill XP":
this.description = `If ${name} ≥ ${number}xp, `;
break;
case "Equipped Item Quantity":
let plural = name;
if (!/s$/i.test(name)) plural += "s";
this.description = `If ≤ ${number} ${plural} equipped, `;
break;
case "Mastery Level":
this.description = `If ${name} ${masteryItem} mastery ≥ ${number}, `;
}
switch (actionCatagory) {
case "Start Skill":
this.description += `start ${actionName} ${skillItem}`;
if (actionName == "Woodcutting") this.description += ` & ${skillItem2}`;
if (actionName == "Magic") {
this.description += ` with ${skillItem2}`;
if (!/s$/i.test(skillItem2)) this.description += "s";
}
break;
case "Start Combat":
this.description += `start fighting ${actionName}`;
break;
case "Switch Equipment Set":
this.description += `switch to equipment set ${actionName}`;
break;
case "Equip Item":
this.description += `equip ${actionName} to current set`;
break;
case "Unequip Item":
this.description += `unequip ${actionName} from current set`;
break;
case "Buy Item":
if (qty > 1) {
this.description += `buy ${qty} ${actionName} from shop`;
} else {
this.description += `buy ${actionName} from shop`;
}
break;
case "Sell Item":
this.description += `sell ${actionName}`;
}
this.elementID = `AQ${nameIncrement}`;
nameIncrement++;
this.trigger = setTrigger(catagory, name, greaterThan, masteryItem, number);
this.startAction = setAction(actionCatagory, actionName, skillItem, skillItem2, qty); //function returns true if requirements were met and action executed otherwise false.
}
}
function resetForm() {
document.getElementById("aq-form").reset(); //reset form
[1, 2, 3, 4, 6, 7, 8, 9].forEach((a) => (document.getElementById(`aq-${a}`).type = "hidden")); //hide fields
for (let i = 0; i < 6; i++) document.getElementById(`aq-list-${i}`).innerHTML = ""; //empty dropdown lists
triggerTier = [null, null, null];
actionTier = [null, null, null, null];
}
function submitForm(event) {
const arr = [];
for (let i = 0; i < 10; i++) {
let e = event.target.elements[`aq-${i}`].value;
if (document.getElementById(`aq-${i}`).type == "text" && !validateOption(i, e)) return false;
e == "" ? arr.push(null) : arr.push(e);
}
if (arr[9] === null) arr[9] = 1;
addToQueue(new Action(...arr));
resetForm();
return false;
}
function validateOption(number, value) {
const list = document.getElementById(`aq-${number}`).getAttribute("list");
if (list === null) return true;
for (const a of document.getElementById(list).options) {
if (a.value === value) return true;
}
return false;
}
let triggerTier = [null, null, null];
let actionTier = [null, null, null, null];
function dropdowns(i, type, tier) {
let t = htmlChar(document.getElementById(`aq-${i}`).value);
let arr = [];
switch (type) {
case "trigger":
arr = [];
switch (tier) {
case 0:
arr = Object.keys(triggers);
break;
case 1:
arr = Object.keys(triggers[triggerTier[0]]);
break;
case 2:
arr = Object.keys(triggers[triggerTier[0]][triggerTier[1]]);
}
if (arr.includes(t) && triggerTier[tier] !== t) {
triggerTier[tier] = t;
if (triggerTier[0] == "Item Quantity") {
switch (tier) {
case 0:
if (document.getElementById(`aq-list-0`).childElementCount > 0) document.getElementById(`aq-list-0`).innerHTML = "";
Object.keys(triggers[t]).forEach((e) =>
document.getElementById(`aq-list-0`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[2, 3, 4].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-1`).type = "text";
break;
case 1:
if (document.getElementById(`aq-list-1`).childElementCount > 0) document.getElementById(`aq-list-1`).innerHTML = "";
Object.keys(triggers[triggerTier[0]][t]).forEach((e) =>
document.getElementById(`aq-list-1`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[3, 4].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-2`).type = "text";
break;
case 2:
[3].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-4`).type = "text";
}
} else if (triggerTier[0] == "Mastery Level") {
switch (tier) {
case 0:
if (document.getElementById(`aq-list-0`).childElementCount > 0) document.getElementById(`aq-list-0`).innerHTML = "";
Object.keys(triggers[t]).forEach((e) =>
document.getElementById(`aq-list-0`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[2, 3, 4].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-1`).type = "text";
break;
case 1:
if (document.getElementById(`aq-list-2`).childElementCount > 0) document.getElementById(`aq-list-2`).innerHTML = "";
Object.keys(triggers[triggerTier[0]][t]).forEach((e) =>
document.getElementById(`aq-list-2`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[2, 4].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-2`).type = "text";
break;
case 2:
[2].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-4`).type = "text";
}
} else {
switch (tier) {
case 0:
if (document.getElementById(`aq-list-0`).childElementCount > 0) document.getElementById(`aq-list-0`).innerHTML = "";
Object.keys(triggers[t]).forEach((e) =>
document.getElementById(`aq-list-0`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[2, 3, 4].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-1`).type = "text";
break;
case 1:
[2, 3].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-4`).type = "text";
}
}
}
break;
case "action":
arr = [];
switch (tier) {
case 0:
arr = Object.keys(actions);
break;
case 1:
arr = Object.keys(actions[actionTier[0]]);
break;
case 2:
arr = Object.keys(actions[actionTier[0]][actionTier[1]]);
break;
}
if (arr.includes(t) && actionTier[tier] !== t) {
actionTier[tier] = t;
if (actionTier[0] == "Start Skill") {
switch (tier) {
case 0:
if (document.getElementById(`aq-list-3`).childElementCount > 0) document.getElementById(`aq-list-3`).innerHTML = "";
Object.keys(actions[t]).forEach((e) =>
document.getElementById(`aq-list-3`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[7, 8, 9].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-6`).type = "text";
break;
case 1:
if (document.getElementById(`aq-list-4`).childElementCount > 0) document.getElementById(`aq-list-4`).innerHTML = "";
Object.keys(actions[actionTier[0]][t]).forEach((e) =>
document.getElementById(`aq-list-4`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[8, 9].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-7`).type = "text";
break;
case 2:
if (actions[actionTier[0]][actionTier[1]] !== null) {
if (document.getElementById(`aq-list-5`).childElementCount > 0) document.getElementById(`aq-list-5`).innerHTML = "";
Object.keys(actions[actionTier[0]][actionTier[1]][t]).forEach((e) =>
document.getElementById(`aq-list-5`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[9].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-8`).type = "text";
}
}
} else if (actionTier[0] == "Buy Item") {
switch (tier) {
case 0:
if (document.getElementById(`aq-list-3`).childElementCount > 0) document.getElementById(`aq-list-3`).innerHTML = "";
Object.keys(actions[t]).forEach((e) =>
document.getElementById(`aq-list-3`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[7, 8, 9].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-6`).type = "text";
break;
case 1:
[7, 8].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-9`).type = "text";
}
} else {
if (tier === 0) {
if (document.getElementById(`aq-list-3`).childElementCount > 0) document.getElementById(`aq-list-3`).innerHTML = "";
Object.keys(actions[t]).forEach((e) =>
document.getElementById(`aq-list-3`).insertAdjacentHTML("beforeend", `<option>${e}</option>`)
);
[7, 8, 9].forEach((e) => (document.getElementById(`aq-${e}`).type = "hidden"));
document.getElementById(`aq-6`).type = "text";
}
}
}
}
}
$("li.nav-main-item:contains(Bank)")[0].insertAdjacentHTML(
"afterend",
`
<li class="nav-main-item">
<a class="nav-main-link nav-compact" onclick="actionTab();">
<img class="nav-img" src="assets/media/skills/prayer/mystic_lore.svg">
<span class="nav-main-link-name">Action Queue</span>
<small id="current-queue" style="color: rgb(210, 106, 92);">inactive</small>
</a>
</li>`
);
document.getElementsByClassName("nav-main-link").forEach((element) => {
if (element.href.includes("changePage")) {
element.addEventListener("click", () => hideActionTab());
}
});
document.getElementsByClassName("btn btn-sm btn-light btn-combat-minibar-hp")[0].addEventListener("click", () => hideActionTab());
const aqHTML = `<div class="content" id="action-queue-container" style="display: none">
<div class="row row-deck">
<div class="col-md-12">
<div class="block block-rounded block-link-pop border-top border-settings border-4x">
<div class="block-content">
<div>
<form onSubmit="return submitForm(event)" id="aq-form">
<div style="display: inline-block; margin-left: 20px; height: 180px; vertical-align: top">
<h3 class="aq-header">Trigger</h3>
<div>
<input
type="text"
class="aq-dropdown"
id="aq-0"
required
list="trigger-catagory"
placeholder="Catagory"
oninput="dropdowns(0, 'trigger', 0)"
/>
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-1" list="aq-list-0" oninput="dropdowns(1, 'trigger', 1)" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-2" list="aq-list-1" oninput="dropdowns(2, 'trigger', 2)" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-3" list="aq-list-2" oninput="dropdowns(3, 'trigger', 2)" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-4" pattern="^\\d{1,10}$" required placeholder="number" />
</div>
</div>
<div style="display: inline-block; margin-left: 20px; height: 180px; vertical-align: top">
<h3 class="aq-header">Action</h3>
<div>
<input
type="text"
class="aq-dropdown"
id="aq-5"
required
list="action-catagory"
placeholder="Catagory"
oninput="dropdowns(5, 'action', 0)"
/>
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-6" list="aq-list-3" oninput="dropdowns(6, 'action', 1)" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-7" list="aq-list-4" oninput="dropdowns(7, 'action', 2)" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-8" list="aq-list-5" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-9" pattern="^\\d{1,10}$" placeholder="number" />
</div>
</div>
<div style="margin-left: 20px">
<input type="submit" class="btn btn-sm" style="background-color: #0083ff" value="Add to queue">
<button type="button" id="aq-pause" class="btn btn-sm" style="background-color: #e69621" onclick="togglePause()">Pause</button>
<button type="button" class="btn btn-sm" style="background-color: #676767" onclick="toggleLoop()">Toggle Looping</button>
<span id="aq-loop" style="font-size: 0.875rem; margin-left: 3px;">Looping disabled</span>
</div>
</form>
</div>
<h2 class="content-heading border-bottom mb-4 pb-2">Current Queue</h2>
<div style="min-height: 50px" id="aq-item-container"></div>
</div>
</div>
</div>
</div>
</div>
<datalist id="trigger-catagory"></datalist>
<datalist id="aq-list-0"></datalist>
<datalist id="aq-list-1"></datalist>
<datalist id="aq-list-2"></datalist>
<datalist id="action-catagory"></datalist>
<datalist id="aq-list-3"></datalist>
<datalist id="aq-list-4"></datalist>
<datalist id="aq-list-5"></datalist>
<style>
.aq-dropdown {
width: 260px;
}
.aq-header {
margin-bottom: 10px;
color: whitesmoke;
}
.aq-item {
display: flex;
justify-content: space-between;
background-color: #464646;
padding: 12px;
margin: 12px;
}
</style>
`;
document.getElementById("main-container").insertAdjacentHTML("beforeend", aqHTML);
Object.keys(triggers).forEach((a) => document.getElementById("trigger-catagory").insertAdjacentHTML("beforeend", `<option>${a}</option>`));
Object.keys(actions).forEach((a) => document.getElementById("action-catagory").insertAdjacentHTML("beforeend", `<option>${a}</option>`));
function deleteAction(id) {
const i = actionQueueArray.findIndex((a) => a.elementID == id);
if (i < 0) return;
actionQueueArray.splice(i, 1);
if (currentActionIndex > i) currentActionIndex--;
const element = document.getElementById(id);
if (element) element.remove();
updateQueue();
}
const replaceChar = [
{ reg: "'", replace: "'" },
];
function htmlChar(string) {
let s = string;
replaceChar.forEach(function (obj) {
const regEx = new RegExp(obj.reg + "(?!([^<]+)?>)", "g");
s = s.replace(regEx, obj.replace);
});
return s;
}
function addToQueue(obj) {
actionQueueArray.push(obj);
const element = `<div class="aq-item" id="${obj.elementID}">
<p style="margin: auto 0;">${obj.description}</p>
<div>
<small style="display: none; margin: auto;">failed requirement</small>
<button type="button" class="btn btn-sm btn-danger m-1" onclick="deleteAction('${obj.elementID}')">delete</button>
</div>
</div>`;
document.getElementById("aq-item-container").insertAdjacentHTML("beforeend", element);
if (triggerCheckInterval === null && !queuePause) {
currentActionIndex = 0;
updateQueue();
triggerCheckInterval = setInterval(() => {
triggerCheck();
}, 1000);
updateTextColour("start");
}
}
function triggerCheck() {
let result = true;
if (currentActionIndex >= actionQueueArray.length) {
if (queueLoop && actionQueueArray.length > 0) {
currentActionIndex = 0;
updateQueue();
return;
} else {
clearInterval(triggerCheckInterval);
triggerCheckInterval = null;
updateTextColour("stop");
return;
}
}
if (actionQueueArray[currentActionIndex].trigger()) {
result = actionQueueArray[currentActionIndex].startAction();
document.getElementById(actionQueueArray[currentActionIndex].elementID).children[1].children[0].style.display = result ? "none" : "";
currentActionIndex + 1 >= actionQueueArray.length && queueLoop ? (currentActionIndex = 0) : currentActionIndex++;
updateQueue();
}
}
function updateTextColour(type) {
switch (type) {
case "start":
document.getElementById("current-queue").style.color = "#46c37b";
document.getElementById("current-queue").innerHTML = "running";
break;
case "stop":
document.getElementById("current-queue").style.color = "#d26a5c";
document.getElementById("current-queue").innerHTML = "inactive";
break;
case "pause":
document.getElementById("current-queue").style.color = "#f3b760";
document.getElementById("current-queue").innerHTML = "paused";
}
}
function updateQueue() {
actionQueueArray.forEach((action, index) => {
if (index === currentActionIndex) {
document.getElementById(action.elementID).style.backgroundColor = "#385a0b";
} else document.getElementById(action.elementID).style.backgroundColor = "";
});
}
function toggleLoop() {
queueLoop = !queueLoop;
if (queueLoop) {
document.getElementById("aq-loop").innerHTML = "Looping enabled";
} else {
document.getElementById("aq-loop").innerHTML = "Looping disabled";
}
}
function togglePause() {
queuePause = !queuePause;
if (queuePause) {
clearInterval(triggerCheckInterval);
triggerCheckInterval = null;
document.getElementById("aq-pause").innerHTML = "Unpause";
document.getElementById("aq-pause").style.backgroundColor = "#5a9e00";
updateTextColour("pause");
} else {
if (actionQueueArray.length > 0) {
updateQueue();
triggerCheckInterval = setInterval(() => {
triggerCheck();
}, 1000);
updateTextColour("start");
} else {
updateTextColour("stop");
}
document.getElementById("aq-pause").innerHTML = "Pause";
document.getElementById("aq-pause").style.backgroundColor = "#e69621";
}
}