// ==UserScript==
// @name Melvor Golbin Raider
// @namespace http://tampermonkey.net/
// @version 0.1.5
// @description Bloop Bleep. I am a robot.
// @author NotCorgan#1234
// @match https://melvoridle.com/*
// @match https://www.melvoridle.com/*
// @match https://test.melvoridle.com/*
// @exclude https://melvoridle.com/update/*
// @exclude https://www.melvoridle.com/update/*
// @exclude https://test.melvoridle.com/update/*
// @grant none
// @noframes
// ==/UserScript==
/* jshint esversion: 6 */
// Made for version 1.0.2
(function () {
function injectScript(main) {
var script = document.createElement('script');
script.textContent = `try {(${main})();} catch (e) {console.log(e);}`;
document.body.appendChild(script).parentNode.removeChild(script);
}
function script() {
// Loading script
/*
RaidManager.difficulties[3] = {
coinMultiplier: 1.5,
combatTriangle: 1,
enemyAccuracyModifier: 10,
enemyEvasionModifier: 10,
enemyHPModifier: 25,
enemyMaxHitModifier: 10,
hasSecondPassiveChange: true,
name: "Chaos",
negativeModifierCount: 10,
positiveModifierCount: 5,
selectedClass: "btn-info",
unselectedClass: "btn-outline-info",
}
RaidDifficulty[3] = 'Chaos';
RaidDifficulty['Chaos'] = 3;
$('#raid-difficulty-btn-2').after(`
<button class="btn btn-outline-info w-25" type="button" id="raid-difficulty-btn-3" onclick="raidManager.changeDifficulty(3);">
<span>Chaos</span>
</button>
`);
*/
let defaultSteps = [
{
"result": "weapons",
"conditional": "wave",
"comparison": "equals",
"num": "1"
},
{
"result": "armour",
"conditional": "wave",
"comparison": "equals",
"num": 2
},
{
"result": "food",
"conditional": "wave",
"comparison": "equals",
"num": 3
},
{
"conditional": "and",
"result": "passive",
"num": "0",
"mod": "5",
"conditions": [
{
"conditional": "wave",
"comparison": "mod",
"num": 0,
"mod": "5"
},
{
"conditional": "equipment",
"slot": 11,
"type": "prio",
"comparison": "greater-than",
"num": "1"
}
]
},
{
"result": "weapons",
"conditional": "and",
"conditions": [
{
"conditional": "wave",
"comparison": "greater-than-equals",
"num": 4
},
{
"conditional": "equipment",
"comparison": "in",
"slot": 4,
"collection": [
{
"num": -1
},
{
"num": 436
},
{
"num": 431
}
],
"type": "item"
}
]
},
{
"result": "food",
"conditional": "food",
"comparison": "less-than",
"type": "total",
"num": 800
},
{
"result": "runes",
"conditional": "and",
"conditions": [
{
"conditional": "or",
"conditions": [
{
"conditional": "rune",
"comparison": "less-than",
"type": 389,
"num": "500"
},
{
"conditional": "rune",
"comparison": "less-than",
"type": 393,
"num": "500"
},
{
"conditional": "rune",
"type": 390,
"comparison": "less-than",
"num": "500"
}
]
},
{
"conditional": "wave",
"comparison": "greater-than-equals",
"num": "4"
}
]
},
{
"conditional": "and",
"result": "armour",
"conditions": [
{
"conditional": "stats",
"type": "damage-reduction",
"comparison": "less-than",
"num": "20"
},
{
"conditional": "wave",
"comparison": "less-than",
"num": "20"
}
]
},
{
"conditional": "and",
"result": "weapons",
"conditions": [
{
"conditional": "equipment",
"slot": 4,
"comparison": "not-equals",
"type": "item",
"num": 932
},
{
"conditional": "wave",
"comparison": "greater-than",
"num": "20"
}
]
},
{
"conditional": "and",
"result": "runes",
"conditions": [
{
"conditional": "equipment",
"slot": 4,
"comparison": "equals",
"num": 932,
"type": "item"
},
{
"conditional": "or",
"conditions": [
{
"conditional": "rune",
"type": 394,
"comparison": "less-than",
"num": "500"
},
{
"conditional": "rune",
"type": 822,
"comparison": "less-than",
"num": "500"
},
{
"conditional": "rune",
"type": 392,
"comparison": "less-than",
"num": "500"
},
{
"conditional": "rune",
"type": 820,
"comparison": "less-than",
"num": "500"
}
]
}
]
},
{
"conditional": "or",
"result": "armour",
"conditions": [
{
"conditional": "equipment",
"slot": 0,
"type": "prio",
"comparison": "greater-than",
"num": "2"
},
{
"conditional": "equipment",
"slot": 1,
"type": "prio",
"comparison": "greater-than",
"num": "2"
},
{
"conditional": "equipment",
"slot": 2,
"type": "prio",
"comparison": "greater-than",
"num": "2"
},
{
"conditional": "equipment",
"slot": 3,
"type": "prio",
"comparison": "greater-than",
"num": "2"
},
{
"conditional": "equipment",
"slot": 6,
"type": "prio",
"comparison": "greater-than",
"num": "2"
},
{
"conditional": "equipment",
"slot": 7,
"type": "prio",
"comparison": "greater-than",
"num": "2"
},
{
"conditional": "equipment",
"slot": 8,
"type": "prio",
"comparison": "greater-than",
"num": "2"
},
{
"conditional": "equipment",
"slot": 10,
"type": "prio",
"comparison": "greater-than",
"num": "2"
}
]
},
{
"result": "runes",
"conditional": "default"
}
];
let golbinRaiderSelectionSteps = JSON.parse(localStorage.getItem(`golbinRaiderSelectionSteps`)||JSON.stringify(defaultSteps))
window.golbinRaiderSelectionSteps = golbinRaiderSelectionSteps;
let prioritiesList = [
CONSTANTS.equipmentSlot.Weapon,
CONSTANTS.equipmentSlot.Shield,
CONSTANTS.equipmentSlot.Helmet,
CONSTANTS.equipmentSlot.Platebody,
CONSTANTS.equipmentSlot.Gloves,
CONSTANTS.equipmentSlot.Platelegs,
CONSTANTS.equipmentSlot.Boots,
CONSTANTS.equipmentSlot.Cape,
CONSTANTS.equipmentSlot.Amulet,
CONSTANTS.equipmentSlot.Ring,
CONSTANTS.equipmentSlot.Quiver,
CONSTANTS.equipmentSlot.Passive];
let defaultPriorities = {
"Weapon":[932,1200,930,1199,928,499,497],
"Shield":[812,1114,1226],
"Helmet":[1225,1214,718,713,776,781],
"Platebody":[1216,720,715,778,783],
"Gloves":[1147,722,717,780,785],
"Platelegs":[719,714,777,782],
"Boots":[1213,721,716,779,784],
"Cape":[941,818,361],
"Amulet":[819,795,793],
"Ring":[1146,794,1115,792,1144,1148],
"Quiver":[1083],
"Passive":[1115,1114,795]
};
let golbinRaiderPriorities = JSON.parse(localStorage.getItem(`golbinRaiderPriorities`)||JSON.stringify(defaultPriorities))
window.golbinRaiderPriorities = golbinRaiderPriorities;
let golbinRaiderSettings = JSON.parse(localStorage.getItem(`golbinRaiderSettings`)||JSON.stringify({
autoHeal: true
}))
window.golbinRaiderSettings = golbinRaiderSettings;
let golbinRaiderModifierPriority = JSON.parse(localStorage.getItem(`golbinRaiderModifierPriority`)||JSON.stringify({
positive: [...RaidManager.possibleModifiers].map(mod => ({key: mod.key, soft: 0, hard: 0 })),
negative: [...RaidManager.possibleModifiers].map(mod => ({key: mod.key, soft: 0, hard: 0 }))
}));
window.golbinRaiderModifierPriority = golbinRaiderModifierPriority;
/*
TODO
Item Selection Revamp (customizable weighting?)
Ammo Selection
Rune Selection
Settings (???)
autoHeal
Allow 2H
Allow Magic
Allow Ranged
*/
let golbinRaidResults = {
weapons: { id: "weapons", name: "Weapons" },
armour: { id: "armour", name: "Armor" },
food: { id: "food", name: "Food" },
runes: { id: "runes", name: "Runes" },
ammo: { id: "ammo", name: "Ammo" },
passive: { id: "passive", name: "Passive" },
pause: { id: "pause", name: "Pause Run" },
abort: { id: "abort", name: "Abort Run" }
};
let golbinItemCount = id => raidManager.bank.getQty(id)
let haveRunes = runes => runes.reduce((bool, rune) => bool && golbinItemCount(rune[0]) >= rune[1], true)
let availableRunes = () => items.filter(item => item.type === "Rune" && !RaidManager.bannedItems.includes(item.id)).map(item => item.id);
let combatLevel = (item) => {
if(item.equipRequirements == undefined || item.equipRequirements.length == 0)
return 0;
return item.equipRequirements.filter(er => er.type == "Level").map(er => {
return Math.max(...er.levels.filter(l => {
return [6,7,8,12,16].includes(l.skill)
}).map(l => l.level));
})[0];
}
let hasOceanSong = () => raidManager.player.availableAttacks.map(aa => aa.attack.id).indexOf(31) != -1
const camel2title = (camelCase) => camelCase.replace(/([A-Z])/g, (match) => ` ${match}`).replace(/^./, (match) => match.toUpperCase()).trim();
const golbinRaidValues = {
'num': {
id: 'num',
name: 'Value',
type: 'number',
default: 0
},
'mod': {
id: 'mod',
name: 'Mod',
type: 'number',
default: 0
}
}
const golbinRaidComparisons = {
'true': {
id: 'true',
name: "Is True",
fn: (a) => a == true,
values: () => [],
},
'false': {
id: 'false',
name: "Is False",
fn: (a) => a == false,
values: () => [],
},
'equals': {
id: 'equals',
name: "==",
fn: (a, {num}) => a == num,
values: () => [
golbinRaidValues['num']
],
},
'not-equals': {
id: 'not-equals',
name: "!=",
fn: (a, {num}) => a != num,
values: () => [
golbinRaidValues['num']
],
},
'less-than': {
id: 'less-than',
name: "<",
fn: (a, {num}) => a < num,
values: () => [
golbinRaidValues['num']
],
},
'less-than-equals': {
id: 'less-than-equals',
name: "<=",
fn: (a, {num}) => a <= num,
values: () => [
golbinRaidValues['num']
],
},
'greater-than': {
id: 'greater-than',
name: ">",
fn: (a, {num}) => a > num,
values: () => [
golbinRaidValues['num']
],
},
'greater-than-equals': {
id: 'greater-than-equals',
name: ">=",
fn: (a, {num}) => a >= num,
values: () => [
golbinRaidValues['num']
],
},
'in': {
id: 'in',
name: "In",
fn: (a, {collection}) => collection.findIndex(({num}) => num == a) != -1,
collection: true,
values: () => [
golbinRaidValues['num']
],
},
'not-in': {
id: 'not-in',
name: "Not In",
fn: (a, {collection}) => collection.findIndex(({num}) => num == a) == -1,
collection: true,
values: () => [
golbinRaidValues['num']
],
},
'includes': {
id: 'includes',
name: "Includes",
fn: (a, {num}) => a.includes(num),
values: () => [
golbinRaidValues['num']
],
},
'not-includes': {
id: 'not-includes',
name: "Doesn't Include",
fn: (a, {num}) => !a.includes(num),
values: () => [
golbinRaidValues['num']
],
},
'mod': {
id: 'mod',
name: "%",
fn: (a, {num, mod}) => a % mod == num,
values: () => [
golbinRaidValues['num'],
golbinRaidValues['mod']
],
}
}
const golbinRaidConditionals = {
wave: {
id: 'wave',
name: 'Wave',
visible: true,
selectable: true,
params: [],
evaluate: ({comparison, ...rest}) => {
let a = raidManager.wave;
return golbinRaidComparisons[comparison].fn(a, rest);
},
comparisons: () => [
golbinRaidComparisons['equals'],
golbinRaidComparisons['not-equals'],
golbinRaidComparisons['less-than'],
golbinRaidComparisons['less-than-equals'],
golbinRaidComparisons['greater-than'],
golbinRaidComparisons['greater-than-equals'],
golbinRaidComparisons['mod'],
golbinRaidComparisons['in'],
golbinRaidComparisons['not-in'],
],
values: () => []
},
equipment: {
id: 'equipment',
name: 'Equipment',
visible: true,
selectable: true,
params: [
{
id: 'slot',
name: 'Slot',
type: 'dropdown',
default: -1,
values: ({type}) => {
let v = [...prioritiesList].map((id) => ({id: id, name: CONSTANTS.equipmentSlot[id]}))
v.unshift({id: -1, name: 'Any'});
return v
}
},
{
id: 'type',
name: 'Type',
type: 'dropdown',
default: 'item',
values: ({slot}) => {
let v = [];
v.push({id: 'item', name: 'Item'})
v.push({id: 'prio', name: 'Priority'})
if(slot != -1)
v.push({id: 'stats', name: 'Stats'})
v.push({id: 'alt', name: 'Alt'})
if(slot != -1)
v.push({id: 'special', name: 'Has Special'})
return v
}
},
{
id: 'stat',
name: 'Stat',
type: 'dropdown',
default: 'damageReduction',
visible: ({type}) => type == 'stats',
values: () => equipStatKeys.map(stat => {
return {id: stat, name: camel2title(stat)};
})
}
],
evaluate: ({comparison, slot, type, stat, ...rest}) => {
let a = 0;
if(type == 'special')
a = raidManager.player.equipment.slotArray[slot].occupiedBy === 'None' && raidManager.player.altAttacks[CONSTANTS.equipmentSlot[slot]].length == 0 && raidManager.player.equipment.slotArray[slot].item.hasSpecialAttack;
if(type == 'alt')
a = raidManager.player.equipment.slotArray[slot].occupiedBy === 'None' && raidManager.player.altAttacks[CONSTANTS.equipmentSlot[slot]];
if(type == 'item')
a = raidManager.player.equipment.slotArray[slot].item.id;
if(type == 'stats')
a = (raidManager.player.equipment.slotArray[slot].item.equipmentStats.find(i => i.key == stat) || {value: 0}).value;
if(type == 'prio') {
a = golbinRaiderPriorities[CONSTANTS.equipmentSlot[slot]] && golbinRaiderPriorities[CONSTANTS.equipmentSlot[slot]].length > 0 && golbinRaiderPriorities[CONSTANTS.equipmentSlot[slot]].findIndex(id => id == raidManager.player.equipment.slotArray[slot].item.id);
if(a === false || a == -1) {
a = Infinity;
} else {
a++;
}
}
return golbinRaidComparisons[comparison].fn(a, rest);
},
comparisons: ({slot, type}) => {
let comparisons = [
golbinRaidComparisons['equals'],
golbinRaidComparisons['not-equals'],
];
if(type == 'item') {
comparisons = comparisons.concat([
golbinRaidComparisons['in'],
golbinRaidComparisons['not-in'],
])
}
if(type == 'stats' || type == 'prio') {
comparisons = comparisons.concat([
golbinRaidComparisons['less-than'],
golbinRaidComparisons['less-than-equals'],
golbinRaidComparisons['greater-than'],
golbinRaidComparisons['greater-than-equals'],
])
}
if(type == 'alt' || slot == -1) {
comparisons = [
golbinRaidComparisons['includes'],
golbinRaidComparisons['not-includes'],
];
}
if(type == 'special') {
comparisons = [
golbinRaidComparisons['true'],
golbinRaidComparisons['false'],
];
}
return comparisons;
},
values: ({type}) => {
let values = [];
if(type == 'item')
values.push({
id: 'num',
name: 'Item',
type: 'dropdown',
default: -1,
values: ({slot}) => {
let v = items.filter(item => slot == -1 ? item.validSlots && item.validSlots.length > 0 : item.validSlots && item.validSlots.includes(CONSTANTS.equipmentSlot[slot]));
v.unshift({id: -1, name: 'Empty'});
return v;
},
content: ({num}, collection) => `${num > -1 ? `<img class="skill-icon-xs mr-1" src="${CDNDIR}${items[num].media}"> ` : ''}${num > -1 ? items[num].name : 'Empty'}`,
render: ({id, name}) => `${id > -1 ? `<img class="skill-icon-xs mr-1" src="${CDNDIR}${items[id].media}"> ` : ''} ${name}`
});
if(type == 'alt')
values.push({
id: 'num',
name: 'Alts',
type: 'dropdown',
default: -1,
values: ({slot}) => {
let v = [...raidManager.specialAttackSelection].filter((attack, i, arr) => arr.findIndex(a => attack.id == a.id) == i);
v.unshift({id: -1, name: 'Empty'});
return v;
},
content: ({num}, collection) => `${collection.find(attack => attack.id == num).name}`,
render: ({id, name, attackTypes}) => `${attackTypes && attackTypes.length == 1 ? `(${attackTypes[0].toUpperCase()})` : ''} ${name}`
});
return values;
}
},
food: {
id: 'food',
name: 'Food',
visible: true,
selectable: true,
params: [
{
id: 'type',
name: 'Type',
default: 'qty',
type: 'dropdown',
values: () => ({healsFor: {id: 'healsFor', name: 'Heals For'}, qty: {id: 'qty', name: 'Quantity'}, total: {id: 'total', name: 'Total Healing'}})
}
],
evaluate: ({comparison, type, ...rest}) => {
let a = 0;
if(type == 'healsFor')
a = items[raidManager.player.food.slots[raidManager.player.food.selectedSlot].item.id].healsFor;
if(type == 'qty')
a = raidManager.player.food.slots[raidManager.player.food.selectedSlot].quantity;
if(type == 'total')
a = items[raidManager.player.food.slots[raidManager.player.food.selectedSlot].item.id].healsFor * raidManager.player.food.slots[raidManager.player.food.selectedSlot].quantity;
return golbinRaidComparisons[comparison].fn(a, rest);
},
comparisons: () => [
golbinRaidComparisons['less-than'],
golbinRaidComparisons['less-than-equals'],
golbinRaidComparisons['greater-than'],
golbinRaidComparisons['greater-than-equals'],
],
values: () => []
},
rune: {
id: 'rune',
name: 'Rune Count',
visible: true,
selectable: true,
params: [
{
id: 'type',
name: 'Rune',
type: 'dropdown',
default: CONSTANTS.item.Air_Rune,
values: () => Object.fromEntries(availableRunes().map(rune => [rune, items[rune]])),
content: ({type}, collection) => `<img class="skill-icon-xs mr-1" src="${CDNDIR}${items[type].media}"> ${items[type].name}`,
render: ({id, name}) => `<img class="skill-icon-xs mr-1" src="${CDNDIR}${items[id].media}"> ${name}`
}
],
evaluate: ({comparison, type, ...rest}) => {
let a = golbinItemCount(type);
return golbinRaidComparisons[comparison].fn(a, rest);
},
comparisons: () => [
golbinRaidComparisons['less-than'],
golbinRaidComparisons['less-than-equals'],
golbinRaidComparisons['greater-than'],
golbinRaidComparisons['greater-than-equals'],
],
values: () => []
},
ammo: {
id: 'ammo',
name: 'Ammo Count',
visible: true,
selectable: true,
params: [],
evaluate: ({comparison, ...rest}) => {
let a = raidManager.player.equipment.slotArray[CONSTANTS.equipmentSlot.Quiver].quantity;
return golbinRaidComparisons[comparison].fn(a, rest);
},
comparisons: () => [
golbinRaidComparisons['less-than'],
golbinRaidComparisons['less-than-equals'],
golbinRaidComparisons['greater-than'],
golbinRaidComparisons['greater-than-equals'],
],
values: () => []
},
stats: {
id: 'stats',
name: 'Stats',
visible: true,
selectable: true,
params: [
{
id: 'type',
name: 'Type',
default: 'damage-reduction',
type: 'dropdown',
values: () => {
let stats = raidManager.player.stats.getValueTable();
stats.forEach(stat => stat.id = stat.name.toLowerCase().replace(' ', '-'));
return stats.map(stat => stat);
},
content: ({type}, collection) => `${collection.find(c => c.id == type).name}`,
render: ({id, name}) => `${name}`
}
],
evaluate: ({comparison, type, ...rest}) => {
let stats = raidManager.player.stats.getValueTable();
let stat = stats.find(stat => stat.name.toLowerCase().replace(' ', '-') == type);
return golbinRaidComparisons[comparison].fn((stat || {value: 0}).value, rest);
},
comparisons: () => [
golbinRaidComparisons['equals'],
golbinRaidComparisons['not-equals'],
golbinRaidComparisons['less-than'],
golbinRaidComparisons['less-than-equals'],
golbinRaidComparisons['greater-than'],
golbinRaidComparisons['greater-than-equals'],
],
values: () => []
},
or: {
id: 'or',
name: 'OR',
visible: true,
selectable: true,
container: true,
evaluate: ({conditions, ...rest}) => {
return conditions.reduce((a,b) => a || golbinRaidConditionals[b.conditional].evaluate(b), false);
}
},
and: {
id: 'and',
name: 'AND',
visible: true,
selectable: true,
container: true,
evaluate: ({conditions, ...rest}) => {
return conditions.reduce((a,b) => a && golbinRaidConditionals[b.conditional].evaluate(b), true);
}
},
default: {
id: 'default',
name: 'Default',
visible: true,
selectable: true,
evaluate: ({...rest}) => {
return true
}
},
result: {
visible: false,
selectable: false,
evaluate: ({result, conditional, ...rest}) => {
let ret = golbinRaidConditionals[conditional].evaluate(rest);
if(result == 'passive' && raidManager.player.modifiers.golbinRaidPassiveSlotUnlocked == 0)
ret = false;
return result && ret ? result : ret;
}
}
}
function evaluateSteps(steps) {
for(let i=0; i<steps.length; i++) {
let result = golbinRaidConditionals.result.evaluate(steps[i]);
if(result !== false)
return { result: result, step: steps[i] };
}
}
window.evaluateSteps = evaluateSteps;
function itemWeight(id, isAlt=false, slot=false) {
let item = items[id];
if(!item)
return -1;
let equipmentStats = new EquipmentStats(item.equipmentStats);
let weight = 0;
weight += equipmentStats.attackSpeed / 1000
if(Math.max(equipmentStats.stabAttackBonus, equipmentStats.slashAttackBonus, equipmentStats.blockAttackBonus) > 0)
weight += Math.max(equipmentStats.stabAttackBonus, equipmentStats.slashAttackBonus, equipmentStats.blockAttackBonus);
if(equipmentStats.rangedAttackBonus > 0)
weight += equipmentStats.rangedAttackBonus / 2;
if(equipmentStats.magicAttackBonus > 0)
weight += equipmentStats.magicAttackBonus / 2;
if(equipmentStats.meleeStrengthBonus > 0)
weight += equipmentStats.meleeStrengthBonus
if(equipmentStats.rangedStrengthBonus > 0)
weight += equipmentStats.rangedStrengthBonus / 2
if(equipmentStats.magicDamageBonus > 0)
weight += equipmentStats.magicDamageBonus / 2
if(equipmentStats.meleeDefenceBonus > 0)
weight += equipmentStats.meleeDefenceBonus
if(equipmentStats.rangedDefenceBonus > 0)
weight += equipmentStats.rangedDefenceBonus / 2
if(equipmentStats.magicDefenceBonus > 0)
weight += equipmentStats.magicDefenceBonus / 2
weight += equipmentStats.damageReduction * 1000
if(combatLevel(item) >= 70)
weight += 2500;
if(combatLevel(item) >= 85)
weight += 5000;
if(item.validSlots.includes('Weapon') && item.occupiesSlots.includes('Shield'))
weight = -1;
if(item.attackType === "ranged" && item.validSlots.filter(slot => slot != CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Passive]).includes("Weapon"))
weight = -1;
if(item.attackType == "magic" && item.validSlots.filter(slot => slot != CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Passive]).includes("Weapon"))
weight = -1;
equipmentStats = null;
return weight;
}
function generateTooltip(id, qty=1, isPassive=false, isAlt=false, slot=false, altAttacks=[]) {
let item = items[id];
if(!item)
return `<div class="text-center"><span class="text-warning">${slot}</span></div>`;
if(!slot && item.validSlots !== undefined && item.validSlots.length > 0)
slot = item.validSlots[0];
if(isPassive)
slot = CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Passive];
let itemStat = "";
if (item.healsFor)
itemStat += `<br><span class="text-white">Healing: ${(item.healsFor * qty).toFixed(2)}</span>`;
if (isEquipment(item) && !isPassive && !(slot && golbinRaiderPriorities[slot] && golbinRaiderPriorities[slot].includes(id)))
itemStat += `<br><span class="text-white">Weight: ${itemWeight(id, isAlt, slot)}</span>`;
if(slot && golbinRaiderPriorities[slot] && golbinRaiderPriorities[slot].includes(id))
itemStat += `<br><span class="text-white">Priority: ${golbinRaiderPriorities[slot].indexOf(id) + 1}</span>`;
if (isEquipment(item) && !isPassive && isAlt)
itemStat += `<br><span class="text-warning">ALT</span>`;
if (isEquipment(item) && !isPassive && isAlt && altAttacks.length > 0)
itemStat += altAttacks.map((id)=>{
let attack = attacksIDMap.get(id);
if(attack !== undefined) {
return `<br><span class="text-danger">${attack.name} (${formatPercent(attack.defaultChance)}): <span class="text-warning">${describeAttack(attack)}</span></span>`;
} else {
return ``;
}
}).join("");
if (isEquipment(item) && !isPassive && !isAlt && item.hasSpecialAttack)
itemStat += item.specialAttacks.map((attack)=>{
return `<br><span class="text-danger">${attack.name} (${formatPercent(attack.defaultChance)}): <span class="text-warning">${describeAttack(attack)}</span></span>`;
}).join("");
if (item.description != undefined)
itemStat += `<br><span class="text-info">${item.description}</span>`;
if (!isPassive) {
let equipStats = item.equipmentStats;
if(equipStats) {
equipStats.forEach((stat)=>{
itemStat += '<br>';
if (stat.value > 0) {
itemStat += Equipment.getEquipStatDescription(stat.key, stat.value);
} else {
itemStat += `<span class="text-danger">${Equipment.getEquipStatDescription(stat.key, stat.value)}</span>`;
}
});
}
}
return `<div class="text-center"><span class="text-warning">${item.name}</span><small class='text-success'>${itemStat}</small></div>`;
}
function generateSpecialTooltip(specialAttacks=[]) {
let allAttacks = specialAttacks.map((slot, i) => {
if(slot.length == 0)
return "";
let specials = slot.map((id)=>{
let attack = attacksIDMap.get(id);
if(attack !== undefined) {
return `<br><span class="text-danger">${attack.name} (${formatPercent(attack.defaultChance)}): <span class="text-warning">${describeAttack(attack)}</span></span>`;
} else {
return "";
}
}).join("");
return `<br><span class="text-success">${CONSTANTS.equipmentSlot[i]}</span><small class='text-warning'>${specials}</small>`
}).join("");
return `<div class="text-center"><span class="text-warning">Special Attacks</span>${allAttacks}</div>`;
}
function generateItemImage(id, qty=0, selected=false, current=false, slot=false, isAlt=false, altAttacks=[]) {
return `
<item-icon
class="draggable"
data-id="${id}"
data-qty="${qty}"
data-slot="${slot}"
data-is-alt="${isAlt}"
data-alt-attacks="${JSON.stringify(altAttacks)}"
data-is-passive="${slot === CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Passive]}"
style="display: inline-block; position: relative; height: 59px; width: 59px;"
draggable="true">
<img class="combat-equip-img border border-2x border-rounded-equip border-combat-outline p-1 ${selected ? 'border-info' : ''} ${isAlt && !selected ? 'border-warning' : ''}"
src="${CDNDIR + (id != -1 && items[id] ? items[id].media : (slot!==false ? 'assets/media/bank/' + equipmentSlotData[slot].emptyMedia + '.png' : 'assets/media/bank/passive_slot.png'))}"
style="${current ? 'border-color: #e56767 !important;' : ''}" />
<div class="font-size-sm text-white text-center ${qty <= 1 ? 'd-none' : ''}" style="position: absolute; bottom: 0px; width: 100%;">
<small class="badge-pill bg-secondary">${qty}</small>
</div>
</item-icon>
`;
}
function generateRuneImage(runeCount=false) {
return `
<rune-icon
style="display: inline-block; position: relative; height: 59px; width: 59px;"
data-rune-count='${JSON.stringify(runeCount)}'>
<img class="combat-equip-img border border-2x border-rounded-equip border-combat-outline p-1"
src="${CDNDIR + items[CONSTANTS.item.Rune_Essence].media}"/>
</rune-icon>
`
}
function generateSpecialImage(currentItems=false) {
let specialAttacks = currentItems.map((slot, i) => {
if(slot.isAlt)
return slot.altAttacks;
if(slot.id == -1 || i == CONSTANTS.equipmentSlot.Passive)
return [];
let item = items[slot.id];
if(isEquipment(item) && item.hasSpecialAttack)
return item.specialAttacks.map(attack => attack.id);
return [];
});
if(specialAttacks.flat().length > 0)
return `
<special-icon
style="display: inline-block; position: relative; height: 59px; width: 59px;"
data-special-attacks='${JSON.stringify(specialAttacks)}'>
<img class="combat-equip-img border border-2x border-rounded-equip border-combat-outline p-1"
src="${CDNDIR}assets/media/main/special_attack.svg"/>
</rune-icon>
`;
return "";
}
function generateEquipmentGrid(currentItems=false, currentRunes=false, currentFood=false) {
let equipMap = [CONSTANTS.equipmentSlot.Passive, CONSTANTS.equipmentSlot.Helmet, -1,
CONSTANTS.equipmentSlot.Cape, CONSTANTS.equipmentSlot.Amulet, CONSTANTS.equipmentSlot.Quiver,
CONSTANTS.equipmentSlot.Weapon, CONSTANTS.equipmentSlot.Platebody, CONSTANTS.equipmentSlot.Shield,
-1, CONSTANTS.equipmentSlot.Platelegs, -1,
CONSTANTS.equipmentSlot.Gloves, CONSTANTS.equipmentSlot.Boots, CONSTANTS.equipmentSlot.Ring,
(currentRunes ? -2 : -1), -1, (currentFood ? -3 : -1)];
return `<div class="text-center"><span class="text-warning">Equipment</span>
<equipment-grid style="display: grid; grid-template-rows: 60px 60px 60px 60px 60px 60px; grid-template-columns: 60px 60px 60px;">
${equipMap.map(slot => {
if(slot > -1) {
return generateItemImage(currentItems[slot].id, currentItems[slot].qty, false, false, CONSTANTS.equipmentSlot[slot], currentItems[slot].isAlt, currentItems[slot].altAttacks);
} else if (slot == -2) {
return generateRuneImage(currentRunes)
} else if (slot == -3) {
return generateItemImage(currentFood.id, currentFood.qty, false, false, CONSTANTS.equipmentSlot[slot]);
} else {
return `<div></div>`
}
}).join('')}
</equipment-grid>
</div>`;
}
function generateRuneGrid(runeCount=false) {
return `<div class="text-center"><span class="text-warning">Runes</span>
<rune-grid style="display: grid; grid-template-rows: 60px 60px 60px; grid-template-columns: 60px 60px 60px 60px;">
${runeCount.map(rune => generateItemImage(rune[0], rune[1], true, false)).join('')}
</rune-grid>
</div>`;
}
function itemOnShow(instance) {
let id = parseInt(instance.reference.dataset.id);
let qty = parseInt(instance.reference.dataset.qty);
let isAlt = instance.reference.dataset.isAlt === "true";
let altAttacks = JSON.parse(instance.reference.dataset.altAttacks);
let isPassive = instance.reference.dataset.isPassive === "true";
let slot = instance.reference.dataset.slot;
if(slot === "false")
slot = false;
instance.setContent(generateTooltip(id, qty, isPassive, isAlt, slot, altAttacks));
}
function itemOnHidden(instance) {
instance.setContent('');
}
function specialOnShow(instance) {
let specialAttacks = JSON.parse(instance.reference.dataset.specialAttacks);
instance.setContent(generateSpecialTooltip(specialAttacks));
}
function specialOnHidden(instance) {
instance.setContent('');
}
function runeOnShow(instance) {
let runeCount = JSON.parse(instance.reference.dataset.runeCount);
instance.setContent(generateRuneGrid(runeCount));
tippy($('item-icon', instance.popper.children[0].children[0]).toArray(), {
content: '',
allowHTML: true,
placement: "left",
interactive: false,
animation: false,
onShow: itemOnShow,
onHidden: itemOnHidden
});
}
function runeOnHidden(instance) {
$('item-icon', instance.popper.children[0].children[0]).toArray().forEach(el => {
if(el._tippy) {
el._tippy.destroy();
}
});
instance.setContent('');
}
function equipmentOnShow(instance) {
let currentItems = JSON.parse(instance.reference.dataset.currentItems);
let currentRunes = JSON.parse(instance.reference.dataset.currentRunes);
let currentFood = JSON.parse(instance.reference.dataset.currentFood);
instance.setContent(generateEquipmentGrid(currentItems, currentRunes, currentFood));
tippy($('item-icon', instance.popper.children[0].children[0]).toArray(), {
content: '',
allowHTML: true,
placement: "left",
interactive: false,
animation: false,
onShow: itemOnShow,
onHidden: itemOnHidden
});
tippy($('rune-icon', instance.popper.children[0].children[0]).toArray(), {
content: '',
allowHTML: true,
placement: "left",
interactive: true,
animation: false,
onShow: runeOnShow,
onHidden: runeOnHidden
});
}
function equipmentOnHidden(instance) {
$('item-icon', instance.popper.children[0].children[0]).toArray().forEach(el => {
if(el._tippy) {
el._tippy.destroy();
}
});
instance.setContent('');
}
function addChoice(category, selected, options, currentItems, currentRunes, currentFood, extra={}) {
if(extra.oldPassive) {
if(extra.oldPassive == -1) {
currentItems[CONSTANTS.equipmentSlot.Passive].id = -1;
} else {
currentItems[CONSTANTS.equipmentSlot.Passive].id = extra.oldPassive;
}
}
let logEntry = `
<entry
style="display: grid; grid-template-rows: 60px; grid-template-columns: auto 1fr auto auto auto; margin-top: 5px; height: 60px; width: 100%; ${raidManager.wave == 1 ? 'border-bottom: 2px solid black;' : ''}"
data-wave="${raidManager.wave}"
data-category="${category}"
data-selected="${selected}"
data-options='${JSON.stringify(options)}'>
<wave
style="display: inline-block; grid-area: 1 / 1 / 1 / 1; font-size: 36px; line-height: 60px; height: 60px; color: white; margin-top: -2px; margin-left: 10px; min-width: 65px;"
data-wave='${raidManager.wave}'
data-current-items='${JSON.stringify(currentItems)}'
data-current-runes='${JSON.stringify(currentRunes)}'
data-current-food='${JSON.stringify(currentFood)}'>
${raidManager.wave}
</wave>
<special style="display: flex; grid-area: 1 / 2 / 1 / 2">
${generateSpecialImage(currentItems)}
</special>
<existing-items style="display: flex; justify-content: flex-end; grid-area: 1 / 3 / 1 / 3;">
${selected == -1 && category == 'weapons' ? generateItemImage(currentItems[CONSTANTS.equipmentSlot.Weapon].id, 0, true, false, CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Weapon], currentItems[CONSTANTS.equipmentSlot.Weapon].isAlt, currentItems[CONSTANTS.equipmentSlot.Weapon].altAttacks) : ''}
${selected == -1 && category == 'armour' ? options.filter((value, index, self) => self.map(item => items[item.itemID].validSlots[0]).indexOf(items[value.itemID].validSlots[0]) === index).map((item, i, self) => generateItemImage(currentItems[CONSTANTS.equipmentSlot[items[item.itemID].validSlots[0]]].id, 0, true, false, false, currentItems[CONSTANTS.equipmentSlot[items[item.itemID].validSlots[0]]].isAlt, currentItems[CONSTANTS.equipmentSlot.Weapon].altAttacks)).join('') : ''}
${selected == -1 && category == 'food' ? generateItemImage(currentFood.id, currentFood.qty, true, false) : ''}
${selected == -1 && category == 'ammo' ? generateItemImage(currentItems[CONSTANTS.equipmentSlot.Quiver].id, currentItems[CONSTANTS.equipmentSlot.Quiver].qty, true, false, false, currentItems[CONSTANTS.equipmentSlot.Quiver].isAlt, currentItems[CONSTANTS.equipmentSlot.Weapon].altAttacks) : ''}
${selected == -1 && category == 'runes' ? generateRuneImage(currentRunes) : ''}
${selected == -1 && category == 'passive' ? generateItemImage(currentItems[CONSTANTS.equipmentSlot.Passive].id, 0, true, false, CONSTANTS.equipmentSlot.Passive, currentItems[CONSTANTS.equipmentSlot.Passive].isAlt, currentItems[CONSTANTS.equipmentSlot.Weapon].altAttacks) : ''}
${selected > -1 && category == 'weapons' ? generateItemImage(currentItems[CONSTANTS.equipmentSlot.Weapon].id, 0, false, true, CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Weapon], currentItems[CONSTANTS.equipmentSlot.Weapon].isAlt, currentItems[CONSTANTS.equipmentSlot.Weapon].altAttacks) : ''}
${selected > -1 && category == 'armour' ? options.map((item, i, self) => (self.map(option => items[option.itemID].validSlots[0]).findIndex((slot, idx) => items[item.itemID].validSlots[0] == items[options[selected].itemID].validSlots[0] ? selected == idx : items[item.itemID].validSlots[0] == slot) == i) ? generateItemImage(currentItems[CONSTANTS.equipmentSlot[items[item.itemID].validSlots[0]]].id, 0, false, selected == i, items[item.itemID].validSlots[0], currentItems[CONSTANTS.equipmentSlot[items[item.itemID].validSlots[0]]].isAlt, currentItems[CONSTANTS.equipmentSlot.Weapon].altAttacks) : '').join('') : ''}
${selected > -1 && category == 'food' ? generateItemImage(currentFood.id, currentFood.qty, options[selected].itemID == currentFood.id, options[selected].itemID != currentFood.id) : ''}
${selected > -1 && category == 'ammo' ? generateItemImage(currentItems[CONSTANTS.equipmentSlot.Quiver].id, currentItems[CONSTANTS.equipmentSlot.Quiver].qty, options[selected].itemID == currentItems[CONSTANTS.equipmentSlot.Quiver].id, options[selected].itemID != currentItems[CONSTANTS.equipmentSlot.Quiver].id, false, currentItems[CONSTANTS.equipmentSlot.Quiver].isAlt, currentItems[CONSTANTS.equipmentSlot.Weapon].altAttacks) : ''}
${selected > -1 && category == 'runes' ? generateRuneImage(currentRunes) : ''}
${selected > -1 && category == 'passive' ? generateItemImage(currentItems[CONSTANTS.equipmentSlot.Passive].id, 0, false, true, CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Passive], false) : ''}
</existing-items>
<span style="grid-area: 1 / 4 / 1 / 4; font-size: 36px; line-height: 60px; height: 60px; color: white; margin-top: -2px; margin-left: 5px; margin-right: 5px;">-></span>
<item-options style="display: inline-block; grid-area: 1 / 5 / 1 / 5; margin-right: 10px;">
${options.map((item, i) => (item != undefined && item.itemID != undefined && item.itemID > -1) ? generateItemImage(item.itemID, item.qty, selected == i, false, items[item.itemID].validSlots != undefined ? (category == 'passive' ? CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Passive] : items[item.itemID].validSlots[0]) : false, item.isAlt, item.altAttacks) : '').join('')}
</item-options>
</entry>`;
$('log').prepend(logEntry);
tippy($('item-icon', $('log').get(0).children[0]).toArray(), {
content: '',
allowHTML: true,
placement: "left",
interactive: false,
animation: false,
onShow: itemOnShow,
onHidden: itemOnHidden,
});
tippy($('special-icon', $('log').get(0).children[0]).toArray(), {
content: '',
allowHTML: true,
placement: "left",
interactive: false,
animation: false,
onShow: specialOnShow,
onHidden: specialOnHidden,
});
tippy($('rune-icon', $('log').get(0).children[0]).toArray(), {
content: '',
allowHTML: true,
placement: "left",
interactive: true,
animation: false,
onShow: runeOnShow,
onHidden: runeOnHidden,
});
tippy($('wave', $('log').get(0).children[0]).get(0), {
content: '',
allowHTML: true,
placement: "left",
interactive: true,
animation: false,
onShow: equipmentOnShow,
onHidden: equipmentOnHidden,
appendTo: () => document.body
});
if($('log').get(0).children.length > 25) {
let entries = $('log').get(0).children;
$('item-icon', entries[entries.length-1]).toArray().forEach(el => {
if(el._tippy)
el._tippy.destroy();
});
$('rune-icon', entries[entries.length-1]).toArray().forEach(el => {
if(el._tippy)
el._tippy.destroy();
});
$('wave', entries[entries.length-1]).toArray().forEach(el => {
if(el._tippy)
el._tippy.destroy();
});
if(entries[entries.length-1]._tippy)
entries[entries.length-1]._tippy.destroy();
$('log').get(0).removeChild(entries[entries.length-1]);
entries = null;
}
}
function generateModifiersTab() {
//modifierData
//RaidManager.possibleModifiers
/*
const playerMods = new MappedModifiers();
playerMods.addArrayModifiers(this.randomPlayerModifiers);
const golbinMods = new MappedModifiers();
golbinMods.addArrayModifiers(this.randomEnemyModifiers);
...playerMods.getActiveModifierDescriptions()
...golbinMods.getActiveModifierDescriptions()
*/
return `
<div style='display: grid; grid-template-rows: 60px 1fr; justify-items: start; align-items: center; position: absolute; top: 0; left: 0; right: 0; bottom: 0; overflow-y: scroll;'>
<buttons style='display: grid; grid-template-rows: 60px; grid-template-columns: repeat(2, 1fr); justify-items: center; align-items: center; width: 100%;'>
<button class='btn m-1 btn-outline-success' onclick="showGolbinRaiderModifiers('positive')">Positive</button>
<button class='btn m-1 btn-outline-danger' onclick="showGolbinRaiderModifiers('negative')">Negative</button>
</buttons>
<modifiers style='display: grid; grid-template-columns: 1fr; grid-auto-flow: row; margin: 10px;'></modifiers>
</div>
`;
}
let sortableModifiersInstance = false;
function updateGolbinModifierSoftCap(type, key, val) {
golbinRaiderModifierPriority[type].find(m => m.key == key).soft = parseInt(val);
localStorage.setItem(`golbinRaiderModifierPriority`, JSON.stringify(golbinRaiderModifierPriority));
}
window.updateGolbinModifierSoftCap = updateGolbinModifierSoftCap;
function updateGolbinModifierHardCap(type, key, val) {
golbinRaiderModifierPriority[type].find(m => m.key == key).hard = parseInt(val);
localStorage.setItem(`golbinRaiderModifierPriority`, JSON.stringify(golbinRaiderModifierPriority));
}
window.updateGolbinModifierHardCap = updateGolbinModifierHardCap;
function showGolbinRaiderModifiers(type) {
if(sortableModifiersInstance) {
sortableModifiersInstance.destroy();
sortableModifiersInstance = false;
}
let buttons = $('buttons button', $('sidebar container tabs tab.modifiers')).toArray()
buttons[0].classList.remove('btn-secondary');
buttons[1].classList.remove('btn-secondary');
if(type == 'positive')
buttons[0].classList.add('btn-secondary');
if(type == 'negative')
buttons[1].classList.add('btn-secondary');
let posHead = getLangString('GOLBIN_RAID', type == 'positive' ? 'GIVES_YOU' : 'GIVES_GOLBINS');
let negHead = getLangString('GOLBIN_RAID', type == 'positive' ? 'GIVES_GOLBINS' : 'GIVES_YOU');
let textClass = type == 'positive' ? 'text-success' : 'text-danger';
$('sidebar container tabs tab.modifiers modifiers').get(0).innerHTML = '';
$('sidebar container tabs tab.modifiers modifiers').append(`
<header style='display: grid; grid-template-columns: 1fr repeat(2, 100px); grid-auto-flow: row; align-items: center;'>
<span class='text-white'>Modifier</span>
<span class='text-white'>Soft Cap</span>
<span class='text-white'>Hard Cap</span>
</header>`);
let sortedModifiers = [...RaidManager.possibleModifiers].sort((a,b) => golbinRaiderModifierPriority[type].findIndex(m => m.key == a.key) - golbinRaiderModifierPriority[type].findIndex(m => m.key == b.key));
$('sidebar container tabs tab.modifiers modifiers').append(sortedModifiers.map(mod => {
const modData = modifierData[mod.key];
let min = 1;
let max = 5;
if(mod.multiplier !== undefined) {
min *= mod.multiplier;
max *= mod.multiplier;
}
if(modData.modifyValue !== undefined) {
min = modData.modifyValue(min);
max = modData.modifyValue(max);
}
let val = `<span class='text-warning'>(${min}-${max})</span>`;
const text = `${modData.isNegative ? negHead : posHead} ${templateString(modifierData[mod.key].langDescription, {value: val})}`;
return `<modifier style='display: grid; grid-template-columns: 1fr repeat(2, 100px); grid-auto-flow: row; align-items: center;'>
<span class='${textClass}'>
<small>${text}</small>
</span>
<value style="display: inline-block; width: 100px;">
<input type="number" min="0" class="form-control m-1" placeholder="" value="${golbinRaiderModifierPriority[type].find(m => m.key == mod.key).soft}" oninput='updateGolbinModifierSoftCap("${type}", "${mod.key}", this.value)'>
</value>
<value style="display: inline-block; width: 100px;">
<input type="number" min="0" class="form-control m-1" placeholder="" value="${golbinRaiderModifierPriority[type].find(m => m.key == mod.key).hard}" oninput='updateGolbinModifierHardCap("${type}", "${mod.key}", this.value)'>
</value>
</modifier>`;
}).join(''));
sortableModifiersInstance = Sortable.create($('sidebar container tabs tab.modifiers modifiers').get(0), {
group: `golbinRaiderModifiers`,
draggable: "modifier",
filter: "header",
handle: ".text-success, .text-danger",
onEnd: (evt) => {
golbinRaiderModifierPriority[type].splice(evt.newDraggableIndex, 0, golbinRaiderModifierPriority[type].splice(evt.oldDraggableIndex, 1)[0]);
localStorage.setItem(`golbinRaiderModifierPriority`, JSON.stringify(golbinRaiderModifierPriority));
},
onMove: function() {
tippy.hideAll();
},
onChoose: function(evt) {
tippy.hideAll();
},
});
}
window.showGolbinRaiderModifiers = showGolbinRaiderModifiers;
function generatePrioritiesTab() {
return `
<priorities style='display: grid; grid-template-rows: repeat(2, 60px); grid-template-columns: repeat(6, 60px); justify-items: center; align-items: center;'>
${prioritiesList.map(slot => `<priority onclick="showGolbinRaiderPriority('${CONSTANTS.equipmentSlot[slot]}')">${generateItemImage(-1, 0, false, false, CONSTANTS.equipmentSlot[slot])}</priority>`).join('')}
</priorities>
<itemlist style='height: 100%; width: 100%;'></itemlist>
`;
};
function showGolbinRaiderItemSelect(slot, index) {
$("#modal-golbinraider-item-select").get(0).dataset.slot = slot;
$("#modal-golbinraider-item-select").get(0).dataset.index = index;
updateGolbinRaiderItemSearch('');
$("#modal-golbinraider-item-select").modal("show");
}
let sortableInstance = false;
function showGolbinRaiderPriority(slot) {
if(sortableInstance) {
sortableInstance.destroy();
sortableInstance = false;
}
$('item-icon img', $('sidebar container tabs tab priorities').get(0)).toArray().forEach((el, i) => {
el.classList.remove('border-info');
if(CONSTANTS.equipmentSlot[prioritiesList[i]] == slot)
el.classList.add('border-info');
});
$('sidebar container tabs tab itemlist').get(0).innerHTML = '';
$('sidebar container tabs tab itemlist').append(golbinRaiderPriorities[slot].map(id => generateItemImage(id, 0, false, false, slot)).join(''));
$('sidebar container tabs tab itemlist').append(generateItemImage(-1, 0, false, false, slot));
sortableInstance = Sortable.create($('sidebar container tabs tab itemlist').get(0), {
group: `golbinRaiderPriority-${slot}`,
draggable: "item-icon",
onEnd: (evt) => {
golbinRaiderPriorities[slot].splice(evt.newIndex, 0, golbinRaiderPriorities[slot].splice(evt.oldIndex, 1)[0]);
golbinRaiderPriorities[slot] = golbinRaiderPriorities[slot].filter(id => id > -1);
localStorage.setItem(`golbinRaiderPriorities`, JSON.stringify(golbinRaiderPriorities));
},
onMove: function() {
tippy.hideAll();
},
onChoose: function(evt) {
tippy.hideAll();
},
});
$('item-icon', $('sidebar container tabs tab itemlist').get(0)).toArray().forEach((el, i) => {
el.setAttribute('onclick', `showGolbinRaiderItemSelect('${slot}', ${i})`);
});
tippy($('item-icon', $('sidebar container tabs tab itemlist').get(0)).toArray(), {
content: '',
allowHTML: true,
placement: "left",
interactive: false,
animation: false,
onShow: itemOnShow,
onHidden: itemOnHidden
});
}
function generateDropdown(collection=[],
selected=-1,
content=() => `<img class="skill-icon-xs mr-1" src="${CDNDIR}assets/media/main/bank_header.svg">`,
render=({media, name}) => `<a class="dropdown-item pointer-enabled"><img class="skill-icon-xs mr-1" src="${media}">${name}</a>`) {
return `
<div class="dropdown" style="margin: 0 5px; display: inline-block;">
<button type="button" class="btn btn-dark dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${content(selected, collection.map ? collection : Object.values(collection))}
</button>
<div class="dropdown-menu dropdown-menu-right font-size-sm overflow-y-auto" style="max-height:80vh;">
${(collection.map ? collection : Object.values(collection)).map(item => render(item)).join('')}
</div>
</div>
`;
}
function triggerStepsSave() {
localStorage.setItem(`golbinRaiderSelectionSteps`, JSON.stringify(golbinRaiderSelectionSteps));
}
function getConditionAndStep(idxs) {
let path = idxs.length ? [...idxs] : [idxs];
let i = path.shift();
let condition = golbinRaiderSelectionSteps[i];
let step = $('steps > sortable-steps > step, steps > step').get(i);
if(path.length > 0) {
condition = path.reduce((a,b) => a.conditions[b], condition);
step = path.reduce((a,b) => $('> conditional > conditions > step', a).get(b), step);
}
return {condition: condition, step: step};
}
function updateGolbinRaiderConditional(idxs, id) {
let { condition, step } = getConditionAndStep(idxs);
let el = $('> conditional', step).get(0);
let old = condition.conditional;
condition.conditional = id;
if(golbinRaidConditionals[old].params)
golbinRaidConditionals[old].params.forEach(param => delete condition[param.id]);
if(golbinRaidConditionals[id].params)
golbinRaidConditionals[id].params.forEach(param => param.visible === undefined || param.visible(condition) ? condition[param.id] = param.default : '');
if(golbinRaidConditionals[old].container && !golbinRaidConditionals[id].container)
delete condition.conditions;
if(!golbinRaidConditionals[old].container && golbinRaidConditionals[id].container) {
delete condition.comparison;
condition.conditions = [];
}
if(condition.comparison) {
if(golbinRaidComparisons[condition.comparison].collection) {
delete condition.collection;
} else {
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[old].values) {
golbinRaidConditionals[old].values(condition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
values[idx] = val;
});
}
if(values.length > 0)
values.forEach(value => delete condition[value.id]);
}
}
let comparisons = golbinRaidConditionals[id].comparisons && golbinRaidConditionals[id].comparisons(condition);
if(comparisons && comparisons.length > 0) {
if(comparisons.findIndex(comp => comp.id == condition.comparison) == -1)
condition.comparison = comparisons[0].id;
} else {
delete condition.comparison;
}
if(condition.comparison) {
if(golbinRaidComparisons[condition.comparison].collection) {
condition.collection = [];
} else {
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[id].values) {
golbinRaidConditionals[id].values(condition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
values[idx] = val;
});
}
if(values.length > 0)
values.forEach(value => condition[value.id] = value.default);
}
}
el.outerHTML = generateConditional(idxs, condition);
triggerStepsSave();
}
window.updateGolbinRaiderConditional = updateGolbinRaiderConditional;
function updateGolbinRaiderComparison(idxs, id) {
let { condition, step } = getConditionAndStep(idxs);
let el = $('> conditional > comparison', step).get(0);
let old = condition.comparison;
condition.comparison = id;
let oldValues = [...golbinRaidComparisons[old].values(condition)];
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[condition.conditional].values) {
golbinRaidConditionals[condition.conditional].values(condition).forEach(val => {
let oldIdx = oldValues.findIndex(v => v.id == val.id);
let idx = values.findIndex(v => v.id == val.id);
if(oldIdx > -1)
oldValues[oldIdx] = val;
if(idx > -1)
values[idx] = val;
});
}
if(golbinRaidComparisons[old].collection && golbinRaidComparisons[id].collection) {
if(oldValues.length > 0) {
oldValues.forEach(value => {
let vIdx = values.findIndex(val => val.id == value.id);
if(vIdx == -1 || values[vIdx].type != value.type) {
condition.collection.forEach(item => {
delete item[value.id];
});
}
});
}
if(values.length > 0) {
values.forEach((value, i) => {
let vIdx = oldValues.findIndex(val => val.id == value.id);
if(vIdx == -1 || oldValues[vIdx].type != value.type) {
condition.collection.forEach((item, j) => {
item[value.id] = value.default;
updateGolbinRaiderValue(idxs, value.id, condition[value.id]);
});
}
});
}
} else if(golbinRaidComparisons[old].collection && !golbinRaidComparisons[id].collection) {
delete condition.collection;
if(values.length > 0) {
values.forEach((value, i) => {
condition[value.id] = value.default;
updateGolbinRaiderValue(idxs, value.id, condition[value.id]);
});
}
} else if(!golbinRaidComparisons[old].collection && golbinRaidComparisons[id].collection) {
condition.collection = [];
if(oldValues.length > 0)
oldValues.forEach(value => delete condition[value.id]);
} else {
if(values.length > 0) {
values.forEach((value, i) => {
let vIdx = oldValues.findIndex(val => val.id == value.id);
if(vIdx == -1 || oldValues[vIdx].type != value.type) {
condition[value.id] = value.default;
}
updateGolbinRaiderValue(idxs, value.id, condition[value.id]);
});
}
}
el.outerHTML = generateComparison(idxs, condition);
triggerStepsSave();
}
window.updateGolbinRaiderComparison = updateGolbinRaiderComparison;
function updateGolbinRaiderResult(idxs, id) {
let { condition, step } = getConditionAndStep(idxs);
let el = $('> conditional > result', step).get(0);
condition.result = id;
el.outerHTML = generateResult(idxs, condition);
triggerStepsSave();
}
window.updateGolbinRaiderResult = updateGolbinRaiderResult;
function updateGolbinRaiderParameter(idxs, i, id) {
let { condition, step } = getConditionAndStep(idxs);
let els = $('> conditional > parameters', step);
let param = golbinRaidConditionals[condition.conditional].params[i];
let oldCondition = { ...condition }
condition[param.id] = id;
for(let j=0; j < golbinRaidConditionals[condition.conditional].params.length; j++) {
let p = golbinRaidConditionals[condition.conditional].params[j];
if(p.visible === undefined || p.visible(condition)) {
let values = p.values && p.values(condition);
if(values && values.length > 0) {
if(values.findIndex(val => val.id == condition[p.id]) == -1) {
condition[p.id] = values[0].id;
}
}
} else {
delete condition[p.id];
}
}
for(let j=0; j < golbinRaidConditionals[condition.conditional].params.length; j++) {
let p = golbinRaidConditionals[condition.conditional].params[j];
let el = $('parameter', els).get(j);
if(p.visible === undefined || p.visible(condition)) {
if(el === undefined) {
condition[p.id] = p.default;
els.append(generateParam(idxs, j, condition));
} else {
el.outerHTML = generateParam(idxs, j, condition);
}
} else {
if(el !== undefined) {
el.outerHTML = '';
}
}
}
let comparisons = golbinRaidConditionals[condition.conditional].comparisons && golbinRaidConditionals[condition.conditional].comparisons(condition);
if(comparisons && comparisons.length > 0) {
if(comparisons.findIndex(comp => comp.id == condition.comparison) == -1) {
condition.comparison = comparisons[0].id;
}
updateGolbinRaiderComparison(idxs, condition.comparison);
} else {
delete condition.comparison;
}
let oldValues = [...golbinRaidComparisons[oldCondition.comparison].values(oldCondition)];
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[condition.conditional].values) {
golbinRaidConditionals[condition.conditional].values(condition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
values[idx] = val;
});
}
if(golbinRaidConditionals[oldCondition.conditional].values) {
golbinRaidConditionals[oldCondition.conditional].values(oldCondition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
oldValues[idx] = val;
});
}
if(golbinRaidComparisons[oldCondition.comparison].collection && golbinRaidComparisons[condition.comparison].collection) {
if(oldValues.length > 0) {
oldValues.forEach(value => {
let vIdx = values.findIndex(val => val.id == value.id);
if(vIdx == -1 || values[vIdx].type != value.type) {
condition.collection.forEach(item => {
delete item[value.id];
});
}
});
}
if(values.length > 0) {
values.forEach((value, i) => {
let vIdx = oldValues.findIndex(val => val.id == value.id);
if(vIdx == -1 || oldValues[vIdx].type != value.type) {
condition.collection.forEach((item, j) => {
item[value.id] = value.default;
updateGolbinRaiderValue(idxs, value.id, condition[value.id]);
});
}
});
}
} else if(golbinRaidComparisons[oldCondition.comparison].collection && !golbinRaidComparisons[condition.comparison].collection) {
delete condition.collection;
if(values.length > 0) {
values.forEach((value, i) => {
condition[value.id] = value.default;
updateGolbinRaiderValue(idxs, value.id, condition[value.id]);
});
}
} else if(!golbinRaidComparisons[oldCondition.comparison].collection && golbinRaidComparisons[condition.comparison].collection) {
condition.collection = [];
if(oldValues.length > 0)
oldValues.forEach(value => delete condition[value.id]);
} else {
if(values.length > 0) {
values.forEach((value, i) => {
let vIdx = oldValues.findIndex(val => val.id == value.id);
if(vIdx == -1 || oldValues[vIdx].type != value.type) {
condition[value.id] = value.default;
}
updateGolbinRaiderValue(idxs, value.id, condition[value.id]);
});
}
}
triggerStepsSave();
}
window.updateGolbinRaiderParameter = updateGolbinRaiderParameter;
function updateGolbinRaiderValue(idxs, i, id, collectionIdx=-1) {
let { condition, step } = getConditionAndStep(idxs);
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[condition.conditional].values) {
golbinRaidConditionals[condition.conditional].values(condition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
values[idx] = val;
});
}
let value = values.find(val => val.id == i);
let valueIdx = values.findIndex(val => val.id == i);
let els = $('> conditional > comparison > values', step).get(0);
let el = $('> value, > value-collection > value', els).get(collectionIdx > -1 ? collectionIdx : valueIdx);
if(collectionIdx > -1) {
condition.collection[collectionIdx][i] = id;
} else {
condition[i] = id;
}
if(el && !el.contains(document.activeElement)) // Fixes redrawing text boxes while editing
el.outerHTML = generateValue(idxs, i, condition, collectionIdx);
triggerStepsSave();
}
window.updateGolbinRaiderValue = updateGolbinRaiderValue;
function addGolbinRaiderStep(idxs=-1) {
if(idxs == -1) {
golbinRaiderSelectionSteps.splice(golbinRaiderSelectionSteps.length-1, 0, {conditional: 'default', result: 'food'});
$('steps > sortable-steps').append(generateStep([golbinRaiderSelectionSteps.length-2], golbinRaiderSelectionSteps[golbinRaiderSelectionSteps.length-2]));
$('steps > step').get(0).outerHTML = generateStep([golbinRaiderSelectionSteps.length-1], golbinRaiderSelectionSteps[golbinRaiderSelectionSteps.length-1]);
} else {
let { condition, step } = getConditionAndStep(idxs);
condition.conditions.push({conditional: 'default'});
$('> conditional > conditions > add-condition', step).before(generateStep(idxs.concat([condition.conditions.length-1]), condition.conditions[condition.conditions.length-1]));
}
triggerStepsSave();
}
window.addGolbinRaiderStep = addGolbinRaiderStep;
function addGolbinRaiderValueCollection(idxs) {
let { condition, step } = getConditionAndStep(idxs);
let valuesRoot = $('> conditional > comparison > values', step).get(0);
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[condition.conditional].values) {
golbinRaidConditionals[condition.conditional].values(condition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
values[idx] = val;
});
}
let newValues = {};
if(values.length > 0)
values.forEach(value => newValues[value.id] = value.default);
condition.collection.push(newValues);
$('add-value', valuesRoot).before(generateValueCollection(idxs, condition, condition.collection.length-1));
triggerStepsSave();
}
window.addGolbinRaiderValueCollection = addGolbinRaiderValueCollection;
function removeGolbinRaiderValueCollection(idxs, collectionIdx) {
let { condition, step } = getConditionAndStep(idxs);
$('> conditional > comparison > values > value-collection', step).get(collectionIdx).outerHTML = '';
condition.collection.splice(collectionIdx, 1);
for(let i=collectionIdx; i<condition.collection.length; i++)
$('> conditional > comparison > values > value-collection', step).get(i).outerHTML = generateValueCollection(idxs, condition, i);
triggerStepsSave();
}
window.removeGolbinRaiderValueCollection = removeGolbinRaiderValueCollection;
function removeGolbinRaiderStep(idxs) {
if(idxs.length == 1) {
let { condition, step } = getConditionAndStep(idxs);
golbinRaiderSelectionSteps.splice(idxs[0], 1);
step.outerHTML = '';
for(let i=idxs[0]; i<golbinRaiderSelectionSteps.length-1; i++)
$('steps > sortable-steps > step').get(i).outerHTML = generateStep([i], golbinRaiderSelectionSteps[i]);
$('steps > step').get(0).outerHTML = generateStep([golbinRaiderSelectionSteps.length-1], golbinRaiderSelectionSteps[golbinRaiderSelectionSteps.length-1]);
} else {
let { step } = getConditionAndStep(idxs);
let idx = idxs.pop();
let { condition, step: conditionRoot } = getConditionAndStep(idxs);
step.outerHTML = '';
condition.conditions.splice(idx, 1);
for(let i=idx; i<condition.conditions.length; i++)
$('> conditional > conditions > step', conditionRoot).get(i).outerHTML = generateStep(idxs.concat(i), condition.conditions[i]);
}
triggerStepsSave();
}
window.removeGolbinRaiderStep = removeGolbinRaiderStep;
function generateConditional(idxs, condition) {
let conditional = `<conditional style="display: flex; align-items: center;">`;
if(golbinRaidConditionals[condition.conditional].selectable && !golbinRaidConditionals[condition.conditional].visible) {
conditional += `<div class="btn btn-dark">${golbinRaidConditionals[condition.conditional].name}</div>`
} else {
conditional += `
${generateDropdown(golbinRaidConditionals,
condition,
({conditional}) => golbinRaidConditionals[conditional].name,
({id, name, selectable, visible}) => (selectable && visible ? `<a class='dropdown-item pointer-enabled' onclick='updateGolbinRaiderConditional(${JSON.stringify(idxs)}, ${typeof id == 'number' || typeof id == 'boolean' ? id : `"${id}"`})'>${name}</a>` : ''))}
`;
}
if(golbinRaidConditionals[condition.conditional].params && golbinRaidConditionals[condition.conditional].params.length > 0) {
conditional += `
<parameters>
${golbinRaidConditionals[condition.conditional].params.map((param, i) => generateParam(idxs, i, condition)).join('')}
</parameters>`;
}
if(condition.comparison) {
conditional += generateComparison(idxs, condition);
}
if(condition.conditions) {
conditional += `
<conditions>
${condition.conditions.map((con, i) => generateStep(idxs.concat([i]), con)).join('')}
<add-condition
style="display: inline-flex; justify-content: flex-start; position: relative; min-height: 60px; margin: 3px; align-items: center; border: 2px solid black; border-radius: 10px; padding: 5px;">
<img class="pointer-enabled skill-icon-xs mr-1" onclick="addGolbinRaiderStep(${JSON.stringify(idxs)})" src="${CDNDIR}assets/media/main/plus.svg">
</add-condition>
</conditions>`;
}
if(condition.result) {
conditional += generateResult(idxs, condition);
}
conditional += `</conditional>`;
return conditional;
}
function generateValueCollection(idxs, condition, collectionIdx) {
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[condition.conditional].values) {
golbinRaidConditionals[condition.conditional].values(condition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
values[idx] = val;
});
}
return `<value-collection style="position: relative; margin: 3px; padding-right: 25px;">
<remove-value style="position: absolute; right: 0; height: 100%; width: 25px; display: flex; justify-content: center; align-items: center; align-self: center;">
<button type="button" class="btn-block-option" onclick="removeGolbinRaiderValueCollection(${JSON.stringify(idxs)}, ${collectionIdx})" aria-label="Close">
<i class="fa fa-fw fa-times"></i>
</button>
</remove-value>
${values.map((v, j) => generateValue(idxs, v.id, condition, collectionIdx)).join('')}
</value-collection>`;
}
function generateValues(idxs, condition) {
let valueStr = ``;
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[condition.conditional].values) {
golbinRaidConditionals[condition.conditional].values(condition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
values[idx] = val;
});
}
if(values.length > 0) {
valueStr += `<values style="display: grid; grid-template-columns: 1fr; margin: 3px; padding: 5px; grid-auto-flow: row; grid-gap: 4px; ${golbinRaidComparisons[condition.comparison].collection ? `border: 2px solid black; border-radius: 10px;` : ``}">`;
if(golbinRaidComparisons[condition.comparison].collection) {
valueStr += condition.collection.map((val, i) => {
return generateValueCollection(idxs, condition, i);
}).join('')
valueStr += `<add-value
style="display: inline-flex; justify-content: flex-start; position: relative; margin: 3px; align-items: center;">
<img class="pointer-enabled skill-icon-xs mr-1" src="${CDNDIR}assets/media/main/plus.svg" onclick="addGolbinRaiderValueCollection(${JSON.stringify(idxs)})">
</add-value>`;
} else {
valueStr += values.map((val, i) => generateValue(idxs, val.id, condition)).join('')
}
valueStr += `</values>`;
}
return valueStr;
}
function generateComparison(idxs, condition) {
let comparison = `
<comparison style="display: flex; align-items: center;">
${generateDropdown(golbinRaidConditionals[condition.conditional].comparisons(condition),
condition,
({comparison}) => golbinRaidComparisons[comparison].name,
({id, name}) => `<a class='dropdown-item pointer-enabled' onclick='updateGolbinRaiderComparison(${JSON.stringify(idxs)}, ${typeof id == 'number' || typeof id == 'boolean' ? id : `"${id}"`})'>${name}</a>`)}`;
comparison += generateValues(idxs, condition);
comparison += `</comparison>`;
return comparison;
}
function generateResult(idxs, condition) {
return `
<result style="display: flex; align-items: center; margin-left: auto; margin-right: 15px; padding-left: 10px;">
<span style="width: 40px; font-size: 36px; color: white;">-></span>
${generateDropdown(golbinRaidResults,
condition,
({result}) => golbinRaidResults[result].name,
({id, name}) => `<a class='dropdown-item pointer-enabled' onclick='updateGolbinRaiderResult(${JSON.stringify(idxs)}, ${typeof id == 'number' || typeof id == 'boolean' ? id : `"${id}"`})'>${name}</a>`)}
</result>`;
}
function generateParam(idxs, i, condition) {
let param = golbinRaidConditionals[condition.conditional].params[i];
if(param.visible === undefined || param.visible(condition)) {
if(param.type == "dropdown") {
return `
<parameter>
${generateDropdown(param.values(condition),
condition,
(args, collection) => {
let {[param.id]:result} = args;
return param.content ? param.content(args, collection) : (collection.find(c => c.id == result) || collection.find(c => c.id == param.default)).name;
},
(args) => {
let { id, name } = args;
return `<a class='dropdown-item pointer-enabled' onclick='updateGolbinRaiderParameter(${JSON.stringify(idxs)}, ${i}, ${typeof id == 'number' || typeof id == 'boolean' ? id : `"${id}"`})'>${param.render ? param.render(args) : name}</a>`
}
)}
</parameter>`;
} else {
console.log("unknown param" + param.type);
return '';
}
} else {
return ''
}
}
function generateValue(idxs, id, condition, collectionIdx=-1) {
let values = [...golbinRaidComparisons[condition.comparison].values(condition)];
if(golbinRaidConditionals[condition.conditional].values) {
golbinRaidConditionals[condition.conditional].values(condition).forEach(val => {
let idx = values.findIndex(v => v.id == val.id);
if(idx > -1)
values[idx] = val;
});
}
let value = values.find(val => val.id == id);
let ret = ``;
if(value.type == "dropdown") {
ret += `
<value style="display: inline-block;">
${generateDropdown(value.values(condition),
collectionIdx > -1 ? condition.collection[collectionIdx] : condition,
(args, collection) => {
let {[value.id]:result} = args;
return value.content ? value.content(args, collection) : collection[result].name;
},
(args) => {
let { id, name } = args;
return `<a class='dropdown-item pointer-enabled' onclick='updateGolbinRaiderValue(${JSON.stringify(idxs)}, "${value.id}", ${typeof id == 'number' || typeof id == 'boolean' ? id : `"${id}"`}${collectionIdx > -1 ? `, ${collectionIdx}` : ''})'>${value.render ? value.render(args) : name}</a>`
}
)}
</value>`;
} else if(value.type == "number") {
let result = condition[value.id];
ret += `
<value style="display: inline-block; width: 100px;">
<input type="number" min="0" class="form-control m-1" placeholder="" value="${result}" oninput='updateGolbinRaiderValue(${JSON.stringify(idxs)}, "${value.id}", this.value${collectionIdx > -1 ? `, ${collectionIdx}` : ''})'>
</value>`;
} else {
console.log(`unknown value ${value.type}`);
return '';
}
return ret;
}
function generateStep(idxs, step) {
return `
<step style="display: flex; justify-content: flex-start; position: relative; min-height: 60px; margin: 3px; align-items: center; border: 2px solid black; border-radius: 10px; padding: 15px;">
<remove-step style="align-self: flex-end; position: absolute; top: 0; right: 0; height: 25px; width: 25px; display: flex; justify-content: center; align-items: center;">
<button type="button" class="btn-block-option" onclick="removeGolbinRaiderStep(${JSON.stringify(idxs)})" aria-label="Close">
<i class="fa fa-fw fa-times"></i>
</button>
</remove-step>
${idxs.length == 1 ? `<step-number style="display: inline-block; font-size: 36px; line-height: 60px; height: 60px; color: white; min-width: 35px;">${idxs[idxs.length-1] + 1}</step-number>` : ``}
${generateConditional(idxs, step)}
</step>
`;
}
function generateSteps(steps) {
return `
<steps style='position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: 0; overflow-y: auto;'>
<sortable-steps>
${steps.map((step, i) => i != steps.length-1 ? generateStep([i], step) : '').join('')}
</sortable-steps>
<add-step
style="display: inline-flex; justify-content: flex-start; position: relative; min-height: 60px; margin: 3px; align-items: center; border: 2px solid black; border-radius: 10px; padding: 5px;">
<img class="pointer-enabled skill-icon-xs mr-1" onclick="addGolbinRaiderStep()" src="${CDNDIR}assets/media/main/plus.svg">
</add-step>
${generateStep([steps.length-1], steps[steps.length-1])}
</steps>
`;
}
function generateSideBar() {
return `
<sidebar style='position: fixed; right: -600px; top: 4rem; height: 90%; width: 600px; z-index: 999; transition: right 500ms ease, width 500ms ease; background-color: grey; border-radius: 10px;'>
<button
style='position: absolute; left: -40px; top: 50%; height: 50px; width: 40px; z-index: 999; background-color: #a9a9a9;'
onclick='toggleGolbinRaiderSidebar();'>GR</button>
<container style='position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: 10px; background-color: #404040; display: grid; grid-template-rows: 30px 30px auto; grid-template-columns: auto; border-radius: 10px;'>
<stats style='position: absolute; top: 0; left: 0; right: 0; bottom: 0; grid-area: 1 / 1 / 1 / 1; display: grid; grid-template-rows: auto; grid-template-columns: auto;'></stats>
<navigation style='position: absolute; top: 0; left: 0; right: 0; bottom: 0; grid-area: 2 / 1 / 2 / 1;'>
<button class='history' onclick='showGolbinRaiderTab(0);'>History</button>
<button class='priorities' onclick='showGolbinRaiderTab(1);'>Priorities</button>
<button class='steps' onclick='showGolbinRaiderTab(2);'>Steps</button>
<button class='modifiers' onclick='showGolbinRaiderTab(3);'>Modifiers</button>
<button class='runes' onclick='showGolbinRaiderTab(4);'>Runes</button>
<button class='weights' onclick='showGolbinRaiderTab(5);'>Weights</button>
<button class='settings' onclick='showGolbinRaiderTab(6);'>Settings</button>
</navigation>
<tabs style='position: absolute; top: 0; left: 0; right: 0; bottom: 0; grid-area: 3 / 1 / 3 / 1;'>
<tab class='history' style='position: absolute; top: 0; left: 0; right: 0; bottom: 0;'>
<log style='position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: 0; overflow-y: auto;'></log>
</tab>
<tab class='priorities d-none' style='position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: grid; grid-template-rows: 120px auto; grid-template-columns: auto; justify-items: center; align-items: center;'>
${generatePrioritiesTab()}
</tab>
<tab class='steps d-none' style='position: absolute; top: 0; left: 0; right: 0; bottom: 0;'>
${generateSteps(golbinRaiderSelectionSteps)}
</tab>
<tab class='modifiers d-none' style='position: absolute; top: 0; left: 0; right: 0; bottom: 0;'>
${generateModifiersTab()}
</tab>
<tab class='runes d-none' style='position: absolute; top: 0; left: 0; right: 0; bottom: 0;'>
<span style='color: white;'>Coming Soon :)</span>
</tab>
<tab class='weights d-none' style='position: absolute; top: 0; left: 0; right: 0; bottom: 0;'>
<span style='color: white;'>Coming Soon :)</span>
</tab>
<tab class='settings d-none'>
<span style='color: white;'>Coming Soon :)</span>
</tab>
</tabs>
</container>
</sidebar>`;
}
function generateItemSearchModal() {
return `
<div class="modal" id="modal-golbinraider-item-select" tabindex="-1" role="dialog" aria-labelledby="modal-block-extra-large" aria-modal="true" style="display: none;">
<div class="modal-dialog modal-xl" role="document" style="height:80%;">
<div class="modal-content">
<div class="block block-themed block-transparent mb-0">
<div class="block-header bg-primary-dark">
<h3 class="block-title">
Hey
</h3>
<div class="block-options">
<button type="button" class="btn-block-option" data-dismiss="modal" aria-label="Close">
<i class="fa fa-fw fa-times"></i>
</button>
</div>
</div>
<div class="block-content block-content-full">
<div class="col-12">
<div class="row" id="golbinraider-items-search">
<div class="col-12">
<input type="text" class="form-control form-control-lg py-4" id="golbinraider-item-search" name="golbinraider-item-search" placeholder="Search Items">
</div>
<div id="golbinraider-items-list" class="col-12">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`
}
function updateGolbinRaiderItemSearch(search) {
let slot = $("#modal-golbinraider-item-select").get(0).dataset.slot;
let index = $("#modal-golbinraider-item-select").get(0).dataset.index;
const options = {
shouldSort: true,
tokenize: true,
matchAllTokens: true,
findAllMatches: true,
threshold: 0,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: ["name"],
};
let slotItems = items.filter(item => item.validSlots && item.validSlots.includes(slot) && !RaidManager.bannedItems.includes(item.id) && (!golbinRaiderPriorities[slot].includes(item.id) || item.id == golbinRaiderPriorities[slot][index]));
if(slot == CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Passive])
slotItems = slotItems.filter(item => !RaidManager.bannedPassiveItems.includes(item.id))
const fuse = new Fuse(slotItems, options);
let result = fuse.search(search);
if(search == '')
result = slotItems;
$('item-icon', $('#golbinraider-items-list').get(0)).toArray().forEach(el => {
if(el._tippy)
el._tippy.destroy();
});
$('#golbinraider-items-list').get(0).innerHTML = '';
$('#golbinraider-items-list').append(generateItemImage(-1, 0, false, true, slot));
$('#golbinraider-items-list').append(result.map(item => generateItemImage(item.id, 0, item.id == golbinRaiderPriorities[slot][index], false, slot)).join(''));
$('item-icon', $('#golbinraider-items-list').get(0)).toArray().forEach(el => {
el.setAttribute('onclick', `selectGolbinRaiderItemPriority('${slot}', ${index}, ${el.dataset.id})`);
});
tippy($('item-icon', $('#golbinraider-items-list').get(0)).toArray(), {
content: '',
allowHTML: true,
placement: "left",
interactive: false,
animation: false,
onShow: itemOnShow,
onHidden: itemOnHidden
});
}
function selectGolbinRaiderItemPriority(slot, index, id=-1) {
golbinRaiderPriorities[slot][index] = id;
golbinRaiderPriorities[slot] = golbinRaiderPriorities[slot].filter(id => id > -1);
localStorage.setItem(`golbinRaiderPriorities`, JSON.stringify(golbinRaiderPriorities));
showGolbinRaiderPriority(slot);
$("#modal-golbinraider-item-select").modal("hide");
}
let sortableSteps = false;
function updateSortableSteps() {
if(sortableSteps) {
sortableSteps.destroy();
sortableSteps = false;
}
sortableSteps = Sortable.create($('sidebar container tabs tab steps sortable-steps').get(0), {
group: `golbinRaiderSteps`,
draggable: "step",
handle: 'step-number',
onEnd: (evt) => {
let start = Math.min(evt.newIndex, evt.oldIndex);
let end = Math.max(evt.newIndex, evt.oldIndex);
golbinRaiderSelectionSteps.splice(evt.newIndex, 0, golbinRaiderSelectionSteps.splice(evt.oldIndex, 1).pop());
for(let i=start; i<=end; i++)
$('steps > sortable-steps > step').get(i).outerHTML = generateStep([i], golbinRaiderSelectionSteps[i]);
triggerStepsSave();
},
onMove: function() {
},
onChoose: function(evt) {
},
});
}
const sideBar = generateSideBar();
$('#page-container').append(sideBar);
updateSortableSteps();
window.toggleGolbinRaiderSidebar = () => {
let width = $('sidebar').get(0).style.getPropertyValue('width');
let right = $('sidebar').get(0).style.getPropertyValue('right');
$('sidebar').get(0).style.setProperty('right', right == '0px' ? `-${width}` : '0px');
}
window.showGolbinRaiderTab = (i) => {
$($('sidebar tabs tab').addClass('d-none').get(i)).removeClass('d-none');
if(i == 2) {
$('sidebar').get(0).style.setProperty('width', '1200px');
} else {
$('sidebar').get(0).style.setProperty('width', '600px');
}
};
window.showGolbinRaiderPriority = showGolbinRaiderPriority;
window.showGolbinRaiderItemSelect = showGolbinRaiderItemSelect;
window.selectGolbinRaiderItemPriority = selectGolbinRaiderItemPriority;
$('#page-container').append(generateItemSearchModal());
$("#golbinraider-item-search").keyup(function() {
let search = $("#golbinraider-item-search").val();
updateGolbinRaiderItemSearch(search);
});
let golbinLogStats = [];
function addStat(updateFn) {
golbinLogStats.push({el: document.createElement('stat'), updateFn: updateFn});
golbinLogStats[golbinLogStats.length-1].el.style.setProperty('grid-area', `1 / ${golbinLogStats.length} / 1 / ${golbinLogStats.length}`);
golbinLogStats[golbinLogStats.length-1].el.style.setProperty('color', 'white');
golbinLogStats[golbinLogStats.length-1].el.style.setProperty('font-size', '12px');
golbinLogStats[golbinLogStats.length-1].el.style.setProperty('text-align', 'center');
$('stats').get(0).style.setProperty('grid-template-columns', golbinLogStats.map(s => 'auto').join(' '));
$('stats').append(golbinLogStats[golbinLogStats.length-1].el);
}
addStat(() => `Wave ${raidManager.wave+1}`);
addStat(() => `${raidManager.waveProgress + 1} / ${raidManager.waveLength}`);
//addStat(() => `${maxFood()} Max Food`);
//addStat(() => `${maxAmmo()} Max Ammo`);
//addStat(() => `${maxRunes()} Max Runes`);
//addStat(() => `${raidManager.player.equipmentStats.damageReduction}% DR`);
addStat(() => `${new Date((new Date().getTime() - raidManager.startTimestamp)).toISOString().substr(11, 8)}`);
addStat(() => `${raidManager.coinsEarned} GC`);
addStat(() => `${(raidManager.coinsEarned / ((new Date().getTime() - raidManager.startTimestamp)/1000)).toFixed(2)} GC/s`);
function statTick() {
if($('#page-header').get(0).classList.contains('bg-golbinraid') && $('sidebar button').get(0).classList.contains('d-none')) {
$('sidebar button').get(0).classList.remove('d-none');
} else if(!$('#page-header').get(0).classList.contains('bg-golbinraid') && !$('sidebar button').get(0).classList.contains('d-none')) {
$('sidebar button').get(0).classList.add('d-none');
}
if(!game.isGolbinRaid) return;
golbinLogStats.forEach(stat => stat.el.innerText = stat.updateFn());
}
let __raidManagerStopCombat = raidManager.stopCombat.bind(raidManager);
function _raidManagerStopCombat(fled=true) {
raidManager.selectedDifficulty = raidManager._setDifficulty;
__raidManagerStopCombat(...arguments);
if(!fled) {
setTimeout(() => { Swal.close(); raidManager.preStartRaid() }, 1000);
}
}
raidManager.stopCombat = _raidManagerStopCombat.bind(raidManager);
function restartCombat() {
raidManager.selectedDifficulty = raidManager._setDifficulty;
raidManager.stopCombat(true);
setTimeout(() => { Swal.close(); raidManager.preStartRaid() }, 1000);
}
let __golbinTick = raidManager.tick.bind(raidManager);
function _golbinTick() {
__golbinTick(...arguments);
try {
if(raidManager.state == RaidState.FightingWave) {
if(golbinRaiderSettings.autoHeal) {
let enemyMaxHit = Math.max(...raidManager.enemy.availableAttacks.map(a => raidManager.enemy.getAttackMaxDamage(a.attack)))
if(raidManager.player.hitpoints <= enemyMaxHit) {
//console.log(`ALMOST DIED ${raidManager.player.hitpoints}hp, ${enemyMaxHit} max hit`);
if(raidManager.player.hitpoints < raidManager.player.stats.maxHitpoints)
raidManager.player.eatFood();
}
}
}
if(!(raidManager.state === RaidState.Unstarted || raidManager.isPaused || !game.isGolbinRaid))
statTick();
} catch (e) {
console.log(e);
}
}
raidManager.tick = _golbinTick.bind(raidManager);
let __fireRandomModifierSelection = raidManager.fireRandomModifierSelection.bind(raidManager);
function _fireRandomModifierSelection() {
//__fireRandomModifierSelection(...arguments);
//modifierData
//RaidManager.possibleModifiers
const playerMods = new MappedModifiers();
playerMods.addArrayModifiers(this.randomPlayerModifiers);
const golbinMods = new MappedModifiers();
golbinMods.addArrayModifiers(this.randomEnemyModifiers);
let option = rollInteger(0, raidManager.randomModifiersBeingSelected.length-1);
const modifiers = golbinRaiderModifierPriority[raidManager.isSelectingPositiveModifier ? 'positive' : 'negative'];
const priorities = [...raidManager.randomModifiersBeingSelected].map(mod => modifiers.findIndex(m => m.key == mod.key));
const values = [...raidManager.randomModifiersBeingSelected].map(mod => {
let modData = modifierData[mod.key]
let v = 0;
if(raidManager.isSelectingPositiveModifier) {
v = (modData.isNegative ? golbinMods.standardModifiers.get(mod.key) : playerMods.standardModifiers.get(mod.key)) || 0
} else {
v = (modData.isNegative ? playerMods.standardModifiers.get(mod.key) : golbinMods.standardModifiers.get(mod.key)) || 0
}
if(modData.modifyValue)
v = modData.modifyValue(v);
return v;
})
const underSoftCap = priorities.map((idx, i) => values[i] < modifiers[idx].soft || modifiers[idx].soft == 0);
const underHardCap = priorities.map((idx, i) => values[i] < modifiers[idx].hard || modifiers[idx].hard == 0);
for(let i=0; i<priorities.length; i++) {
if(underHardCap[i]) {
if(underHardCap[option]) {
if(underSoftCap[i]) {
if(underSoftCap[option]) {
if(priorities[i] < priorities[option])
option = i;
} else {
option = i;
}
}
} else {
option = i;
}
}
}
raidManager.selectRandomModifier(option);
}
raidManager.fireRandomModifierSelection = _fireRandomModifierSelection.bind(raidManager);
let __fireCategorySelectModal = raidManager.fireCategorySelectModal.bind(raidManager);
function _fireCategorySelectModal() {
//__fireCategorySelectModal(...arguments);
let result, step;
try {
({ result, step } = evaluateSteps(golbinRaiderSelectionSteps));
} catch (e) {
console.log(e);
}
if(result == 'abort') {
restartCombat();
return;
}
if(result == 'pause') {
raidManager.pause();
return;
}
if(result == 'passive') {
let currentRunes = availableRunes().map(id => [id, golbinItemCount(id)]);
let currentItems = raidManager.player.equipment.slotArray.map(slot => {
return { id: slot.item.id, qty: slot.quantity, isAlt: raidManager.player.altAttacks[slot.type].length > 0, altAttacks: raidManager.player.altAttacks[slot.type].map(alt => alt.id) };
});
let currentFood = {
id: (items[raidManager.player.food.slots[raidManager.player.food.selectedSlot].item.id] || {id: -1}).id,
qty: raidManager.player.food.slots[raidManager.player.food.selectedSlot].quantity || 0
}
raidManager.rerollPassiveCallback();
addChoice('passive', 0, [{ itemID: raidManager.player.equipment.slotArray[CONSTANTS.equipmentSlot.Passive].item.id, qty: 1 }], currentItems, currentRunes, currentFood);
return;
}
if(result !== undefined && result != false) {
raidManager.showEquipmentSelectionModal(result);
return;
}
}
raidManager.fireCategorySelectModal = _fireCategorySelectModal.bind(raidManager);
let __fireItemSelectModal = raidManager.fireItemSelectModal.bind(raidManager);
function _fireItemSelectModal() {
//__fireItemSelectModal(...arguments);
}
raidManager.fireItemSelectModal = _fireItemSelectModal.bind(raidManager);
let __setEquipmentSelection = raidManager.setEquipmentSelection.bind(raidManager);
function _setEquipmentSelection(category) {
__setEquipmentSelection(...arguments);
let options = [...raidManager.itemsBeingSelected];
let availRunes = availableRunes();
let currentRunes = availableRunes().map(id => [id, golbinItemCount(id)]);
let currentItems = raidManager.player.equipment.slotArray.map(slot => {
if(slot.occupiedBy !== 'None')
return { id: -1, qty: 0, isAlt: false, altAttacks: [] }
return { id: slot.item.id, qty: slot.quantity, isAlt: raidManager.player.altAttacks[slot.type].length > 0, altAttacks: raidManager.player.altAttacks[slot.type].map(alt => alt.id) };
});
let currentFood = {
id: (items[raidManager.player.food.slots[raidManager.player.food.selectedSlot].item.id] || {id: -1}).id,
qty: raidManager.player.food.slots[raidManager.player.food.selectedSlot].quantity || 0
}
try {
if(category == 'runes') {
let option = options.findIndex(o => o.itemID == -1);
for(let i = 0; i<availRunes.length; i++) {
let rune = availRunes[i];
if(hasOceanSong && !haveRunes([[rune, 1]]) && options.findIndex(item => item.itemID == rune) != -1)
option = options.findIndex(item => item.itemID == rune)
}
options[options.findIndex(o => o.itemID == -1)].itemID = CONSTANTS.item.Rune_Essence;
if(options[option].itemID != CONSTANTS.item.Rune_Essence) {
raidManager.addRunesCallback(options[option].itemID, options[option].qty)
} else {
raidManager.addExistingRunesCallback(options[option].qty)
}
addChoice(category, option, options, currentItems, currentRunes, currentFood);
} else if (category == 'food') {
options = options.map(option => {
option.healing = items[option.itemID].healsFor * option.qty;
if(option.itemID == raidManager.player.food.slots[raidManager.player.food.selectedSlot].item.id)
option.healing += raidManager.player.food.slots[raidManager.player.food.selectedSlot].item.healsFor * raidManager.player.food.slots[raidManager.player.food.selectedSlot].quantity;
return option;
});
let option = -1;
let currentHealing = raidManager.player.food.slots[raidManager.player.food.selectedSlot].item.healsFor * raidManager.player.food.slots[raidManager.player.food.selectedSlot].quantity;
options.forEach((o, i) => {
if(o.healing > currentHealing && (options[option] === undefined || o.healing > options[option].healing))
option = i;
});
if(option > -1) {
raidManager.addFoodCallback(options[option].itemID, options[option].qty);
} else {
raidManager.selectNothingCallback();
}
addChoice(category, option, options, currentItems, currentRunes, currentFood);
} else {
options = options.map(option => {
option.slots = items[option.itemID].validSlots.concat(items[option.itemID].occupiesSlots).filter(slot => slot != CONSTANTS.equipmentSlot[CONSTANTS.equipmentSlot.Passive]);
option.weight = itemWeight(option.itemID, option.isAlt, items[option.itemID].validSlots[0]);
option.diff = option.weight - option.slots.map(slot => itemWeight(raidManager.player.equipment.slotArray[CONSTANTS.equipmentSlot[slot]].item.id, raidManager.player.altAttacks[slot].length > 0, slot)).reduce((a,b) => a + b, 0);
option.existingPriority = golbinRaiderPriorities[items[option.itemID].validSlots[0]].includes(raidManager.player.equipment.slotArray[CONSTANTS.equipmentSlot[items[option.itemID].validSlots[0]]].item.id) ? golbinRaiderPriorities[items[option.itemID].validSlots[0]].indexOf(raidManager.player.equipment.slotArray[CONSTANTS.equipmentSlot[items[option.itemID].validSlots[0]]].item.id) : -1
option.priority = golbinRaiderPriorities[items[option.itemID].validSlots[0]].includes(option.itemID) ? golbinRaiderPriorities[items[option.itemID].validSlots[0]].indexOf(option.itemID) : -1;
return option;
});
let option = -1;
options.forEach((o, i) => {
let priority = o.priority;
let currentPriority = option > -1 ? options[option].priority : -1;
let existingPriority = o.existingPriority;
let diff = o.diff;
let currentDiff = option > -1 ? options[option].diff : 0;
if(priority != -1 && (existingPriority == -1 || priority < existingPriority) && (currentPriority == -1 || priority < currentPriority))
option = i;
if(priority == existingPriority && priority == currentPriority && diff > currentDiff)
option = i;
});
if(option > -1) {
raidManager.equipItemCallback(options[option].itemID, options[option].qty, options[option].isAlt);
if(options[option].isAlt)
options[option].altAttacks = raidManager.player.altAttacks[items[options[option].itemID].validSlots[0]].map(alt => alt.id);
} else {
raidManager.selectNothingCallback();
}
addChoice(category, option, options, currentItems, currentRunes, currentFood);
}
} catch (e) {
console.log(e);
}
options = null;
currentRunes = null;
}
raidManager.setEquipmentSelection = _setEquipmentSelection.bind(raidManager);
console.log('Melvor Golbin Raider Loaded');
}
if(window.loadCombat) {
let __loadCombat = window.loadCombat;
window.loadCombat = function() {
__loadCombat();
injectScript(script);
}
} else if (typeof unsafeWindow !== 'undefined' && unsafeWindow.loadCombat) {
let __loadCombat = unsafeWindow.loadCombat;
unsafeWindow.loadCombat = function() {
__loadCombat();
injectScript(script);
}
}
})();