AnimalLoreTool.js
Note: After saving, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
/* globals stratics, mw, $, jQuery */
(function() {
"use strict";
let i,
elementCounter = 200,
tabcounter = 20;
const br = '<br>',
input = 'input',
label = 'label',
legend = 'legend',
loc = location.href.split('#')[0].toString(),
logs = [],
makehtml = stratics.f.util.makehtml,
nbsp = ' ',
number = 'number',
span = 'span',
info = [];
stratics.data.abilityArray = ['Entangle', 'Anemia', 'Angry Fire', 'Arcane Pyromancy', 'Armor Ignore', 'Armor Pierce', 'Aura of Energy', 'Aura of Nausea', 'Bashing', 'Battle Defense', 'Bladeweave', 'Bleed', 'Block', 'Blood Disease', 'Bushido', 'Chivalry', 'Cold Wind', 'Concussion Blow', 'Conductive Blast', 'Crimson Meteor', 'Crushing Blow', 'Daemonic Targeting', 'Defense Mastery', 'Disarm', 'Dismount', 'Double Shot', 'Double Strike', 'Dragon Breath', 'Dual Wield', 'Essence of Disease', 'Essence of Earth', 'Explosive Goo', 'Fan Dancer Fire', 'Feint', 'Firestorm', 'Flight and Fury', 'Flurry Force', 'Focus', 'Force Arrow', 'Force of Nature', 'Frenzied Whirlwind', 'Grasping Claw', 'Healing', 'Howl of Cacophony', 'Immortal Death', 'Infectious Strike', 'Inferno', 'Infused Throw', 'Lethal Arrow', 'Life Leech', 'Lightning Arrow', 'Lightning Force', 'Magery', 'Magery Mastery', 'Mana Drain', 'Mana Leech', 'Mortal Strike', 'Moving Shot', 'Mystic Arc', 'Mysticism', 'Mysticism Mastery', 'Necromage', 'Necromancy', 'Nerve Strike', 'Ninjitsu', 'Paralyze', 'Paralyzing Blow', 'Petrifying Gaze', 'Piercing', 'Poison Breath', 'Poisoning', 'Psychic Attack', 'Rage', 'Raging Breath', 'Repel', 'Riding Swipe', 'Rune Corruption', 'Searing Wounds', 'Serpent Arrow', 'Shadow Strike', 'Slashing', 'Spell Chain', 'Spellweaving', 'Stamina Leech', 'Steal Life', 'Sticky Skin', 'Stone Statue', 'Stygian Fireball', 'Tail Swipe', 'Talon Strike', 'True Fear', 'Venomous Bite', 'Vicious Bite', 'Wave of Hopelessness', 'Webbing', 'Whirlwind Attack', 'Wrestle Mastery', 'Falling Walls', 'Mana Corruption', 'Condemnation', 'Creeping Death', 'Eat Summons', 'Fiendish Calling', 'Fool’s Gold', 'Howl of the Lich', 'Mark of One', 'Meteors', 'Mimic', 'Strangle', 'Teleport Self', 'Teleport Taunt', 'Unholy Touch', 'Vengeful Curse', 'Bone Breaker'].sort();
stratics.data.abilityArray.push('None');
stratics.data.foodArray = ['Blackrock Stew', 'Crops', 'Eggs', 'Fish', 'Fruit & Vegetables', 'Gold', 'Grain', 'Hay', 'Leather', 'Meat', 'Metal'].sort();
stratics.data.foodArray.push('None');
stratics.data.packinstinctArray = ['Arachnid', 'Bear', 'Bull', 'Canine', 'Daemon', 'Equine', 'Feline', 'Ostard', 'Raptor'].sort();
stratics.data.packinstinctArray.push('None');
$('body').data('lorefields', []);
$('body').data('loreselects', []);
$('body').data('lorechecks', []);
/* Script-specific localization */
const lstrings = [];
const l = (str, id) => {
lstrings.push([id, str]);
return stratics.f.util.l(str);
};
const tabsUpdate = () => {
$('#tabControlAL li').each(function() {
let a = $(this).find('a');
a.text(stratics.f.util.l(a.data('value')));
});
};
const lupdate = () => {
for (let x of lstrings) {
let $ele = $(`#${x[0]}`);
if ($ele.is('input[type=button]')) {
$ele.prop('value', stratics.f.util.l(x[1]));
} else {
$ele.text(stratics.f.util.l(x[1]));
}
}
tabsUpdate();
if ($('#toolmenuLangSelect').val() !== stratics.f.util.getLang) {
$('#toolmenuLangSelect').val(stratics.f.util.getLang);
}
};
const findInField = (fieldI, dataI, listarray = stratics.f.util.getCurrData(dataI, stratics.data.lores)) => {
let addSecondaries = function (x, extras) {
if (listarray.indexOf(x) !== -1) {
listarray = listarray.concat(extras);
}
};
// Some special abilities imply others. This adds those implied abilities.
addSecondaries('Bashing', ['Disarm','Paralyze','Heightened Senses','Shield Bash']);
addSecondaries('Battle Defense', ['Mortal Strike','Concussion','Disarm','Toughness','Stagger']);
addSecondaries('Magery Mastery', ['Magery']);
addSecondaries('Necromage', ['Necromancy','Magery']);
addSecondaries('Slashing', ['Nerve Strike','Armor Ignore','Disarm','Focused Eye','Onslaught']);
addSecondaries('Wrestle Mastery', ['Disarm','Paralyze','Knockout','Rampage']);
$(`#fieldset${fieldI} .selectable li`).each(function() {
let x = $(this).attr('data-value');
for (let i in listarray) {
if (listarray[i] === x) {
$(this).addClass('ui-selected');
}
}
});
};
/* Logs a message to the debug console. */
/* 1 for status, 2 for text dump. */
stratics.f.util.log = (i, msg) => {
const $console = $('#debugConsole' + i);
if (i === 1) {
logs.push(msg);
}
if ($('#debugConsole1').length > 0) {
if (i === 1) {
for (let logmsg of logs) {
$console.html(`${$console.html()}${stratics.f.util.getTime()}: ${logmsg}${br}`);
}
} else if (i === 2) {
$console.append(`${msg}\n`);
}
logs.length = 0;
}
};
stratics.f.util.log(1, 'Starting script.');
/* Returns the maximum value from an array. */
stratics.f.util.getMax = (arr) => Math.max(...arr);
/* Returns the minimum value from an array, ignoring null values. */
stratics.f.util.getMin = (arr) => Math.min(...arr);
/* Event handler for markup AJAX request */
$('body').on('markupArrived', () => {
const log = stratics.f.util.log;
log(1, 'Markup has been received.');
log(2, stratics.wiki.markup);
log(1, 'Cleaning old info from markup.');
stratics.wiki.markup3 = stratics.wiki.markup.replace(/\n\s*\|/g, '\n|')
.replace(/\|\s?(specialAttack|magicLevel|instinct_Arachnid|instinct_Bear|instinct_Bull|instinct_Canine|instinct_Daemon|instinct_Equine|instinct_Feline|instinct_Ostard|instinct_Raptor|eats_BlackrockStew|eats_None|eats_none|readings|eats_Gold|instinct_None|bardingDifficulty|baseDamage|controlslots|damageCold|damageEnergy|damageFire|damagePhysical|damagePoison|eats_Crops|eats_Eggs|eats_Fish|eats_FruitVeg|eats_Grain|eats_Hay|eats_Leather|eats_Meat|eats_Metal|eats_Special|max_anatomy|max_bushido|max_chivalry|maxcontrolslots|max_detectingHidden|max_dex|max_discordance|max_eval|max_focus|max_healing|max_hiding|max_hits|max_hpr|max_int|max_magery|max_mana|max_manaRegen|max_med|max_mysticism|max_necro|max_ninjitsu|max_parrying|max_poison|max_resist|max_resistCold|max_resistEnergy|max_resistFire|max_resistPhysical|max_resistPoison|max_spellweaving|max_spiritSpeak|max_stam|max_stamRegen|max_str|max_tactics|max_wrestling|min_anatomy|min_bushido|min_chivalry|min_detectingHidden|min_dex|min_discordance|min_eval|min_focus|min_healing|min_hiding|min_hits|min_hpr|min_int|min_magery|min_mana|min_manaRegen|min_med|min_mysticism|min_necro|min_ninjitsu|min_parrying|min_poison|min_resist|min_resistCold|min_resistEnergy|min_resistFire|min_resistPhysical|min_resistPoison|min_spellweaving|min_spiritSpeak|min_stam|min_stamRegen|min_str|min_tactics|min_wrestling|packInstinct|packText).+\n/g, '')
.replace(/\|\s?bardable =(.*)\n/g, function (a, b) {
stratics.wiki.bardable = (b.trim() === 'true');
return '';
})
.replace(/<!-- Everything below this point is generated from Animal Lore readings. Do not edit. -->\n/g, '')
.replace(/\n+(\n\||<!--)/g, '$1');
log(1, 'Markup has been cleaned.');
log(2, stratics.wiki.markup3);
$('body').trigger('markupReady');
});
/* Event handler for when form is submitted. */
$('body').on('formPrepForNewData', () => {
const log = stratics.f.util.log;
log(1, 'Retriving stored data from the markup.');
stratics.wiki.markup3 = stratics.wiki.markup3.replace(/(?:<!--storedLores = (.+)-->)/gm, (match, storedLores) => {
stratics.data.lores = JSON.parse(storedLores);
return '';
}).trim().replace(/}}$/,'').trim();
if (!stratics.data.lores) stratics.data.lores = [];
log(2, stratics.wiki.markup3);
});
/* Updates the state of the barding lore value and bardable inputs. */
stratics.f.util.updateBardState = () => {
let $bardCheck = $('#loreToolBardable'),
$bardTextbox = $(`#${$('body').data('lorefields')[6]}`),
setState = function(state, background) {
$bardCheck[0].indeterminate = false;
$bardCheck.val('checked', state);
$bardTextbox[0].disabled = state;
$bardTextbox.css('background', background);
};
if (typeof(stratics.wiki.bardable) === 'undefined') {
$bardCheck[0].indeterminate = true;
setState(false, '#101010');
} else if (!stratics.wiki.bardable) { // bardable = false
setState(true, 'grey');
} else { // bardable = true
setState(false, '#101010');
}
};
/* Event handler for when form has loaded stored data. */
$('body').on('populateForm', function() {
let loreCheck = setInterval(function() { // Wait for stratics.data.lores to be defined.
if (!!stratics.data.lores) {
clearInterval(loreCheck);
// 15-19: Damages (Physical, Fire, Cold, Poison, Energy);
// 20-21: Base Damage (Min/Max)
// 43-44: Pet Control Slots (Min/Max)
for (let i of [15, 16, 17, 18, 19, 20, 21, 43, 44]) {
let values = stratics.f.util.getCurrData(i, stratics.data.lores);
if (values.length > 0) {
$(`#${$('body').data('lorefields')[i]}`).val(values.pop());
}
}
// Set stored bardable value
stratics.f.util.updateBardState();
// Set stored Foods values
findInField(8, 45);
// Set stored Pack Instincts values
findInField(9, 46);
// Set stored Special abilities values
findInField(15, 47);
}
}, 10);
});
/* Event handler for when form has loaded stored data. */
$('body').on('markupReady', function() {
let loadedCheck = setInterval(function() { // Once statics.wiki.markup3 is defined, go get stratics.data.lores defined.
if (!!stratics.wiki.markup3) {
clearInterval(loadedCheck);
$('body').trigger('formPrepForNewData');
$('body').trigger('populateForm');
$('body').trigger('formReady');
}
}, 10);
$('#toolmenuSubmit').button('enable');
});
stratics.f.util.parseLoreData = function (text) {
let reading = [];
if (!stratics.pincos) stratics.pincos = {};
let lores = stratics.pincos.lores = {
hits: /hits:\s\d+\/([\?\d]+)/,
stam: /stamina:\s\d+\/([\?\d]+)/,
mana: /mana:\s\d+\/([\?\d]+)/,
str: /strength:\s([\d\?]+)/,
dex: /dexterity:\s([\d\?]+)/,
int: /intelligence:\s([\d\?]+)/,
HPRegen: /hit\spoint\sregeneration:\s([\d\-]+)/,
stamRegen: /stamina\sregeneration:\s([\d\-]+)/,
manaRegen: /mana\sregeneration:\s([\d\-]+)/,
barding: /barding\sdifficulty:\s([\d\.\?]+)/,
petSlotMin: /pet\sslots:\s(\d+)/,
petSlotMax: /pet\sslots:\s\d+\s+=>\s(\d+)/,
tameReq: /taming\srequired:\s([\d\.]+)/,
anatomy: /anatomy:\s([\d\-\.]+)/,
bushido: /bushido:\s([\d\-\.]+)/,
chivalry: /chivalry:\s([\d\-\.]+)/,
detect: /detecting hidden:\s([\d\-\.]+)/,
discord: /discordance:\s([\d\-\.]+)/,
evalInt: /evaluating intelligence:\s([\d\-\.]+)/,
focus: /focus:\s([\d\-\.]+)/,
healing: /healing:\s([\d\-\.]+)/,
hiding: /hiding:\s([\d\-\.]+)/,
magery: /magery:\s([\d\-\.]+)/,
magicResist: /resisting spells:\s([\d\-\.]+)/,
meditation: /meditation:\s([\d\-\.]+)/,
mysticism: /mysticism:\s([\d\-\.]+)/,
necromancy: /necromancy:\s([\d\-\.]+)/,
ninjitsu: /ninjitsu:\s([\d\-\.]+)/,
parrying: /parrying:\s([\d\-\.]+)/,
poisoning: /poisoning:\s([\d\-\.]+)/,
spellweaving: /spellweaving:\s([\d\-\.]+)/,
tactics: /tactics:\s([\d\-\.]+)/,
wrestling: /wrestling:\s([\d\-\.]+)/,
resistPhys: /physical:\s([\-\d]+)/,
resistFire: /fire:\s([\-\d]+)/,
resistCold: /cold:\s([\-\d]+)/,
resistPois: /poison:\s([\-\d]+)/,
resistEner: /energy:\s([\-\d]+)/,
damagePhys: /physical:\s([\-\d]+)/,
damageFire: /fire:\s([\-\d]+)/,
damageCold: /cold:\s([\-\d]+)/,
damagePois: /poison:\s([\-\d]+)/,
damageEner: /energy:\s([\-\d]+)/,
baseDamageMin: /base damage:\s(\d+)/,
baseDamageMax: /base damage:\s\d+\-(\d+)/,
spiritspeak: /spirit speak:\s([\d\-\.]+)/,
foods: /food:\s([a-z\s,]+)$/im,
packInstincts: /pack instinct:\s([a-z\s,]+)$/im,
};
let getVal = stratics.pincos.getVal = function (x, text) {
let val,
damages = text.match(/DAMAGE\-+([\-a-z\:\s\d\%\(\)]|\n)+/)[0],
resists = text.match(/RESISTANCES\-+([\-a-z\:\s\d\%\(\)]|\n)+/)[0];
if (null === lores[x]) {
return null;
}
if (/^resist/.test(x)) {
val = resists.match(lores[x])[1];
} else if (/damage/.test(x)) {
val = damages.match(lores[x])[1];
} else {
val = text.match(lores[x])[1];
}
if (val === '---') {
val = 0;
}
return val;
};
for (let x of ['hits','stam','mana','str','dex','int','barding','HPRegen','stamRegen','manaRegen','resistPhys','resistFire','resistCold','resistPois','resistEner','damagePhys','damageFire','damageCold','damagePois','damageEner','baseDamageMin','baseDamageMax','wrestling','tactics','magicResist','anatomy','healing','poisoning','detect','hiding','parrying','magery','evalInt','meditation','necromancy','spiritspeak','mysticism','focus','spellweaving','discord','bushido','ninjitsu','chivalry','petSlotMin','petSlotMax']) {
reading.push(getVal(x, text)+"");
}
for (let x of ['foods','packInstincts']) {
reading.push(getVal(x, text).split(/,\s?/g).join('|'));
}
let specials = text.match(/SPECIAL ATTACKS\-+\n([a-z\s\n])+(?=\-)/i)[0].split('\n');
specials.shift();
specials.pop();
reading.push(specials.join('|'));
return reading.join(',');
};
stratics.f.util.parseLoreArray = function (str) {
if (/\-/.test(str)) {
str = str.split('-');
str = [...str[0].split(','), ...str[1].split(',')];
} else {
str = [...str.split(',')];
}
for (let x = 0; x < 3; x++) {
if (/\//.test(str[x])) {
str[x] = str[x].split('/')[1];
}
}
for (let x = 0; x < 45; x++) {
str[x] = parseFloat(str[x], 10);
}
for (let x of [45, 46, 47]) {
if (Array.isArray(str[x])) {
str[x] = str[x].split('|');
}
}
// Set bardable state
$('#loreToolBardable').attr('checked', !Number.isNaN(parseFloat(str[6])));
stratics.f.util.updateBardState();
// Set stored Special abilities values
findInField(15, 47, str[47]);
return str;
};
$('body').on('formManualAddData', () => {
var str = $('#inputManualData').val();
str = stratics.f.util.parseLoreArray(str);
stratics.data.lores.push(str);
$('#inputManualData').val('');
});
$('body').on('formReadNewData', () => {
let data = stratics.data.lores || [],
thisData,
foods = [],
instincts = [],
selects = $('body').data('loreselects');
const log = stratics.f.util.log;
log(1, 'Collecting data for new record.');
thisData = data[data.length] = [];
for (let j of $('body').data('lorefields')) {
thisData.push(parseFloat($(`#${j}`).val(), 10));
if (isNaN(thisData[thisData.length - 1])) {
thisData[thisData.length - 1] = null; // Empty string would convert the field to a string; we specifically want it to stay an int.
}
}
$(`#${selects[0]} .ui-selected`).each(function() {
foods.push($(this).attr('data-value'));
});
$(`#${selects[1]} .ui-selected`).each(function() {
instincts.push($(this).attr('data-value'));
});
let specials = [];
$('#fieldset15 .ui-selected').each(function () {
specials.push($(this).attr('data-value'));
});
thisData.push(foods, instincts, specials.join('|'));
log(1, 'Foods:' + JSON.stringify(foods));
log(1, 'Instincts:' + JSON.stringify(instincts));
});
stratics.f.util.getCurrData = (i, data, lastOnly = 0) => {
const flatten = list => list.reduce(
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
);
let retVal;
if (lastOnly) {
retVal = data[data.length - 1][i];
} else {
retVal = flatten(data.map(a => {
a = a[i];
a = void 0 === a ? "" : a;
return "string" === typeof a ? a.split("|") : a;
}));
}
if (null !== retVal && Array.isArray(retVal)) {
retVal = retVal.filter(n => n !== undefined && n !== "").filter(x => x !== null);
}
/* This last bit seems overly complex, but avoids the situation where the correct value is submitted, then an incorrect value is
submitted *afterwards*, then the correct value is again submitted. (ie, [100,45,100], which would turn into [100,45].) This
corrects for that, putting it into submission order, even when the most recent submitted value was also previously submitted (ie. [45,100]). */
let lastVal = retVal[retVal.length - 1];
retVal = [...new Set(retVal)];
let lastIndex = retVal.indexOf(lastVal);
if (lastIndex !== retVal.length - 1) {
retVal.splice(lastIndex, 1);
retVal.push(lastVal);
}
return retVal;
};
stratics.f.util.buildMinMax = (names, fieldnum, data) => {
stratics.f.util.log(1, `Building min/max: ${name}.`);
const checkNan = (arg) => arg = isNaN(arg) ? '' : arg,
util = stratics.f.util;
let rtnText = "";
for (var name of names) {
let min = checkNan(util.getMin(util.getCurrData(fieldnum, data))),
max = checkNan(util.getMax(util.getCurrData(fieldnum++, data)));
if (min === Infinity) min = '';
if (max === -Infinity) max = '';
info[`min_${name}`] = (min + '').replace('null', '');
info[`max_${name}`] = (max + '').replace('null', '');
rtnText += `| min_${name} = ${min}\n| max_${name} = ${max}\n`;
}
return rtnText;
};
stratics.f.util.buildParam = (name, j, data) => {
const log = stratics.f.util.log;
log(1, `Building parameter: ${name}.`);
let info = stratics.f.util.getCurrData(j, data);
if (info.length === 0) {
log(1, `Field ${name} has no useable data stored.`);
}
log(1, `Field ${name}: ${JSON.stringify(info)}`);
info[name] = 0 < info.length ? info.pop() : '';
return `| ${name} = ${(info[name] + '').replace('null','')}\n`;
};
$('body').on('formBuildNewValues', () => {
let data = stratics.data.lores || [];
const log = stratics.f.util.log,
buildMinMax = stratics.f.util.buildMinMax,
buildParam = stratics.f.util.buildParam,
getCurrData = stratics.f.util.getCurrData;
log(1, 'Re-adding new+stored data to the markup.');
stratics.wiki.markup3 = `${stratics.wiki.markup3}\n<!-- Everything below this point is generated from Animal Lore readings. Do not edit. -->\n<!--storedLores = ${JSON.stringify(data)}-->\n}}`;
log(2, stratics.wiki.markup3);
stratics.data.lores = data;
log(1, 'Forming new parameter values.');
let fields = '';
fields += buildMinMax(['hits', 'stam', 'mana', 'str', 'dex', 'int'], 0, data); // Data array values 0-5
let bardingRange = getCurrData(6, stratics.data.lores); // Data array value 6
if (bardingRange.length === 0) { // bardable, but no lores w/barding values have been submitted
fields += `| bardingDifficulty = \n`;
} else {
let bardingMin = stratics.f.util.getMin(bardingRange),
bardingMax = stratics.f.util.getMax(bardingRange);
if (bardingMin === bardingMax) {
fields += `| bardingDifficulty = ${bardingMin}\n`;
} else {
fields += `| bardingDifficulty = ${bardingMin} - ${bardingMax}\n`;
}
}
fields += buildMinMax(['hpr', 'stamRegen', 'manaRegen'], 7, data); // Data array values 7-9
fields += buildMinMax(['resistPhysical', 'resistFire', 'resistCold', 'resistPoison', 'resistEnergy'], 10, data); // Data array values 10-14
fields += buildParam('damagePhysical', 15, data); // Data array value 15
fields += buildParam('damageFire', 16, data); // Data array value 16
fields += buildParam('damageCold', 17, data); // Data array value 17
fields += buildParam('damagePoison', 18, data); // Data array value 18
fields += buildParam('damageEnergy', 19, data); // Data array value 19
let baseD1 = getCurrData(20, data),
baseD2 = getCurrData(21, data);
baseD1 = baseD1.pop();
baseD2 = baseD2.pop();
if (isNaN(baseD1)) { baseD1 = '?'; }
if (isNaN(baseD2)) { baseD2 = '?'; }
fields += `| baseDamage = ${(baseD1 + '').replace('null','')}-${(baseD2 + '').replace('null','')}\n`; // Data array values 20-21
fields += buildMinMax(['wrestling', 'tactics', 'resist', 'anatomy', 'healing', 'poison', 'detectingHidden', 'hiding', 'parrying'], 22, data); // Data array values 22-30
fields += buildMinMax(['magery', 'eval', 'med', 'necro', 'spiritSpeak', 'mysticism', 'focus', 'spellweaving', 'discordance'], 31, data); // Data array values 31-39
fields += buildMinMax(['bushido', 'ninjitsu', 'chivalry'], 40, data); // Data array values 40-42
let foodlist = getCurrData(45, data, 1); // Data array value 45 = Foods
log(1, 'Foods array:' + JSON.stringify(foodlist));
fields += `| eats_BlackrockStew = ${-1 !== foodlist.indexOf('Blackrock Stew') ? 'Yes' : 'No'}\n`;
for (var food of ['Crops', 'Eggs', 'Fish', 'Fruit & Vegetables', 'Gold', 'Grain', 'Hay', 'Leather', 'Meat', 'Metal', 'None']) {
fields += `| eats_${food === 'Fruit & Vegetables' ? 'FruitVeg' : food} = ${-1 !== foodlist.indexOf(food) ? 'Yes' : 'No'}\n`;
}
let packlist = getCurrData(46, data, 1); // Data array value 46 = Pack Instinct
log(1, 'Pack Instinct array:' + JSON.stringify(packlist));
let packText = [];
for (var instinct of stratics.data.packinstinctArray) {
fields += `| instinct_${instinct} = ${-1 !== packlist.indexOf(instinct) ? 'Yes' : 'No'}\n`;
if (-1 !== packlist.indexOf(instinct)) {
packText.push(instinct);
}
}
fields += `| packText = ${packText.join(', ')}\n`;
fields += buildParam('controlslots', 43, data); // Data array value 43 = Pet Slots min
fields += buildParam('maxcontrolslots', 44, data); // Data array value 44 = Pet Slots max
let specials = [];
$('#fieldset15 .ui-selected').each(function () {
specials.push($(this).attr('data-value'));
});
fields += `| specialAttack = ${[...new Set(specials)].join(', ')}\n`;
let checkValid = (str) => {
return parseInt(info[str], 10) > -1;
};
if (checkValid('min_magery') && checkValid('max_magery') && checkValid('min_eval') && checkValid('min_eval')) {
const lookupTitle = str => {
switch (str) {
case 0: // Falls through
case 1: // Falls through
case 2:
return 'None';
case 3:
return 'Neophyte';
case 4:
return 'Novice';
case 5:
return 'Apprentice';
case 6:
return 'Journeyman';
case 7:
return 'Expert';
case 8:
return 'Adept';
case 9:
return 'Master';
case 10:
return 'Grandmaster';
case 11:
return 'Elder';
case 12:
return 'Legendary';
}
};
let min = Math.floor((parseInt(info.min_magery,10) + parseInt(info.min_eval,10)) / 20),
max = Math.floor((parseInt(info.max_magery,10) + parseInt(info.max_eval,10)) / 20);
fields += `| magicLevel = ${lookupTitle(min)}\n`;
if (min !== max) {
fields += `| magicLevel2 = ${lookupTitle(max)}\n`;
}
} else {
fields += `| magicLevel = unknown\n`;
}
fields += `| bardable = ${stratics.wiki.bardable || ''}\n`;
fields += `| readings = ${data.length}\n`;
stratics.wiki.markup3 = stratics.wiki.markup3.replace(/}}$/, fields + '}}');
log(1, 'New parameter values have been formed and stored.');
log(2, stratics.wiki.markup3);
log(1, 'Submitting changed markup.');
stratics.f.wiki.submitMarkup('Adding a new Animal Lore reading.', stratics.wiki.markup3);
});
/* Handle a successful markup submission. */
$('body').on('submitMarkupSuccess', () => {
$('#toolmenuNotification').text('Saved. Reloading the page...');
// stratics.f.util.reloadPage();
});
/* Handle a failed markup submission. */
$('body').on('submitMarkupFailure', () => {
$('#toolmenuNotification').text('An error has occurred submitting the data. Please try again.');
});
/* Image locations are hardcoded, ala http://stratics.com/w/images/a/a6/ALGump1.png */
const gumpImages = ['a/a6', '0/0a', 'd/d6', '3/3e', 'e/e5', '6/6e', '2/2d'];
/* Creates an arbitrary element */
const makeElem = (elem, num, args, label, after) => {
if (typeof args.id === 'undefined') { // Allow for defined element ids
if (typeof args.type === 'undefined') {
args.id = elem + num;
} else {
args.id = args.type + num;
}
}
if (typeof label === 'undefined') {
return makehtml(elem, args);
} else {
if (typeof after === 'undefined') {
return makehtml('label', {}, label + nbsp + makehtml(elem, args));
} else {
return makehtml('label', {}, makehtml(elem, args) + nbsp + label);
}
}
};
/* Creates an arbitrary element */
const makeFieldset = (id, name) => $('<fieldset>', {
id: 'fieldset' + id
})
.append(makehtml(legend, {
style: 'color:#FFF;font-weight:bolder;padding-bottom: 1em;',
id: `fieldsetLegend${id}`
}, name));
/* Creates a dl element */
const makeDL = (str) => makehtml('dl', {
id: `dl${elementCounter}`,
style: "margin-bottom: 1em"
}, str);
/* Creates a dt element */
const makeDT = (str) => makehtml('dt', {
id: `dt${elementCounter}`,
style: "font-weight: 700;margin-left: 2em;color: goldenrod"
}, str);
/* Creates a dd element */
const makeDD = (str) => makehtml('dd', {
id: `dd${elementCounter}`,
style: "margin-left: 4em"
}, str);
/* Creates a span element */
const makeSpan = (str) => makehtml(span, {
id: `span${elementCounter}`
}, str);
/* Creates an input[type=number] element */
const makeNumber = (j, val) => makeElem(input, j, {
type: number,
min: 0,
step: 'any',
style: 'width: 6em;',
value: val
});
/* Create popup dialog, tab structure, and data fieldsets */
const $toolmenu = $(makehtml('div', {
id: 'toolmenu'
})),
$tabsInfo = $('<div>', {
id: 'tabs'
}),
$tabsList = $('<ul id="tabControlAL">'),
makeTab = (i, name) => {
const $div1 = $('<div style="float:left;width: 65%;" id="container' + i + '">').append(makeFieldset(i, l(name, `fieldsetLegend${i}`))),
$div2 = $('<div style="position: absolute; right: 3em; top: 5em;">');
if (i < 11) {
$div2.append(makehtml('img', {
src: `/images/${gumpImages[i - 1]}/ALGump${i}.png`,
style: 'border-radius: 10px;'
}));
}
$(`<div id="ALMenu${i}">`).append([$div1, $div2])
.appendTo($toolmenu);
$(makehtml('li', {}, makehtml('a', {
href: `${loc}#ALMenu${i}`,
id: `tab${i}`
}, name))).appendTo($tabsList);
};
$toolmenu.append([
'<div id="ToolMenuLanguageBox" style="position: absolute; bottom: 2px; left: 1em;">',
'<div id="ToolMenuButtons" style="position: absolute; bottom: 2px; right: 1em;">'
]);
makeTab(1, 'Attributes');
makeTab(2, 'Attributes');
makeTab(3, 'Resistances');
makeTab(4, 'Damage');
makeTab(5, 'Combat Ratings');
makeTab(6, 'Lore & Knowledge');
makeTab(7, 'Lore & Knowledge');
makeTab(15, 'Special Moves & Abilities');
makeTab(11, 'Debug');
makeTab(12, 'Import log files');
makeTab(13, 'Credits');
makeTab(14, 'Manual');
$toolmenu.appendTo('body');
$('#container7').append([
'<br/>',
makeFieldset(8, l('Preferred Foods', 'fieldsetLegend8')),
'<br/>',
makeFieldset(9, l('Pack Instincts', 'fieldsetLegend9')),
'<br/>',
makeFieldset(10, l('Pet Slots', 'fieldsetLegend10'))
]);
$toolmenu.prepend($tabsInfo.append($tabsList));
$('#fieldset15').parent().css('width', '100%');
const addNumberInput = (field, name, skipBreak) => {
let $div = $('<div style="display: block;margin-top: 4px;">'),
num = elementCounter;
$(`#fieldset${field}`).append($div.append([
$(makeNumber(num, "")).prop("name", name + 1),
stratics.f.util.makehtml(label, {
id: `label${elementCounter}`,
for: `number${num}`,
style: "padding-left:0.5em;"
}, l(name, `label${elementCounter++}`)),
skipBreak ? '' : br
]));
return `number${num}`;
},
addCheckboxInput = (field, name, text, checked) => {
let $div = $('<div style="display: flex;margin-top: 4px;">'),
id = `loreTool${name}`;
let checkbox = $(makehtml(input, {
type: 'checkbox',
id: id
}));
if (checked) {
checkbox.prop('checked', true);
}
$(`#fieldset${field}`).append($div.append([checkbox,
makehtml(label, {
style: 'color:#FFF; padding-left: 0.4em; padding-right: 1em;',
for: id,
id: `inputLabel${elementCounter}`
}, l(text, `inputLabel${elementCounter++}`)),
]));
return id;
},
addNumberInputs = (i, arr) => {
let ids = [];
for (var item of arr) {
ids.push(addNumberInput(i, item));
}
return ids;
},
addRangeInput = (field, name) => {
let id = addNumberInput(field, name);
$(`#${id}`).after([
" - ",
$(makeNumber(elementCounter, "")).prop("name", name + 2)
]);
return [id, `number${elementCounter++}`];
},
addButtonInput = (field, name, text, disabled) => {
$(`#${field}`).append($(makehtml(input, {
type: 'button',
value: text,
id: `toolmenu${name}`,
})).text(l(text, `toolmenu${name}`))
.button()
.button(disabled ? 'disable' : 'enable'));
},
addSelect = (field, name, options) => {
let id = `editToolMenu${elementCounter}`;
const liArr = [],
$ul = $(`<ul class="selectable" id="${id}">`);
for (i = 0; options.length > i; i++) {
liArr.push(`<li id="option${elementCounter}" style="cursor:pointer;" data-value="${options[i]}">${l(options[i], `select${elementCounter++}`)}</li>`);
}
$ul.append(liArr);
$(`#fieldset${field}`).append(makeSpan(l('Hold CTRL to select multiple items.', `span${++elementCounter}`)),'<br>',$ul);
$ul.selectable();
return id;
},
addTextInput = (field, text, placeholder = '', flex = 1, id = `editToolMenu${elementCounter}`) => {
let $div = $(`<div style="${(flex) ? 'display: flex;' : ''}margin-top: 3px; margin-bottom: 1.4em;">`).append([
makehtml(label, {
for: id,
id: `inputLabel${elementCounter}`,
style: `margin-left:0.5em; min-height: 28px;`,
}, l(text, `inputLabel${elementCounter}`)),
makehtml(input, {
id: id,
placeholder: l(placeholder, `editToolMenu${elementCounter++}`),
style: 'flex: 1 1 auto; margin-left:0.5em; min-height: 28px;',
type: 'text'
})
]);
$(`#fieldset${field}`).append($div);
return id;
};
/* Populate tabs */
let f = $('body').data('lorefields'),
s = $('body').data('loreselects'),
c = $('body').data('lorechecks');
f.push(...addNumberInputs(1, ['HP', 'Stamina', 'Mana', 'Strength', 'Dexterity', 'Intelligence', 'Barding Difficulty']));
c.push(addCheckboxInput(1, 'Bardable', 'This type of mobile cannot be barded.'));
f.push(...addNumberInputs(2, ['Hit Point Regeneration', 'Stamina Regeneration', 'Mana Regeneration']));
f.push(...addNumberInputs(3, ['Physical Resistance', 'Fire Resistance', 'Cold Resistance', 'Poison Resistance', 'Energy Resistance']));
f.push(...addNumberInputs(4, ['Physical Damage', 'Fire Damage', 'Cold Damage', 'Poison Damage', 'Energy Damage']));
f.push(...addRangeInput(4, 'Base Damage'));
f.push(...addNumberInputs(5, ['Wrestling', 'Tactics', 'Resisting Spells', 'Anatomy', 'Healing', 'Poisoning', 'Detecting Hidden', 'Hiding', 'Parrying']));
f.push(...addNumberInputs(6, ['Magery', 'Evaluating Intelligence', 'Meditation', 'Necromancy', 'Spirit Speak', 'Mysticism', 'Focus', 'Spellweaving', 'Discordance']));
f.push(...addNumberInputs(7, ['Bushido', 'Ninjitsu', 'Chivalry']));
f.push(...addRangeInput(10, 'Pet Slots'));
s.push(addSelect(8, 'Preferred Food', stratics.data.foodArray));
s.push(addSelect(9, 'PackInstincts', stratics.data.packinstinctArray));
s.push(addSelect(15, 'SpecialAbilities', stratics.data.abilityArray));
$('#fieldset11').append('<div id="debugConsole1"></div><br/><textarea style="width:825px;height:300px;" wrap="off" id="debugConsole2"></textarea>');
addTextInput(14, 'Data array', '', 1, 'inputManualData');
addButtonInput('fieldset14', 'ManualAdd', 'Add');
addButtonInput('fieldset14', 'ManualRecalc', 'Recalculate Mob');
$('#tab14').parent().hide(); // Manual entry tab (for development use)
$('#fieldset12').append([
makehtml('div', {
id: `div${elementCounter}`,
class: 'dropZone',
}, l('Drag and drop log files here to import their data.', `div${elementCounter++}`)),
$(makeSpan(l('or', `span${++elementCounter}`))).css('margin-left', '15%'),
makehtml('input', {
id: `file${elementCounter++}`,
style: 'flex: 1 1 auto; margin-left:0.5em; min-height: 28px;',
type: 'file',
name: 'files[]',
multiple: 'multiple'
}),
makehtml('output', {
id: `output${elementCounter++}`,
style: 'width:120%;margin:1em 15%;display:block;height:15em;overflow:auto;',
}, makehtml('ul', {}))
]);
addButtonInput('fieldset12', 'SubmitLogs', 'Submit log files', true);
$('#toolmenuSubmitLogs').css({
position: 'absolute',
right: '1em'
});
$('.selectable li').css('margin','.3em');
$('#tab11').parent().hide(); // Debug tab
$('#tab12').parent().hide(); // Import log files tab
$('#fieldset15 .selectable li').css({
float: 'left',
fontSize: '.9em',
height: '3em ',
lineHeight: '3em',
margin: '3px',
padding: '1px',
textAlign: 'center',
verticalAlign: 'middle',
width: '9%'
}).each(function() {
$(this).html(`<span style="display:inline-block;vertical-align:middle;line-height:normal;padding:5px;">${$(this).text()}</span>`);
});
/* Handle files being added via drag&drop or the input file selector. */
$('#fieldset12 div:first').on("dragover", e => {
e.stopPropagation();
e.preventDefault();
e.originalEvent.dataTransfer.dropEffect = 'copy';
});
$('#fieldset12 div:first, #fieldset12 input').on("drop change", e => {
e.stopPropagation();
e.preventDefault();
let util = stratics.f.util;
let fileCnt = 1;
let parseFile = function(e) {
let d = e.target.result.match(/\[(\d\d)\/(\d\d)\/(\d\d)\]\[(\d\d)\:(\d\d)\:(\d\d)\]/),
f = this.file;
stratics.f.util.log(1, `Parsing log file named ${decodeURI(f.name)}`);
let date = (new Date(`20${d[1]}-${d[2]}-${d[3]}T${d[4]}:${d[5]}:${d[6]}`) + "").split(' GMT')[0];
let str = JSON.stringify(util.parseLoreArray(util.parseLoreData(e.target.result)));
let $li = $(`<li><strong><span class="removeFile">X</span>${decodeURI(f.name)}</strong> (${f.size} bytes, created ${date})</li>`);
stratics.f.util.log(2, str);
$li.data('value', str);
$('#fieldset12 output ul').append($li);
if (this.cur === this.max) {
$('#fieldset12 div:first').css('filter', 'hue-rotate(240deg)');
$('#toolmenuSubmitLogs').button('enable');
}
};
let files = e.originalEvent[!e.originalEvent.dataTransfer ? 'target' : 'dataTransfer'].files;
$('#toolmenuSubmit, #toolmenuSubmitLogs').button('disable');
if (!stratics.data.lores) $('body').trigger('formPrepForNewData');
$('#fieldset12 div:first').css('filter', 'hue-rotate(120deg)');
for (let f of files) {
let reader = new FileReader();
reader.cur = fileCnt++;
reader.max = files.length;
reader.file = f;
reader.readAsText(f);
reader.onload = parseFile;
}
});
/* Populate Credits tab */
let translations = [];
for (let k of stratics.f.util.getLangList()) {
if (k[0] != 'en') {
translations.push(makeDL([
makeDT(makeSpan(l('Translation', `span${++elementCounter}`)) + makeSpan(` (${stratics.i18n[k[0]]["{language name}"]})`)),
makeDD(stratics.i18n[k[0]]["{translators}"])
].join('')));
}
}
$('#fieldset13').append(makeDL([
makeDT(l('Programming', `dt${++elementCounter}`)),
makeDD('BrianFreud')
].join('')) + translations.join(''));
/* Create button controls */
$('#ToolMenuButtons').append('<span id="toolmenuNotification">');
addButtonInput('ToolMenuButtons', 'Submit', 'Submit', true);
addButtonInput('ToolMenuButtons', 'Cancel', 'Cancel', false);
/* Create language selector */
$('#ToolMenuLanguageBox').append('<select id="toolmenuLangSelect" style="float: right;">');
let $options = [];
for (let k of stratics.f.util.getLangList()) {
let selected = (k[0] == stratics.i18n.current) ? 'selected' : '';
$options.push(`<option value="${k[0]}" ${selected}>${k[1]}</option>`);
}
$('#toolmenuLangSelect').append($options).selectmenu({
style: 'dropdown'
});
/* Handle a changed language selection */
$('#toolmenuLangSelect').on('change', function() {
stratics.f.util.setLang($(this).val());
});
/* Handle notification of a changed language selection */
$('body').on('languageChanged', function() {
lupdate();
});
/* Handle a click on the cancel button. */
$('#toolmenuCancel').click(function() {
$toolmenu.dialog('close');
});
/* Handle a click on the submit button. */
$('#toolmenuSubmit').click(function() {
$('#toolmenuNotification').text('Thanks! Saving...');
if (!stratics.data.lores) $('body').trigger('formPrepForNewData');
$('body').trigger('formReadNewData');
$('body').trigger('formBuildNewValues');
});
/* Handle a click on the buttons in the manual entry tab. */
$('#toolmenuManualAdd').on('click', function() {
if (!stratics.data.lores) $('body').trigger('formPrepForNewData');
$('body').trigger('formManualAddData');
});
$('#toolmenuManualRecalc').on('click', function() {
$('body').trigger('formBuildNewValues');
});
/* Allow the debug menu to be seen. */
$(document).bind('keydown', function(event) {
if (event.which === 68 && event.altKey) {
$('#tab11').parent().toggle();
}
});
/* Allow the manual entry menu to be seen. */
$(document).bind('keydown', function(event) {
if (event.which === 77 && event.altKey) {
$('#tab14').parent().toggle();
}
});
/* Handle a click on a 'remove file' button */
$('#fieldset12').on('click', '.removeFile', function () {
$(this).parent().parent().remove();
if ($('#fieldset12').find('li').length === 0) {
$('#toolmenuSubmitLogs').button('disable');
$('#fieldset12 div:first').css('filter', 'hue-rotate(0deg)');
}
});
/* Handle a click on the Submit logs button. */
$('#toolmenuSubmitLogs').on('click', function () {
$('#fieldset12 output li').each(function () {
let str = $(this).data('value').replace(/"/g,'').match(/\[(.+)\]/)[1];
str = stratics.f.util.parseLoreArray(str);
stratics.data.lores.push(str);
});
$('body').trigger('formBuildNewValues');
});
/* Handle a click on the Cannot be Barded checkbox. */
$('#loreToolBardable').change(function () {
if(this.checked) {
stratics.wiki.bardable = false;
document.querySelector('#number206').disabled = true;
} else {
stratics.wiki.bardable = true;
document.querySelector('#number206').disabled = false;
}
stratics.f.util.updateBardState();
});
/* TEMPORARY CODE: Allow Pincos tab to be seen */
$(document).bind('keydown', function(event) {
if (event.which === 80 && event.altKey) {
$('#tab12').parent().toggle();
}
});
/* Start fetching the page's markup. ASYNCH */
stratics.f.wiki.getMarkup();
/* Initialize popup dialog */
stratics.f.util.log(1, 'Initializing dialog.');
$toolmenu.tabs()
.dialog({
title: l('Add an Animal Lore reading', 'ui-id-2'),
autoOpen: false,
position: {
my: 'center',
at: 'center-125',
of: window
},
modal: true,
width: 1180,
height: 805,
close: function() {
$('#PropBox')
.dialog('close');
},
open: function() {
if ($('#PropBox')
.length > 0 && !$('#PropBox')
.dialog("isOpen")) {
$('#PropBox')
.dialog('open');
}
}
});
/* Create Trigger link. */
stratics.f.util.log(1, 'Creating trigger link.');
$('<li>')
.append($('<span>')
.append($('<a id="triggerLinkAL">')
.text(l($('#unloreableMob').length ? 'This type of mobile cannot be lored.' : 'Add an Animal Lore reading', 'triggerLinkAL'))
.click(e => {
e.preventDefault();
$('#toolmenu')
.dialog('open');
})))
.insertAfter('#ca-edit');
/* Store tabs' text */
$('#tabControlAL li').each(function() {
let a = $(this).find('a');
a.data('value', a.text());
});
tabsUpdate();
}());