Melvormodoldactionscript
Melvormodoldactionscript
function checkAmmoQty(id) {
const set = player.equipmentSets.find((a) => a.slots.Quiver.item.id == id);
return set ? set.slots.Quiver.quantity : 0;
}
function checkFoodQty(id) {
const food = player.food.slots.find((a) => a.item.id == id);
return food ? food.quantity : 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 options = {
//trigger options for dropdown menus
triggers: {
Idle: null,
"Rock Mined Out": null,
"Test": null,
"Item Quantity": {},
"Skill Level": {},
"Skill XP": {},
"Mastery Level": {
Agility: {},
Astrology: {},
Cooking: {},
Crafting: {},
Farming: {},
Firemaking: {},
Fishing: {},
Fletching: {},
Herblore: {},
Mining: {},
Runecrafting: {},
Smithing: {},
Summoning: {},
Thieving: {},
Woodcutting: {},
},
"Mastery Pool %": {},
"Pet Unlocked": {},
"Equipped Item Quantity": {},
"Prayer Points": { "≥": "num", "≤": "num" },
"Potion Depleted": {
Agility: null,
Astrology: null,
Combat: null,
Cooking: null,
Crafting: null,
Farming: null,
Firemaking: null,
Fishing: null,
Fletching: null,
Herblore: null,
Mining: null,
Runecrafting: null,
Smithing: null,
Summoning: null,
Thieving: null,
Woodcutting: null,
},
"Enemy in Combat": {},
},
//action options for dropdown menus
actions: {
"Start Skill": {
Agility: null,
Astrology: {},
Cooking: {},
Crafting: {},
Firemaking: {},
Fishing: {},
Fletching: {},
Herblore: {},
Magic: {},
Mining: {},
Runecrafting: {},
Smithing: {},
Summoning: {},
Thieving: {},
Woodcutting: {},
},
"Start Combat": {
"Slayer Task": null,
},
"Change Attack Style": { "Select Spell": { Normal: {}, Curse: {}, Aurora: {},
Ancient: {} } },
"Switch Equipment Set": { 1: null, 2: null, 3: null },
"Equip Item": {},
"Equip Passive": {},
"Unequip Item": {},
"Buy Item": {},
"Sell Item": {},
"Use Potion": {},
"Activate Prayers": {},
"Build Agility Obstacle": {},
"Remove Agility Obstacle": {},
},
};
currentTrees.forEach((tree) => {
if (actionID.includes(tree)) {
actionID.splice(
actionID.findIndex((a) => a == tree),
1
);
} else {
actionID.unshift(tree);
}
});
actionID.forEach((i) => {
if (skillLevel[CONSTANTS.skill.Woodcutting] >= trees[i].level) {
cutTree(i);
} else {
result = false;
}
});
return result;
};
}
}
class Action {
/**
* Create an action object with trigger
* @param {string} category (Tier 1 option) category for trigger
* @param {string} name (Tier 2 option) skill/item name
* @param {string} greaterThan (Tier 3 option) either ≥ or ≤
* @param {string} masteryItem (Tier 3 option) target name for mastery
* @param {string} number (Tier 4 option) target number for skill/mastery/item
* @param {string} actionCategory (Tier 1 option) category for action
* @param {string} actionName (Tier 2 option) skill/monster/item/set name
* @param {string} skillItem (Tier 3 option) name for skilling action
* @param {string} skillItem2 (Tier 4 option) name of second tree to cut or
alt.magic item
* @param {string} qty (Tier 4 option) amount of item to buy if applicable
*/
constructor(
category,
name,
greaterThan,
masteryItem,
number,
actionCategory,
actionName,
skillItem,
skillItem2,
qty
) {
switch (category) {
case "Idle":
this.description = `If no active skills/combat:`;
break;
case "Rock Mined Out":
this.description = `If rock mined out`;
break;
case "Test":
this.description = `If test true`;
break;
case "Item Quantity":
this.description = `If ${name} ${greaterThan} ${number}:`;
break;
case "Prayer Points":
this.description = `If prayer points ${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}:`;
break;
case "Pet Unlocked":
this.description = `If ${name} unlocked:`;
break;
case "Mastery Pool %":
this.description = `If ${name} mastery pool ≥ ${number}%:`;
break;
case "Potion Depleted":
this.description = `If ${name} potion has depleted:`;
break;
case "Enemy in Combat":
this.description = `If fighting ${name}:`;
}
this.data = [category, name, greaterThan, masteryItem, number];
this.elementID = `AQ${nameIncrement++}`;
this.trigger = setTrigger(category, name, greaterThan, masteryItem, number);
if (typeof actionCategory == "string") {
this.action = [
{
elementID: `AQ${nameIncrement++}`,
data: [actionCategory, actionName, skillItem, skillItem2, qty],
start: setAction(actionCategory, actionName, skillItem, skillItem2, qty),
description: actionDescription(actionCategory, actionName, skillItem,
skillItem2, qty),
},
];
} else {
this.action = [];
}
}
}
function resetForm(arr) {
arr.forEach((menu) => {
document.getElementById(`aq-num${menu}`).type = "hidden";
document.getElementById(`aq-num${menu}`).value = "";
validInputs[menu].forEach((a, i) => {
if (i != 0) {
document.getElementById(`aq-text${menu}${i}`).type = "hidden"; //hide all
except first
document.getElementById(`aq-list${menu}${i}`).innerHTML = ""; //empty
datalists
}
document.getElementById(`aq-text${menu}${i}`).value = ""; //clear values
validInputs[menu][i] = null;
});
});
}
function submitForm() {
try {
//create array of the input values
const arr = [];
["A", "B"].forEach((menu) => {
for (let i = 0; i < validInputs[menu].length; i++)
arr.push(htmlChar(document.getElementById(`aq-text${menu}${i}`).value));
arr.push(document.getElementById(`aq-num${menu}`).value);
});
arr.splice(["≥", "≤", ""].includes(arr[2]) ? 3 : 2, 0, "");
/**
* Updates input text boxes
* @param {string} menu ('A'||'B'||'C')
*/
function dropdowns(menu) {
let obj;
if (menu == "A") {
obj = options.triggers;
} else if (menu == "B") {
obj = options.actions;
} else {
obj = options[currentlyEditing.type == "triggers" ? "triggers" : "actions"];
}
for (let i = 0; i < validInputs[menu].length; i++) {
const value = htmlChar(document.getElementById(`aq-text${menu}${i}`).value);
if (Object.keys(obj).includes(value)) {
if (obj[value] == "num") {
document.getElementById(`aq-num${menu}`).type = "text";
break;
}
if (obj[value] == null) break;
obj = obj[value];
if (validInputs[menu][i] != value) {
validInputs[menu][i] = value;
document.getElementById(`aq-text${menu}${i + 1}`).type = "text";
document.getElementById(`aq-list${menu}${i + 1}`).innerHTML = "";
Object.keys(obj).forEach((e) => {
document.getElementById(`aq-list${menu}${i +
1}`).insertAdjacentHTML("beforeend", `<option>${e}</option>`);
});
}
} else {
validInputs[menu][i] = null;
for (i++; i < validInputs[menu].length; i++) {
try {
validInputs[menu][i] = null;
document.getElementById(`aq-text${menu}${i}`).type = "hidden";
document.getElementById(`aq-text${menu}${i}`).value = "";
} catch {}
}
document.getElementById(`aq-num${menu}`).type = "hidden";
document.getElementById(`aq-num${menu}`).value = "";
}
}
}
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">
<form class="aq-mastery-config" id="aq-mastery-config-container"
style="display: none">
<div style="display: inline-block; margin-left: 20px; height: 180px;
vertical-align: top">
<h3 class="aq-header">Mastery Config</h3>
<div>
<select id="aq-skill-list" class="aq-select"></select>
</div>
<div>
<select id="aq-checkpoint-list" class="aq-select">
<option value="0">0%</option>
<option value="0.1">10%</option>
<option value="0.25">25%</option>
<option value="0.5">50%</option>
<option value="0.95">95%</option>
</select>
</div>
<div>
<select id="aq-mastery-strategy" class="aq-select">
<option value="false">Lowest mastery</option>
<option value="true">Custom priority</option>
</select>
</div>
<div>
<select id="aq-base" class="aq-select"></select>
</div>
<div>
<input type="text" id="aq-mastery-array" class="aq-select"/>
</div>
</div>
<div style="margin-left: 20px">
<button type="button" class="btn btn-sm aq-green" id="aq-config-
close">Done</button>
</div>
</form>
<form class="aq-popup" id="aq-edit-container" style="display: none;">
<div style="display: inline-block; margin-left: 20px; height: 180px;
vertical-align: top">
<h3 id="aq-edit-form" class="aq-header">Action</h3>
<div>
<input type="text" class="aq-dropdown" id="aq-textC0" required
list="aq-listC0" placeholder="Category" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-textC1" list="aq-
listC1" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-textC2" list="aq-
listC2" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-textC3" list="aq-
listC3" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-numC" pattern="^\\
d{1,10}$" placeholder="number" title="Positive integer"/>
</div>
</div>
<div style="margin-left: 20px">
<button type="button" id="aq-save-edit" class="btn btn-sm aq-
blue">Save</button>
<button type="button" id="aq-cancel" class="btn btn-sm btn-
danger">Cancel</button>
</div>
</form>
<div class="block-content">
<div>
<form 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-textA0" required
list="aq-listA0" placeholder="Category" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-textA1" list="aq-
listA1" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-textA2" list="aq-
listA2" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-numA" pattern="^\\
d{1,10}$" placeholder="number" title="Positive integer"/>
</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-textB0" required
list="aq-listB0" placeholder="Category" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-textB1" list="aq-
listB1" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-textB2" list="aq-
listB2" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-textB3" list="aq-
listB3" />
</div>
<div>
<input type="hidden" class="aq-dropdown" id="aq-numB" pattern="^\\
d{1,10}$" placeholder="number" title="Positive integer"/>
</div>
</div>
<div style="margin-left: 20px">
<input type="submit" class="btn btn-sm aq-blue" value="Add to queue">
<button type="button" id="aq-pause" class="btn btn-sm aq-
yellow">Pause</button>
</div>
</form>
</div>
<form style="margin: 10px 0 5px 20px">
<button type="button" class="btn btn-sm aq-grey" id="aq-
download">Download Action List</button>
<input type="submit" class="btn btn-sm aq-grey" value="Import Action
List" />
<input type="text" id="aq-pastebin" style="width: 236px;" required
pattern="^\\[.*\\]$" placeholder="Paste data here" />
</form>
<div style="display: flex;justify-content: space-between;max-width:
550px;margin: 10px 0 0 25px;">
<p style="margin: 0;">Looping</p>
<div class="">
<div class="custom-control custom-radio custom-control-inline custom-
control-lg">
<input type="radio" class="custom-control-input" id="aq-loop-
enable" name="aq-looping">
<label class="custom-control-label"
for="aq-loop-enable">Enable</label>
</div>
<div class="custom-control custom-radio custom-control-inline custom-
control-lg">
<input type="radio" class="custom-control-input" id="aq-loop-
disable" name="aq-looping" checked="">
<label class="custom-control-label" for="aq-loop-
disable">Disable</label>
</div>
</div>
</div>
<div style="display: flex;justify-content: space-between;max-width:
550px;margin: 10px 0 0 25px;">
<p style="margin: 0;">Mastery Pool Management</p>
<div class="">
<div class="custom-control custom-radio custom-control-inline custom-
control-lg">
<input type="radio" class="custom-control-input" id="aq-mastery-
enable" name="aq-mastery">
<label class="custom-control-label" for="aq-mastery-
enable">Enable</label>
</div>
<div class="custom-control custom-radio custom-control-inline custom-
control-lg">
<input type="radio" class="custom-control-input" id="aq-mastery-
disable" name="aq-mastery" checked="">
<label class="custom-control-label" for="aq-mastery-
disable">Disable</label>
</div>
</div>
</div>
<div style="margin: 10px 0 0 25px; display: flex; justify-content: space-
between">
<button type="button" class="btn btn-sm aq-grey" id="aq-mastery-
config">Advanced Mastery Options</button>
<button type="button" style="font-size: 0.875rem" class="btn aq-delete
btn-danger" id="aq-delete-all">delete all</button>
</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="aq-listA0"></datalist>
<datalist id="aq-listA1"></datalist>
<datalist id="aq-listA2"></datalist>
<datalist id="aq-listB0"></datalist>
<datalist id="aq-listB1"></datalist>
<datalist id="aq-listB2"></datalist>
<datalist id="aq-listB3"></datalist>
<datalist id="aq-listC0"></datalist>
<datalist id="aq-listC1"></datalist>
<datalist id="aq-listC2"></datalist>
<datalist id="aq-listC3"></datalist>
<style>
.aq-dropdown {
width: 260px;
}
.aq-header {
margin-bottom: 10px;
color: whitesmoke;
}
.aq-arrow {
font-size: 2rem;
padding: 0px 0.25rem;
line-height: 0px;
margin: 0.25rem 2px;
border-radius: 0.2rem;
}
.aq-item {
background-color: #464646;
padding: 12px;
margin: 12px;
cursor: move;
}
.aq-item-inner {
display: flex;
justify-content: space-between;
cursor: move;
}
.aq-delete {
font-size: 1.2rem;
padding: 0px 0.36rem;
line-height: 0px;
margin: 0.25rem 0.25rem 0.25rem 0.125rem;
border-radius: 0.2rem;
}
.aq-grey {
background-color: #676767;
}
.aq-grey:hover {
background-color: #848484;
}
.aq-blue {
background-color: #0083ff;
}
.aq-blue:hover {
background-color: #63b4ff;
}
.aq-green {
background-color: #5a9e00;
}
.aq-green:hover {
background-color: #7bd900;
}
.aq-yellow {
background-color: #e69721;
}
.aq-yellow:hover {
background-color: #ffb445;
}
.t-drag {
opacity: 0.5;
}
.a-drag {
opacity: 0.5;
}
.aq-popup {
position: fixed;
right: 5%;
top: 20%;
z-index: 2;
background-color: #3f4046;
padding: 20px 20px 20px 0;
border-top: 1px solid #e1e6e9;
border-radius: 0.25rem;
border-width: 4px;
}
.aq-mastery-config {
position: fixed;
right: 67%;
top: 18%;
z-index: 3;
background-color: #3f4046;
padding: 20px 20px 20px 0;
border-top: 1px solid #e1e6e9;
border-radius: 0.25rem;
border-width: 4px;
}
.aq-select {
width: 200px;
}
</style>
`;
function loadAQ() {
//add item names
for (const a of items) {
options.triggers["Item Quantity"][a.name] = { "≥": "num", "≤": "num" };
options.actions["Sell Item"][a.name] = null;
if (a.validSlots && a.validSlots.includes("Passive")) options.actions["Equip
Passive"][a.name] = null;
}
craftingItems.forEach((item) => {
options.triggers["Mastery Level"]["Crafting"][items[item.itemID].name] =
"num";
options.actions["Start Skill"]["Crafting"][items[item.itemID].name] = null;
});
items.forEach((item) => {
if (item.type == "Seeds") {
options.triggers["Mastery Level"]["Farming"][item.name] = "num";
}
});
items.forEach((item) => {
if (item.type == "Logs") {
options.triggers["Mastery Level"]["Firemaking"][item.name] = "num";
options.actions["Start Skill"]["Firemaking"][item.name] = null;
}
});
fishingItems.forEach((item) => {
options.triggers["Mastery Level"]["Fishing"][items[item.itemID].name] =
"num";
options.actions["Start Skill"]["Fishing"][items[item.itemID].name] = null;
});
items.forEach((item) => {
if (item.type == "Logs") {
options.triggers["Mastery Level"]["Woodcutting"][item.name] = "num";
options.actions["Start Skill"]["Woodcutting"][item.name] = {};
}
});
for (const log in options.actions["Start Skill"]["Woodcutting"]) {
Object.keys(options.actions["Start Skill"]["Woodcutting"]).forEach(
(a) => (options.actions["Start Skill"]["Woodcutting"][log][a] = null)
);
}
fletchingItems.forEach((item) => {
options.triggers["Mastery Level"]["Fletching"][items[item.itemID].name] =
"num";
options.actions["Start Skill"]["Fletching"][items[item.itemID].name] = null;
});
options.actions["Start Skill"]["Fletching"]["Arrow Shafts"] = {};
for (const log in options.actions["Start Skill"]["Woodcutting"]) {
options.actions["Start Skill"]["Fletching"]["Arrow Shafts"][log] = null;
}
herbloreItemData.forEach((item) => {
options.triggers["Mastery Level"]["Herblore"][item.name] = "num";
options.actions["Start Skill"]["Herblore"][item.name] = null;
});
agilityObstacles.forEach((item) => {
options.triggers["Mastery Level"]["Agility"][item.name] = "num";
});
Object.keys(MiningOres).forEach((item) => {
if (isNaN(item)) {
options.triggers["Mastery Level"]["Mining"][item] = "num";
options.actions["Start Skill"]["Mining"][item] = null;
}
});
runecraftingItems.forEach((item) => {
options.triggers["Mastery Level"]["Runecrafting"][items[item.itemID].name] =
"num";
options.actions["Start Skill"]["Runecrafting"][items[item.itemID].name] =
null;
});
smithingItems.forEach((item) => {
options.triggers["Mastery Level"]["Smithing"][items[item.itemID].name] =
"num";
options.actions["Start Skill"]["Smithing"][items[item.itemID].name] = null;
});
Thieving.npcs.forEach((npc) => {
options.triggers["Mastery Level"]["Thieving"][npc.name] = "num";
options.actions["Start Skill"]["Thieving"][npc.name] = null;
});
summoningItems.forEach((item) => {
options.triggers["Mastery Level"]["Summoning"][items[item.itemID].name] =
"num";
if (items[item.itemID].summoningReq.length == 1) {
options.actions["Start Skill"]["Summoning"][items[item.itemID].name] =
null;
} else {
options.actions["Start Skill"]["Summoning"][items[item.itemID].name] = {};
items[item.itemID].summoningReq.forEach((recipe) => {
const ingredient = recipe.slice(-1)[0].id;
options.actions["Start Skill"]["Summoning"][items[item.itemID].name]
[items[ingredient].name] = null;
});
}
});
}
//agility obstacles
options.actions["Build Agility Obstacle"] = agilityObstacles.reduce((obj, a) => {
if (!obj.hasOwnProperty(a.category + 1)) obj[a.category + 1] = {};
obj[a.category + 1][a.name] = null;
return obj;
}, {});
Object.keys(options.actions["Build Agility Obstacle"]).forEach(
(a) => (options.actions["Remove Agility Obstacle"][a] = null)
);
//potions
options.actions["Use Potion"] = items.reduce((obj, item) => {
if (item.type == "Potion") obj[item.name] = null;
return obj;
}, {});
//prayer actions
PRAYER.forEach((prayer) => (options.actions["Activate Prayers"][prayer.name] =
{}));
for (const prayer1 in options.actions["Activate Prayers"]) {
PRAYER.forEach((prayer) => (options.actions["Activate Prayers"][prayer1]
[prayer.name] = null));
options.actions["Activate Prayers"][prayer1]["None"] = null;
}
options.actions["Activate Prayers"]["None"] = null;
//mastery stuff
for (let i = 1; i < 100; i++) {
lvlIndex[i] = exp.level_to_xp(i) + 1;
}
for (const skill in MASTERY) {
masteryConfig[skill] = {
checkpoint: 0.95,
prio: false,
base: 87,
arr: [],
};
masteryClone[skill] = {
pool: MASTERY[skill].pool,
lvl: [],
};
updateMasteryLvl(skill);
}
window.masteryIDs = {};
for (const skillName in options.triggers["Mastery Level"]) {
masteryIDs[skillName] = {};
for (const name in options.triggers["Mastery Level"][skillName])
masteryIDs[skillName][name] = fetchMasteryID(skillName, name);
}
const replaceChar = [
/*{ reg: "&", replace: "&" },
{ reg: '"', replace: """ },
{ reg: "£", replace: "£" },
{ reg: "€", replace: "€" },
{ reg: "é", replace: "é" },
{ reg: "–", replace: "–" },
{ reg: "®", replace: "®" },
{ reg: "™", replace: "™" },
{ reg: "‘", replace: "‘" },
{ reg: "’", replace: "’" },
{ reg: "“", replace: "“" },
{ reg: "”", replace: "”" },
{ reg: "#", replace: "#" },
{ reg: "©", replace: "©" },
{ reg: "@", replace: "@" },
{ reg: "$", replace: "$" },
{ reg: "\\(", replace: "(" },
{ reg: "\\)", replace: ")" },
{ reg: "<", replace: "<" },
{ reg: ">", replace: ">" },
{ reg: "…", replace: "…" },
{ reg: "-", replace: "-" },*/
{ reg: "'", replace: "'" },
/* { reg: "\\*", replace: "*" },
{ reg: ",", replace: "‚" },*/
];
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()) {
actionQueueArray[currentActionIndex].action.forEach((action, i) => {
result = action.start();
document.getElementById(actionQueueArray[currentActionIndex].elementID).children[1]
.children[
i
].children[1].children[0].style.display = result ? "none" : "";
});
currentActionIndex + 1 >= actionQueueArray.length && queueLoop ?
(currentActionIndex = 0) : currentActionIndex++;
updateQueue();
}
}
/**
* Updates colour and text in sidebar
* @param {string} type ("start" || "stop" || "pause")
*/
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) => {
const element = document.getElementById(action.elementID);
if (index === currentActionIndex) {
for (let i = 0; i < element.children[1].children.length; i++) {
element.children[1].children[i].children[1].children[0].style.display =
"none";
}
element.style.backgroundColor = "#385a0b";
} else element.style.backgroundColor = "";
});
}
function toggleLoop(start) {
queueLoop = start;
}
function togglePause() {
queuePause = !queuePause;
if (queuePause) {
clearInterval(triggerCheckInterval);
triggerCheckInterval = null;
document.getElementById("aq-pause").innerHTML = "Unpause";
document.getElementById("aq-pause").classList.add("aq-green");
document.getElementById("aq-pause").classList.remove("aq-yellow");
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").classList.add("aq-yellow");
document.getElementById("aq-pause").classList.remove("aq-green");
}
}
function autoSave() {
const saveData = {
index: currentActionIndex,
data: [],
loop: queueLoop,
mastery: manageMasteryInterval === null ? false : true,
};
for (const action of actionQueueArray) {
let actionList = [];
action.action.forEach((a) => actionList.push(a.data));
saveData.data.push([...action.data, actionList]);
}
window.localStorage.setItem("AQSAVE" + currentCharacter,
JSON.stringify(saveData));
window.localStorage.setItem("AQMASTERY", JSON.stringify(masteryConfig));
}
function loadLocalSave() {
const obj = JSON.parse(window.localStorage.getItem("AQSAVE" + currentCharacter));
const config = JSON.parse(window.localStorage.getItem("AQMASTERY"));
if (config != null) {
for (const skill in config) {
masteryConfig[skill] = config[skill];
}
}
function setCurrentAction(id) {
const index = actionQueueArray.findIndex((a) => a.elementID == id);
if (index >= 0) {
currentActionIndex = index;
updateQueue();
}
}
function importActions() {
const string = document.getElementById("aq-pastebin").value;
if (!queuePause && actionQueueArray.length === 0) togglePause();
let arr = [];
try {
arr = JSON.parse(string.trim());
if (!Array.isArray(arr)) return false;
for (const params of arr) {
try {
let newAction = null;
if (params.length == 10) {
newAction = new Action(...params);
} else if (params.length == 6) {
newAction = new Action(...params.slice(0, 5));
params[5].forEach((data) => {
newAction.action.push({
elementID: `AQ${nameIncrement++}`,
data,
start: setAction(...data),
description: actionDescription(...data),
});
});
}
if (
!newAction.description.includes("undefined") &&
!newAction.description.includes("null") &&
typeof newAction.trigger == "function" &&
newAction.action.every((a) => {
return (
!a.description.includes("undefined") && !
a.description.includes("null") && typeof a.start == "function"
);
})
)
addToQueue(newAction);
} catch {}
}
document.getElementById("aq-pastebin").value = "";
updateQueue();
} catch (e) {
console.error(e);
} finally {
return false;
}
}
function downloadActions() {
const saveData = [];
for (const action of actionQueueArray) {
let actionList = [];
action.action.forEach((a) => actionList.push(a.data));
saveData.push([...action.data, actionList]);
}
let file = new Blob([JSON.stringify(saveData)], {
type: "text/plain",
});
if (window.navigator.msSaveOrOpenBlob) window.navigator.msSaveOrOpenBlob(file,
"Melvor_Action_Queue.txt");
else {
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = "Melvor_Action_Queue.txt";
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
function updateMasteryLvl(skill) {
MASTERY[skill].xp.forEach((xp, i) => {
let level = 1;
while (xp >= lvlIndex[level + 1]) level++;
masteryClone[skill].lvl[i] = level;
});
masteryClone[skill].completed = masteryClone[skill].lvl.every((a) => a == 99);
masteryClone[skill].pool = MASTERY[skill].pool;
}
function manageMastery() {
for (const skill in MASTERY) {
//token claiming
const maxPool = MASTERY[skill].xp.length * 500000;
const bankID = getBankId(CONSTANTS.item[`Mastery_Token_$
{SKILLS[skill].name}`]);
if (bankID !== -1 && bank[bankID].qty > 0 && MASTERY[skill].pool < maxPool *
0.999) {
const maxTokens = Math.floor(((maxPool - MASTERY[skill].pool) * 1000) /
maxPool);
{
let itemID = CONSTANTS.item[`Mastery_Token_${SKILLS[skill].name}`];
let qtyToUse = Math.min(bank[bankID].qty, maxTokens);
let totalXpToAdd = Math.floor(getMasteryPoolTotalXP(skill) * 0.001) *
qtyToUse;
addMasteryXPToPool(skill, totalXpToAdd, false, true);
updateItemInBank(bankID, itemID, -qtyToUse);
}
}
//choose masteryID
let masteryID = 0;
if (!masteryConfig[skill].prio || masteryClone[skill].lvl.some((a) => a < 60))
{
for (let i = 1; i < MASTERY[skill].xp.length; i++) {
if (MASTERY[skill].xp[i] < MASTERY[skill].xp[masteryID]) masteryID = i;
}
} else {
const noncompletedPrio = masteryConfig[skill].arr.filter(
(a) => masteryClone[skill].lvl[a] < masteryConfig[skill].base
);
if (noncompletedPrio.length == 0) {
//choose lowest of nonprio or lowest of prio if nonprio maxed
let arr = MASTERY[skill].xp.map((a, i) => i);
for (const x of masteryConfig[skill].arr) arr[x] = null;
arr = arr.filter((a) => a != null);
if (arr.every((a) => masteryClone[skill].lvl[a] >= 99)) arr =
[...masteryConfig[skill].arr];
arr.sort((a, b) => MASTERY[skill].xp[a] - MASTERY[skill].xp[b]);
masteryID = arr[0];
} else {
for (const id of masteryConfig[skill].arr) {
if (MASTERY[skill].xp[id] == lvlIndex[masteryConfig[skill].base]) {
//choose lowest of [nonprio, noncompleted prio]
let arr = MASTERY[skill].xp.map((a, i) => i);
for (const x of masteryConfig[skill].arr) arr[x] = null;
arr = arr.filter((a) => a != null);
arr.push(...noncompletedPrio);
arr.sort((a, b) => MASTERY[skill].xp[a] - MASTERY[skill].xp[b]);
masteryID = arr[0];
break;
} else if (masteryClone[skill].lvl[id] < masteryConfig[skill].base) {
masteryID = id;
break;
}
}
}
}
if (masteryID == undefined) continue;
function toggleMastery(start) {
if (start && manageMasteryInterval === null) {
manageMasteryInterval = setInterval(() => {
manageMastery();
}, 1000);
} else if (!start) {
clearInterval(manageMasteryInterval);
manageMasteryInterval = null;
}
}
function addToQueue(obj) {
actionQueueArray.push(obj);
document.getElementById("aq-item-container").insertAdjacentHTML(
"beforeend",
`<div class="aq-item" id="${obj.elementID}" draggable="true">
<div class="aq-item-inner">
<p style="margin: auto 0">${obj.description}</p>
<div style="min-width: 170px; min-height: 39px; display: flex; justify-content:
flex-end;">
<button type="button" class="btn aq-arrow aq-grey" style="font-size:
0.875rem;">select</button>
<button type="button" class="btn aq-arrow aq-grey" style="padding: 0 0.1rem
0.2rem 0.1rem;">+</button>
<button type="button" class="btn aq-arrow aq-grey" style="padding:0
0.09rem;">
<svg width="22" height="22" viewBox="0 0 24 24">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2929 9.8299L19.9409
9.18278C21.353 7.77064 21.353 5.47197 19.9409 4.05892C18.5287 2.64678 16.2292
2.64678 14.817 4.05892L14.1699 4.70694L19.2929 9.8299ZM12.8962 5.97688L5.18469
13.6906L10.3085 18.813L18.0201 11.0992L12.8962 5.97688ZM4.11851 20.9704L8.75906
19.8112L4.18692 15.239L3.02678 19.8796C2.95028 20.1856 3.04028 20.5105 3.26349
20.7337C3.48669 20.9569 3.8116 21.046 4.11851 20.9704Z" fill="currentColor"></path>
</svg>
</button>
<button type="button" class="btn aq-delete btn-danger">X</button>
</div>
</div>
<div style="min-height: 39px; padding-left: 10px;"></div>
</div>`
);
//add tooltips
tooltips[obj.elementID] = [
tippy(document.getElementById(obj.elementID).children[0].children[1].children[0], {
content: "Set as current trigger",
animation: false,
}),
tippy(document.getElementById(obj.elementID).children[0].children[1].children[1], {
content: "Add action",
animation: false,
}),
tippy(document.getElementById(obj.elementID).children[0].children[1].children[2], {
content: "Edit trigger",
animation: false,
}),
tippy(document.getElementById(obj.elementID).children[0].children[1].children[3], {
content: "Delete trigger & actions",
animation: false,
}),
];
//add eventlisteners for dragging trigger block
const element = document.getElementById(obj.elementID);
element.addEventListener("dragstart", () => {
element.classList.add("t-drag");
});
element.addEventListener("dragend", () => {
reorderQueue();
element.classList.remove("t-drag");
});
function reorderQueue() {
const targetIndex = actionQueueArray[currentActionIndex].elementID;
//remove stray dragging classes
document
.getElementById("aq-item-container")
.querySelectorAll(".a-drag")
.forEach((a) => a.classList.remove("a-drag"));
document
.getElementById("aq-item-container")
.querySelectorAll(".t-drag")
.forEach((a) => a.classList.remove("t-drag"));
//sort triggers
const triggerOrder = {};
[...document.getElementById("aq-item-container").children].forEach((item, index)
=> (triggerOrder[item.id] = index));
actionQueueArray.sort((a, b) => triggerOrder[a.elementID] -
triggerOrder[b.elementID]);
//sort actions
const actionList = actionQueueArray.reduce((a, b) => a.concat(b.action), []);
let increment = -1;
[...document.getElementById("aq-item-container").querySelectorAll(".aq-item-
inner")].forEach((item) => {
if (item.id == "") {
increment++;
actionQueueArray[increment].action = [];
} else {
actionQueueArray[increment].action.push(actionList.find((a) => a.elementID ==
item.id));
}
});
//reorder currentActionIndex
currentActionIndex = actionQueueArray.findIndex((a) => a.elementID ==
targetIndex);
}
function cancelEdit() {
document.getElementById("aq-edit-container").style.display = "none";
resetForm(["C"]);
}
function submitEdit() {
const a = document.getElementById("aq-edit-form").innerHTML.includes("Trigger");
const arr = [];
for (let i = 0; i < validInputs.C.length; i++)
arr.push(htmlChar(document.getElementById(`aq-textC${i}`).value));
if (a) arr.pop();
arr.push(document.getElementById(`aq-numC`).value);
if (a) arr.splice(["≥", "≤", ""].includes(arr[2]) ? 3 : 2, 0, "");
if (
validateInput(
a ? options.triggers : options.actions,
arr.filter((a) => a !== "")
)
) {
if (currentlyEditing.type == "add") {
addAction(currentlyEditing.id, ...arr);
} else if (currentlyEditing.type == "triggers") {
const action = new Action(...arr);
const i = actionQueueArray.findIndex((a) => a.elementID ==
currentlyEditing.id);
actionQueueArray[i].description = action.description;
actionQueueArray[i].trigger = action.trigger;
actionQueueArray[i].data = action.data;
document.getElementById(currentlyEditing.id).children[0].children[0].innerHTML =
action.description;
} else {
const description = actionDescription(...arr);
const start = setAction(...arr);
for (const item of actionQueueArray) {
const action = item.action.find((a) => a.elementID == currentlyEditing.id);
if (action) {
action.data = arr;
action.start = start;
action.description = description;
document.getElementById(currentlyEditing.id).children[0].innerHTML =
description;
break;
}
}
}
cancelEdit();
}
return false;
}
function updateMasteryPriority() {
const string = document.getElementById("aq-mastery-array").value;
let arr = null;
try {
arr = JSON.parse(string.trim());
} catch {}
if (!Array.isArray(arr)) return (masteryConfigChanges.arr = null);
arr = [...new Set(arr)].filter((a) => typeof
MASTERY[masteryConfigChanges.skill].xp[a] == "number");
masteryConfigChanges.arr = [...arr];
}
function masteryPopup(open) {
if (open) {
masteryConfigChanges.skill = null;
updateMasteryConfig();
document.getElementById("aq-mastery-config-container").style.display = "";
} else {
updateMasteryConfig();
document.getElementById("aq-mastery-config-container").style.display = "none";
}
}
/**
* Function to change active prayers
* @param {Array} choice array of prayer IDs
*/
function changePrayers(choice = []) {
for (const i of player.activePrayers.entries()) {
choice.includes(i[0])
? choice.splice(
choice.findIndex((a) => a == i[0]),
1
)
: choice.unshift(i[0]);
}
for (const prayer of choice) player.togglePrayer(prayer);
}
/**
* Function to delete every action
*/
function clearQueue() {
for (let i = actionQueueArray.length - 1; i >= 0; i--) {
deleteAction(actionQueueArray[i].elementID, "trigger");
}
}