// ==UserScript==
// @name Neverwinter Gateway - Professions Robot
// @description Automatically selects tasks from professions for empty slots
// @namespace blabla
// @include https://gateway.playneverwinter.com
// @include https://gateway.playneverwinter.com/*
// @include https://gatewaysitedown.playneverwinter.com
// @include https://gatewaysitedown.playneverwinter.com/*
// @include http://gateway.playneverwinter.com
// @include http://gateway.playneverwinter.com/*
// @include http://gatewaysitedown.playneverwinter.com
// @include http://gatewaysitedown.playneverwinter.com/*
// @originalAuthor Subjunctive
// @version 0.1.1
// @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
// Make sure it's running on the main page, no frames
if(window.self !== window.top) {
throw "";
}
var NWROBOT_cleanSettings;
(function(){
/* TM has a history of bugs related to setValue not storing in some cases, so use localStorage
* until I can unify the settings code better.
*
* localStorage only handles strings, of course, so wrap everything in a JSON encoding because
* why not?
*/
var use_GM_storage = false;
function storeValue(key, val) {
if (use_GM_storage) {
return GM_setValue(key, val);
}
var valobj = JSON.stringify({val: val});
return localStorage.setItem(key, valobj);
}
function fetchValue(key, def) {
if (use_GM_storage) {
return GM_getValue(key, def);
}
var valobj = JSON.parse(localStorage.getItem(key));
if (valobj == null)
return def || "";
return valobj.val;
}
function cloneObject(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
function LOG() {
var args = Array.prototype.slice.call(arguments, 0);
var now = new Date();
args.unshift([now.getHours(), now.getMinutes(), now.getSeconds()].join(":"));
console.log.apply(console, args);
}
/* Hard reload on JS error. */
var handler = function (e) {
LOG("Error, reloading: ", e.message);
LOG(e);
window.location.reload(true);
}
window.addEventListener('error', handler);
var onerror = function (m, url, lineno) { LOG("Error, reloading: ", m, "@", url, lineno); }
unsafeWindow.onerror = onerror;
// If "loading" take longer than 1 minute, reload page (maybe a javascript error)
var $ = unsafeWindow.$;
var loading_crash = 0;
var loading_crash_time = 20;
var timer = setInterval(function(){
try {
if( $("div.loading-image:visible").length ){
if( loading_crash > loading_crash_time ){
location.reload(true);
}
else{
loading_crash++;
var now = new Date();
var stamp = [now.getHours(), now.getMinutes(), now.getSeconds()].join(":");
var where = location.href;
where = where.slice(where.indexOf(".com/") + 5);
console.log(stamp, "Loading", where, loading_crash + "s" );
}
}
else{
loading_crash = 0;
}
} catch (e) {
// some error running the heartbeat, force a reload in a second.
clearTimeout(timer);
setTimeout(function () { location.reload(true); }, 1000);
}
},1000);
/**
* Add a string of CSS to the main page
*
* @param {String} cssString The CSS to add to the main page
*/
function AddCss(cssString) {
var head = document.getElementsByTagName('head')[0];
if(!head)
return;
var newCss = document.createElement('style');
newCss.type = "text/css";
newCss.innerHTML = cssString;
head.appendChild(newCss);
}
function countLeadingSpaces(str) {
return str.match(/^(\s*)/)[1].length;
}
var image_prefs = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQC" +
"AMAAAAoLQ9TAAAAllBMVEUAGQASEhIfHx8fJy8pKSk2NjZBQUFJR0ZQUE9RUVFSUlJNX3No" +
"aGhsaWdramlycG1meY98fHx+fn5wgpV0iqKKh4R4jaR9jJx8kad9kad/mbONmaWEnrmEnrq" +
"koZy3t7fIx8bKyMHT0c3S0dDU09DV1NPP1t3W1dXY2Njb2tfe29bf3tzj4uHr6+js6+r39/" +
"f5+PgAAABrL3yvAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTA" +
"QCanBgAAAAHdElNRQfWBRoFKh31UQ8DAAAAgUlEQVQY022OxxLCMAwFRSc4BEIPJZQQ08v+" +
"/8+RsTExDDpIe3ijfSJ/hx9g62Dt4GaAI+8YT0t27+BxxvvE/no5pYT10lGFrE34Ja40W3g" +
"1oMGmW7YZ6hnCYexKTPVkXivuvWe1Cz1aKqPNI3N0slI2TNYZiARJX30qERc7wBPKC4WRDz" +
"WdWHfmAAAAAElFTkSuQmCC";
var image_close = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQC" +
"AQAAAC1+jfqAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfW" +
"BRkTNhxuPxLkAAAAHXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBUaGUgR0lNUO9kJW4AAAE" +
"KSURBVCjPhdGxSgNBFAXQMzpgYWwsLEQUDBJBQgqFIChZEPR7/DA/QCGQTgQtJE1ENoWohY" +
"UgbGKQyFjErNv52nObe19wqGWg7z0l5YVgVdOu+wUt507tqIVQ4Zodp861ooELe15M5KFI6" +
"Zfr9u25MIj6Jl4cmSIPBWrq2o5cufO4aOJDYSozNTa2pK4t03PtwUdMKRRykAmW0dTRcyNX" +
"pBQpI8GJDTR050zkNzK0bMMZLvUNZ8yCfy6Wvbc1NVyi4dloXjqWvds6uvp41pFmpVOKJWd" +
"6bgwxkmTMIotWKpwrfBkZl7uMonUHf5wSlV2+fUZrjnXdzrmyy7djD8GWTW9e51z557o1Tz" +
"85FH/WkOkaHQAAAABJRU5ErkJggg==";
// Setup global closure variables
var fouxConsole = {log:function(){},info:function(){},error:function(){},warn:function(){}};
var console = unsafeWindow.console || fouxConsole;
var $ = unsafeWindow.jQuery;
var timerHandle = 0;
var reloadTimerHandle = 0; // Used for timer that reloads the gateway every few hours
var reloadNeeded = false;
var reloadDelay = 600000; // 10 minutes
var longDelay = 60000; // 1 minute
var delay = 5000; // 5 seconds
var delayShort = 1000;; // 1 second
var dfdNextRun = $.Deferred();
/*
* If there's nothing specified for a level, it looks for the one before it that's specified.
* For example, level 19 of Leadership gets the level 2 config.
*
* For brevity, some lists contain items that are not available at that level; they will be
* ignored by the script.
*
*/
var tasklist = [
{
taskName:"Leadership",
level: {
0:["Hire Your First Mercenary"],
1:["Complete Advanced Training", "Protect Grateful Merchant","Pick Up Package", "Basic Training", "Guard Duty"],
2:["Patrol the Mines","War Games Training","Fight Off Spellplagued","Protect Caravan","Chart Region","Explore Local Area","Train a Guard","Battle Undead","Hire a Mercenary"],
20:["Put Enemy Keep Under Siege","Follow Map to an Unknown Location","Recover Large Mineral Claim","Assault Enemy Stronghold","Protect Diamond Shipment","Destroy Enemy Camp","Collect Taxes","Fight Off Spellplagued","Battle Undead","Give Refugees a Home","Protect Caravan","Patrol the Mines","Chart Region","Explore Local Area"],
},
},
{
// Mailsmithing
taskName:"Armorsmithing_Med",
level: {
0:["Hire your first Prospector"],
1:["Chain Boots","Chain Shirt"],
2:["Gather Iron Ore","Hire an additional Prospector"],
7:["Gather High quality Iron Ore","Upgrade Prospector","Hire an additional Prospector"],
14:["14:Deep Wilderness Gathering","Upgrade Blacksmith","Upgrade Prospector","Hire an additional Prospector"],
19:["14:Deep Wilderness Gathering","Upgrade Blacksmith","Upgrade Prospector","Hire an additional Prospector"],
20:[],
},
},
{
// Platesmithing
taskName:"Armorsmithing_Heavy",
level: {
0:["Hire your first Miner"],
1:["Plate Boots","Plate Shirt","Iron Shield"],
2:["Gather Iron Ore","Hire an additional Miner"],
7:["Gather High quality Iron Ore","Upgrade Miner","Hire an additional Miner"],
14:["14:Deep Wilderness Gathering","Upgrade Armorer","Upgrade Miner","Hire an additional Miner"],
20:[],
},
},
{
taskName:"Leatherworking",
level: {
0:["Hire your first Skinner"],
1:["Leather Boots","Leather Shirt"],
2:["Gather Simple Pelts","Hire an additional Skinner"],
7:["Gather Tough Pelts","Upgrade Skinner","Hire an additional Skinner"],
14:["14:Deep Wilderness Gathering","Upgrade Tanner","Upgrade Skinner","Hire an additional Skinner"],
20:[],
},
},
{
taskName:"Tailoring",
level: {
0:["Hire your first Weaver"],
1:["Cloth Boots","Cloth Shirt"],
2:["Gather Wool Scraps","Hire an additional Weaver"],
7:["Gather Cotton Scraps","Upgrade Weaver","Hire an additional Weaver"],
14:["Intensive Scrap Gathering","Upgrade Outfitter","Upgrade Weaver","Hire an additional Weaver"],
20:[],
},
},
{
taskName:"Artificing",
level: {
0:["Hire your first Carver"],
1:["Virtuous Symbol +1"],
2:["1:Gather Ore and Wood","Hire an additional Carver"],
7:["7:Gather Ore and Wood","Upgrade Carver","Hire an additional Carver"],
14:["14:Deep Wilderness Gathering","Upgrade Engraver","Upgrade Carver","Hire an additional Carver"],
20:[],
},
},
{
taskName:"Weaponsmithing",
level: {
0:["Hire your first Smelter"],
1:["Dagger +1"],
2:["Gather Iron Ore and Pine Wood","Hire an additional Smelter"],
7:["Gather High Quality Ore and Wood","Upgrade Smelter","Hire an additional Smelter"],
14:["14:Deep Wilderness Gathering","Upgrade Grinder","Upgrade Smelter","Hire an additional Smelter"],
20:[],
},
},
{
taskName:"Alchemy",
level: {
0:["Hire your first Apothecary"],
1:["Alchemical Research","Rank 1 Experimentation",],
2:["Alchemical Research","Rank 2 Experimentation","Gather Simple Components"],
// /* for rares to unlock third-tier achievement slot */ 3:["Batch of Minor Elixir of Accuracy", "Batch of Minor Elixirs of Reflexes", "Batch of Minor Force Potions", "Batch of Minor Fortification Potions", "Batch of Minor Healing Potions", "Batch of Minor Rejuvenation Potions", "Batch of Minor Tidespan Potions", "Gather Simple Components"],
3:["Alchemical Research","Rank 3 Experimentation","Gather Simple Components","Hire an additional Apothecary"],
4:["Alchemical Research","Rank 4 Experimentation","Gather Simple Components","Hire an additional Apothecary"],
5:["Alchemical Research","Rank 5 Experimentation","Gather Simple Components","Hire an additional Apothecary"],
6:["Alchemical Research","Rank 6 Experimentation","Gather Simple Components","Hire an additional Apothecary"],
7:["Alchemical Research","Rank 7 Experimentation","Gather Advanced Components","Upgrade Apothecary","Hire an additional Apothecary"],
8:["Transmutation Research","Rank 8 Experimentation","Gather Advanced Components","Upgrade Apothecary","Hire an additional Apothecary"],
9:["Alchemical Research","Rank 9 Experimentation","Gather Advanced Components","Upgrade Apothecary","Hire an additional Apothecary"],
10:["Transmutation Research","Rank 10 Experimentation","Gather Advanced Components","Upgrade Apothecary","Hire an additional Apothecary"],
11:["Alchemical Research","Rank 11 Experimentation","Gather Advanced Components","Upgrade Apothecary","Hire an additional Apothecary"],
12:["Alchemical Research","Rank 12 Experimentation","Gather Advanced Components","Upgrade Apothecary","Hire an additional Apothecary"],
13:["Alchemical Research","Rank 13 Experimentation","Gather Advanced Components","Upgrade Apothecary","Hire an additional Apothecary"],
14:["Alchemical Research","Rank 14 Experimentation","Gather Complex Components","Upgrade Mixologist","Upgrade Apothecary","Hire an additional Apothecary"],
15:["Alchemical Research","Rank 15 Experimentation","Gather Complex Components","Upgrade Mixologist","Upgrade Apothecary","Hire an additional Apothecary"],
16:["Alchemical Research","Rank 16 Experimentation","Gather Complex Components","Upgrade Mixologist","Upgrade Apothecary","Hire an additional Apothecary"],
17:["Alchemical Research","Rank 17 Experimentation","Gather Complex Components","Upgrade Mixologist","Upgrade Apothecary","Hire an additional Apothecary"],
18:["Alchemical Research","Rank 18 Experimentation","Gather Complex Components","Upgrade Mixologist","Upgrade Apothecary","Hire an additional Apothecary"],
19:["Alchemical Research","Rank 19 Experimentation","Gather Complex Components","Upgrade Mixologist","Upgrade Apothecary","Hire an additional Apothecary"],
20:["Empowered Aqua Vitae"],
},
}
];
// Load Settings
/*
This list is a base setting lits
The higher in the list the profession related option the more important it is
Basically if number of slots in few professions sums up to be higher that total number of avaliable slots
Professions that are higher on the below list will get all their setuped slots used,
and professions that are lower will be skipped entairly or just get few but not all slots
Thus if you want for example to prioretize different profession you need to put it as first profession
But it is global - so you need to setup the list in such way to make each profession primary on each character
if its even possible that is
*/
var settingnames = [
{name: 'paused', title: 'Pause Script', def: false, type:'checkbox', tooltip:'Disable All Automation'},
{name: 'debug', title: 'Enable Debug', def: false, type:'checkbox', tooltip:'Enable all debug output to console', onsave: function(newValue, oldValue) {console=newValue?unsafeWindow.console||fouxConsole:fouxConsole;}},
{name: 'autoreload', title: 'Auto Reload', def: false, type:'checkbox', tooltip:'Enabling this will reload the gateway periodically',},
{name: 'autologin', title: 'Attempt to login automatically', def: false, type:'checkbox', tooltip:'Automatically attempt to login to the neverwinter gateway site'},
{name: 'nw_charcount', title: ' How many characters', def: '2', type:'charcount', tooltip:'Specify between how many characters script should cycle through (reload page after changing)'},
{name: 'nw_delay', title: ' Delay between them', def: '60', type:'text2', tooltip:'Specify delay between characters *in seconds* (reload page after changing)'},
{name: 'nw_username', title: ' Account Username', def: '', type:'text', tooltip:''},
{name: 'nw_password', title: ' Account Password', def: '', type:'password', tooltip:''},
{name: 'Label', title:'', content: 'Character Name
LS
MS
PS
LW
Ta
Ar
WS
Al', def: '', type:'label', tooltip:'', repeat:'1'},
{name: 'autopurchase', title: 'Auto Purchase Resources', def: true, type:'checkbox', tooltip:'Automatically purchase required resources from gateway shop (20 at a time)'},
{name: 'excluderare', title: 'Exclude Rare Tasks', def: true, type:'checkbox', tooltip:'Exclude rare tasks to avoid selecting wrong tasks during profession leveling. Selection of specific tasks can be performed by specifying level in task name (eg. "4:Leather Armor +1" will craft the rare task only)'},
{name: 'ActiveChar', title: 'Which character was before?', def: '1', type:'text4', tooltip:'Do not touch this. It just shows which character was done previously - as a way of checking if script works fine'},
];
var setTemp = [];
for(var i = 0; i < settingnames.length; ++i)
{
var t = settingnames[i];
setTemp.push(t);
if(t.repeat == 1)
{
for(var j = 1; j <= fetchValue(settingnames[4].name); ++j)
{
setTemp.push({name: 'nw_charname'+j, title: 'Neverwinter Character '+j, def: '', type:'text1', tooltip:'Name of your '+j+' Neverwinter Character'});
setTemp.push({name: 'Leadership'+j, title: 'Leadership Tasks.............', def: '9', type:'text2', tooltip:'Number of slots to assign to Leadership for '+j+' character'});
setTemp.push({name: 'Armorsmithing_Med'+j, title: 'Mailsmithing Tasks...........', def: '0', type:'text2', tooltip:'Number of slots to assign to Mailsmithing for '+j+' character'});
setTemp.push({name: 'Armorsmithing_Heavy'+j, title: 'Platesmithing Tasks.........', def: '0', type:'text2', tooltip:'Number of slots to assign to Platesmithing for '+j+' character'});
setTemp.push({name: 'Leatherworking'+j, title: 'Leatherworking Tasks......', def: '0', type:'text2', tooltip:'Number of slots to assign to Leatherworking for '+j+' character'});
setTemp.push({name: 'Tailoring'+j, title: 'Tailoring Tasks...................', def: '0', type:'text2', tooltip:'Number of slots to assign to Tailoring for '+j+' character'});
setTemp.push({name: 'Artificing'+j, title: 'Artificing Tasks....................', def: '0', type:'text2', tooltip:'Number of slots to assign to Artificing for '+j+' character'});
setTemp.push({name: 'Weaponsmithing'+j, title: 'Weaponsmithing Tasks....', def: '0', type:'text2', tooltip:'Number of slots to assign to Weaponsmithing for '+j+' character'});
setTemp.push({name: 'Alchemy'+j, title: 'Alchemy Tasks....................', def: '0', type:'text2', tooltip:'Number of slots to assign to Alchemy for '+j+' character'});
}
}
}
settingnames = setTemp;
// Load local settings cache (unsecured)
var settings = {};
for (var i = 0; i < settingnames.length; i++) {
// Ignore label types
if(settingnames[i].type === 'label') {
continue;
}
settings[settingnames[i].name] = fetchValue(settingnames[i].name, settingnames[i].def);
// call the onsave for the setting if it exists
if(typeof(settingnames[i].onsave) === "function") {
settingnames[i].onsave(settings[settingnames[i].name], settings[settingnames[i].name]);
}
}
NWROBOT_cleanSettings = cloneObject(settings);
NWROBOT_cleanSettings.nw_username = NWROBOT_cleanSettings.nw_username.length ? "XXXX" : "";
NWROBOT_cleanSettings.nw_password = NWROBOT_cleanSettings.nw_password.length ? "XXXX" : "";
LOG("Settings loaded: ", JSON.stringify(NWROBOT_cleanSettings));
var longDelay = (settings["nw_delay"]*1000);
// Page Settings
var PAGES = Object.freeze({
LOGIN : { name: "Login", path: "div#login"},
GUARD : { name: "Account Guard", path: "div#page-accountguard"},
CHARSELECT : { name: "Character Select", path: "div.page-characterselect"},
FRONTPAGE : { name: "Front Page", path: "div.page-front"},
PROFESSIONS : { name: "Professions", path: "div.page-professions"},
INVENTORY: { name: "Inventory", path: "div.inventory"},
});
/**
* Uses the page settings to determine which page is currently displayed
*/
function GetCurrentPage() {
for (var page in PAGES) {
if($(PAGES[page]["path"]).filter(":visible").length) {
return PAGES[page];
}
}
}
function page_DEFAULT() {
dfdNextRun.resolve(false,false);
}
function page_LOGIN() {
//if(!$("form > p.error:visible").length && settings["autologin"]) {
// No previous log in error - attempt to log in
LOG("Setting username");
$("input#user").val(settings["nw_username"]);
LOG("Setting password");
$("input#pass").val(settings["nw_password"]);
LOG("Clicking Login Button");
$("div#login > input").click();
//}
dfdNextRun.resolve(false);
}
function page_GUARD() {
// Do nothing on the guard screen
dfdNextRun.resolve(false,false);
}
function page_CHARSELECT() {
// Select the character if it is set nw_charcount ActiveChar
unsafeWindow.client.sounds.toggle(false);
var actualchar = settings["ActiveChar"];
++actualchar;
if(actualchar > settings["nw_charcount"]){
actualchar = 1;
}
LOG("Character number to log: " + actualchar);
settings["ActiveChar"] = actualchar; // Save to local cache
storeValue("ActiveChar", actualchar); // Save to GM cache
var charname = settings["nw_charname" + actualchar];
LOG("Character name to log: " + charname);
if(charname.length) {
LOG("Trying to click...");
$(".char-list-name:contains('"+charname+"')").click()
}
else {
LOG("Error: Empty character name");
}
dfdNextRun.resolve(false,false);
}
function page_FRONTPAGE() {
if (settings["paused"]) return;
// Click the professions tab
$("a.professions").click();
dfdNextRun.resolve(false,false);
}
function page_PROFESSIONS() {
// Switch to overview
$("a.professions-overview").click();
WaitForState("").done(function() {
if (settings["paused"]) return;
// List the buttons on the overview
var completedSlotButtons = $("div.panel-content button").filter(":contains('Collect Result')").filter(":visible");
var openSlotButtons = $("div.panel-content button").filter(":contains('Choose Task')").filter(":visible");
var actualchar = settings["ActiveChar"];
// See if there are any completed tasks on the overview
if(completedSlotButtons.length) {
completedSlotButtons[0].click();
WaitForState("div.professions-rewards-modal button:contains('Collect Result')").done(function() {
$("div.professions-rewards-modal button:contains('Collect Result')").click();
WaitForState("").done(function() {
dfdNextRun.resolve(true,false);
});
});
}
// See if there are any open tasks on the overview
else if(openSlotButtons.length) {
var foundTask = false;
// Go through the professions to assign tasks until specified slots filled
for (var i = 0; i < tasklist.length; i++) {
var currentTasks = $("div.professions-slot-icon." + tasklist[i].taskName);
if(currentTasks.length < settings[tasklist[i].taskName+actualchar]) {
foundTask = true;
openSlotButtons[0].click();
WaitForState("div#content_professions:visible").done(function() {
// Switch to correct type
$("a.professions-" + tasklist[i].taskName).click();
WaitForState("").done(function() {
createNextTask(tasklist[i], 0, dfdNextRun);
});
});
break;
}
}
if(!foundTask) {
LOG("All task counts assigned");
$("a.inventory").click();
dfdNextRun.resolve(false,false);
}
}
else {
$("a.inventory").click();
dfdNextRun.resolve(false,false);
}
});
}
function page_INVENTORY() {
var refineButton = $("span.bag-currency-button > div.input-field.button:not('.disabled') > button:contains('Refine')");
if (refineButton.length) {
LOG("Refining astral diamonds");
refineButton.click();
}
dfdNextRun.resolve(false,true);
}
/**
* Iterative approach to finding the next task to assign to an open slot.
*
* @param {Array} list The list of task names to try in order of precedence
* @param {int} i The current attempt number. Will try to find the i'th task.
* @param {Deferred} dff The deffered object to resolve when a task is found or if all tasks were not found
*/
function createNextTask(prof, i, dff) {
// Check level
var level = parseInt($("a.professions-"+prof.taskName).closest("span").prevAll("div:first").find("span").text());
LOG(prof.taskName, "is level", level);
// Find the applicable config for this level, possibly numbered lower.
for (var list = null, idx = level; idx >= 0 && !list; list = prof.level[idx], idx--)
; /* nothing */
LOG("createNextTask", list.length, i);
if(list.length <=i) {
LOG("Nothing Found");
dff.resolve(false);
return;
}
var taskName = list[i];
LOG("Searching for task:", taskName);
var task = SearchForTaskByName(taskName, prof.taskName, level);
if(task == null) {
LOG("Skipping task selection to purchase resources");
dff.resolve(false);
return;
}
if(task) {
LOG('Task Found');
task.click();
WaitForState("div.page-professions-taskdetails").done(function() {
// Click all buttons and select an item to use in the slot
var def = $.Deferred();
var buttonList = $("h3:contains('Optional Assets:')").closest("div").find("button");
if(buttonList.length) {
SelectItemFor(buttonList, 0, def, prof);
}
else {
def.resolve();
}
def.done(function() {
// All items are populated
LOG("Items Populated");
// Click the Start Task Button
//Get the start task button if it is enabled
var enabledButton = $("div.footer-body > div.input-field.button:not('.disabled') > button:contains('Start Task')");
if(enabledButton.length) {
LOG("Clicking Start Task Button");
enabledButton.click();
WaitForState("").done(function() {
// Done
dff.resolve(true);
});
}
else { // Button not enabled, something required was probably missing
// Go back
$("div.footer-body > div.input-field.button > button:contains('Back')").click();
WaitForState("").done(function() {
// continue with the next one
LOG('Finding next task');
createNextTask(prof, i+1, dff);
});
}
});
});
}
else {
LOG('Finding next task');
createNextTask(prof, i+1, dff);
}
}
/**
* Selects the highest level asset for the i'th button in the list. Uses an iterative approach
* in order to apply a sufficient delay after the asset is assigned
*
* @param {Array} The list of buttons to use to click and assign assets for
* @param {int} i The current iteration number. Will select assets for the i'th button
* @param {Deferred} jQuery Deferred object to resolve when all of the assets have been assigned
*/
function SelectItemFor(buttonListIn, i, def, prof) {
buttonListIn[i].click();
WaitForState("").done(function() {
var specialItems = $("div.modal-item-list a.Special");
var goldItems = $("div.modal-item-list a.Gold");
var silverItems = $("div.modal-item-list a.Silver");
var bronzeItems = $("div.modal-item-list a.Bronze");
var clicked = false;
// Try to avoid using up higher rank assets needlessly
if(prof.taskName === "Leadership") {
var mercenarys = $("div.modal-item-list a.Bronze:contains('Mercenary')");
var guards = $("div.modal-item-list a.Bronze:contains('Guard')");
var footmen = $("div.modal-item-list a.Bronze:contains('Footman')");
if(mercenarys.length) { clicked = true; mercenarys[0].click(); }
else if(guards.length) { clicked = true; guards[0].click(); }
else if(footmen.length) { clicked = true; footmen[0].click(); }
}
else if(prof.taskName === "Tailoring") {
var mercenarys = $("div.modal-item-list a.Bronze:contains('Weaver')");
var guards = $("div.modal-item-list a.Bronze:contains('Outfitter')");
var footmen = $("div.modal-item-list a.Bronze:contains('Assistant Tailor')");
if(mercenarys.length) { clicked = true; mercenarys[0].click(); }
else if(guards.length) { clicked = true; guards[0].click(); }
else if(footmen.length) { clicked = true; footmen[0].click(); }
}
else if(prof.taskName === "Weaponsmithing") {
var mercenarys = $("div.modal-item-list a.Bronze:contains('Smelter')");
var guards = $("div.modal-item-list a.Bronze:contains('Grinder')");
var footmen = $("div.modal-item-list a.Bronze:contains('Assistant Weaponsmith')");
if(mercenarys.length) { clicked = true; mercenarys[0].click(); }
else if(guards.length) { clicked = true; guards[0].click(); }
else if(footmen.length) { clicked = true; footmen[0].click(); }
}
else if(prof.taskName === "Platesmithing") {
var mercenarys = $("div.modal-item-list a.Bronze:contains('Miner')");
var guards = $("div.modal-item-list a.Bronze:contains('Armorer')");
var footmen = $("div.modal-item-list a.Bronze:contains('Assistant Platesmith')");
if(mercenarys.length) { clicked = true; mercenarys[0].click(); }
else if(guards.length) { clicked = true; guards[0].click(); }
else if(footmen.length) { clicked = true; footmen[0].click(); }
}
else if(prof.taskName === "Mailsmithing") {
var mercenarys = $("div.modal-item-list a.Bronze:contains('Prospector')");
var guards = $("div.modal-item-list a.Bronze:contains('Blacksmith')");
var footmen = $("div.modal-item-list a.Bronze:contains('Assistant Mailsmith')");
if(mercenarys.length) { clicked = true; mercenarys[0].click(); }
else if(guards.length) { clicked = true; guards[0].click(); }
else if(footmen.length) { clicked = true; footmen[0].click(); }
}
else if(prof.taskName === "Leatherworking") {
var mercenarys = $("div.modal-item-list a.Bronze:contains('Skinner')");
var guards = $("div.modal-item-list a.Bronze:contains('Tanner')");
var footmen = $("div.modal-item-list a.Bronze:contains('Assistant Leatherworker')");
if(mercenarys.length) { clicked = true; mercenarys[0].click(); }
else if(guards.length) { clicked = true; guards[0].click(); }
else if(footmen.length) { clicked = true; footmen[0].click(); }
}
else if(prof.taskName === "Artificing") {
var mercenarys = $("div.modal-item-list a.Bronze:contains('Carver')");
var guards = $("div.modal-item-list a.Bronze:contains('Engraver')");
var footmen = $("div.modal-item-list a.Bronze:contains('Assistant Artificer')");
if(mercenarys.length) { clicked = true; mercenarys[0].click(); }
else if(guards.length) { clicked = true; guards[0].click(); }
else if(footmen.length) { clicked = true; footmen[0].click(); }
}
else if(prof.taskName === "Alchemy") {
var mercenarys = $("div.modal-item-list a.Bronze:contains('Apothecary')");
var guards = $("div.modal-item-list a.Bronze:contains('Mixologist')");
var footmen = $("div.modal-item-list a.Bronze:contains('Assistant Alchemist')");
if(mercenarys.length) { clicked = true; mercenarys[0].click(); }
else if(guards.length) { clicked = true; guards[0].click(); }
else if(footmen.length) { clicked = true; footmen[0].click(); }
}
// don't use up special workers, might leave slots unfillable
// better: optimize against time saved and open slots
if (!clicked && prof.taskName !== "Leadership") {
// Click the highest slot
if(specialItems.length) { specialItems[0].click(); }
else if(goldItems.length) { goldItems[0].click(); }
else if(silverItems.length) { silverItems[0].click(); }
else if(bronzeItems.length) { bronzeItems[0].click(); }
else { $("button.close-button").click(); }
}
LOG("Clicked item");
WaitForState("").done(function() {
// Get the new set of select buttons created since the other ones are removed when the asset loads
var buttonList = $("h3:contains('Optional Assets:')").closest("div").find("button");
if(i < buttonList.length - 1) {
SelectItemFor(buttonList, i+1, def, prof);
}
else {
// Let main loop continue
def.resolve();
}
});
});
}
/**
* Given a task name and that the search pane is active, will attempt to search for the given task
* and return the button element if the level and resource criteria are met.
*
* @param {String} taskname The name of the task to search within the search pane for
*/
function SearchForTaskByName(taskname, profName, profLevel) {
var tasklevel = 0;
if(taskname.indexOf(":") > -1) {
// split task with level requirement
tasklevel=taskname.split(":",2)[0];
taskname=taskname.split(":",2)[1];
}
// Filter the results
var filterDiv = $("div#tasklist_filter input");
LOG("Searching for:", taskname);
filterDiv.val(taskname);
filterDiv.keyup();
// Find the result
var taskTitle = $("table#tasklist tr h4 span").filter(function() {
return $(this).text() === taskname;
});
if(taskTitle.length) {
for (var i = 0; i < taskTitle.length; i++) {
if($(taskTitle[i]).closest("div.rare").length && profName != "Leadership" && settings["excluderare"]) {
// Avoid rare tasks unless profession is Leadership
LOG("Avoiding rare craft: ", taskname);
}
else if($(taskTitle[i]).closest("div.higherlevel").length) {
// Too high level
LOG("Task level is too high: ", taskname);
}
else if(tasklevel > 0 && $(taskTitle[i]).closest("div").find("span.level-pip").text() != tasklevel) {
// Task level doesn't match
LOG("Task level does not match requirement: ", taskname, tasklevel);
}
else if($(taskTitle[i]).closest("div.unmet").length) {
// Check for required ingredients
LOG("Checking for craftable ingredients for", taskname);
var requires = $(taskTitle[i]).closest("div.unmet").find("div.task-requirements div.icon-slot.red").filter("[data-tt-item]");
for (var j = 0; j < requires.length; j++) {
var ingName = $(requires[j]).attr("data-tt-item");
LOG("Found", ingName);
var searchStr = ingName;
// Specify task search string depending on type of ingredient required
// Add here any ingredient types you want to check tasks for
// Resources
if (ingName.indexOf("Resource_Charcoal") > -1
|| ingName.indexOf("Resource_Rocksalt") > -1
|| ingName.indexOf("Resource_Spool_Thread") > -1
|| ingName.indexOf("Resource_Porridge") > -1
|| ingName.indexOf("Resource_Solvent") > -1
|| ingName.indexOf("Resource_Brimstone") > -1
|| ingName.indexOf("Resource_Coal") > -1
|| ingName.indexOf("Resource_Moonseasalt") > -1
|| ingName.indexOf("Resource_Quicksilver") > -1
|| ingName.indexOf("Resource_Spool_Threadsilk") > -1) {
if (settings["autopurchase"]) {
BuyResource(ingName);
return null;
}
else {
LOG("Auto resource purchasing disabled");
continue;
}
}
else if(ingName.indexOf("Resource_Wood_Carved") > -1
|| ingName.indexOf("Resource_Ornaments") > -1
|| ingName.indexOf("Resource_Weapon") > -1) { searchStr = "Craft"; }
else if(ingName.indexOf("Resource_Pelt") > -1
|| ingName.indexOf("Resource_Wood") > -1
|| ingName.indexOf("Resource_Ore") > -1
|| ingName.indexOf("Resource_Clothscraps") > -1) { searchStr = "Gather"; }
else if(ingName.indexOf("Resource_Leather") > -1) { searchStr = "Cure Pelt"; }
else if(ingName.indexOf("Resource_Rings") > -1) { searchStr = "Forge Ring"; }
else if(ingName.indexOf("Resource_Clothbolt") > -1) { searchStr = "Weave Cloth"; }
else if(ingName.indexOf("Resource_Ingot") > -1) { searchStr = "Forge Plate"; }
// Alchemy
else if(ingName.indexOf("Aquavitae") > -1) { searchStr = "Aqua Vitae"; }
else if(ingName.indexOf("Aquaregia") > -1) { searchStr = "Aqua Regia"; }
else if(ingName.indexOf("Vitriol") > -1) { searchStr = "Vitriol Extraction"; }
else if(ingName.indexOf("Potion") > -1) { searchStr = ingName.replace(/Potion_/,"").replace(/_[0-9]+$/,""); }
// Shirts
else if(ingName.indexOf("Chain_Shirt") > -1) { searchStr = "Chain Shirt"; }
else if(ingName.indexOf("Scale_Shirt") > -1
|| ingName.indexOf("Med_Armorsmithing_Shirt") > -1) { searchStr = "Scale Shirt"; }
else if(ingName.indexOf("Shirt") > -1) { searchStr = "Shirt"; }
// Pants
else if(ingName.indexOf("Chain_Pants") > -1) { searchStr = "Chain Pants"; }
else if(ingName.indexOf("Scale_Pants") > -1
|| ingName.indexOf("Med_Armorsmithing_Pants") > -1) { searchStr = "Scale Pants"; }
else if(ingName.indexOf("Pants") > -1) { searchStr = "Pants"; }
// Armor
else if(ingName.indexOf("T1_Chain_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("T2_Chain_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("Chain_Armor") > -1) { searchStr = "Chain Armor"; }
else if(ingName.indexOf("T1_Scale_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("T2_Scale_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("Scale_Armor") > -1) { searchStr = "Scale Armor"; }
else if(ingName.indexOf("T1_Cloth_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("T2_Cloth_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("Cloth_Armor") > -1) { searchStr = "Cloth Robes"; }
else if(ingName.indexOf("T1_Leather_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("T2_Leather_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("Leather_Armor") > -1) { searchStr = "Leather Armor"; }
else if(ingName.indexOf("T1_Plate_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("T2_Plate_Armor") > -1) { searchStr = "Skip Me"; }
else if(ingName.indexOf("Plate_Armor") > -1) { searchStr = "Plate Armor"; }
// Helms
else if(ingName.indexOf("Chain_Helm") > -1) { searchStr = "Chain Helm"; }
else if(ingName.indexOf("Med_Armorsmithing_T2_Helm") > -1) { searchStr = "Scale Helm"; }
else if(ingName.indexOf("Tailoring_T2_Helm") > -1) { searchStr = "Cloth Cap"; }
else if(ingName.indexOf("Helm") > -1) { searchStr = "Helm"; }
// Arms
else if(ingName.indexOf("T1_Chain_Gloves") > -1) { searchStr = "Chain Gloves +1"; }
else if(ingName.indexOf("T2_Chain_Gloves") > -1) { searchStr = "Chain Gloves +2"; }
else if(ingName.indexOf("Chain_Gloves") > -1) { searchStr = "Chain Gloves"; }
else if(ingName.indexOf("T1_Scale_Gloves") > -1) { searchStr = "Scale Gauntlets +1"; }
else if(ingName.indexOf("T2_Scale_Gloves") > -1) { searchStr = "Scale Gauntlets +2"; }
else if(ingName.indexOf("Scale_Gloves") > -1) { searchStr = "Scale Gauntlets"; }
else if(ingName.indexOf("T1_Cloth_Gloves") > -1) { searchStr = "Cloth Sleeves +1"; }
else if(ingName.indexOf("T2_Cloth_Gloves") > -1) { searchStr = "Cloth Sleeves +2"; }
else if(ingName.indexOf("Cloth_Gloves") > -1) { searchStr = "Cloth Sleeves"; }
else if(ingName.indexOf("T1_Leather_Gloves") > -1) { searchStr = "Leather Gloves +1"; }
else if(ingName.indexOf("T2_Leather_Gloves") > -1) { searchStr = "Leather Bracers +2"; }
else if(ingName.indexOf("Leather_Gloves") > -1) { searchStr = "Leather Bracers"; }
else if(ingName.indexOf("T1_Plate_Gloves") > -1) { searchStr = "Plate Gauntlets +1"; }
else if(ingName.indexOf("T2_Plate_Gloves") > -1) { searchStr = "Plate Gauntlets +2"; }
else if(ingName.indexOf("Plate_Gloves") > -1) { searchStr = "Plate Gauntlets"; }
// Boots
else if(ingName.indexOf("T1_Chain_Boots") > -1) { searchStr = "Chain Boots +1"; }
else if(ingName.indexOf("T2_Chain_Boots") > -1) { searchStr = "Chain Boots +2"; }
else if(ingName.indexOf("Chain_Boots") > -1) { searchStr = "Chain Boots"; }
else if(ingName.indexOf("T1_Scale_Boots") > -1) { searchStr = "Scale Boots +1"; }
else if(ingName.indexOf("T2_Scale_Boots") > -1) { searchStr = "Scale Boots +2"; }
else if(ingName.indexOf("Scale_Boots") > -1) { searchStr = "Scale Boots"; }
else if(ingName.indexOf("T1_Cloth_Boots") > -1) { searchStr = "Cloth Boots +1"; }
else if(ingName.indexOf("T2_Cloth_Boots") > -1) { searchStr = "Cloth Boots +2"; }
else if(ingName.indexOf("Cloth_Boots") > -1) { searchStr = "Cloth Boots"; }
else if(ingName.indexOf("T1_Leather_Boots") > -1) { searchStr = "Leather Boots +1"; }
else if(ingName.indexOf("T2_Leather_Boots") > -1) { searchStr = "Leather Boots +2"; }
else if(ingName.indexOf("Leather_Boots") > -1) { searchStr = "Leather Boots"; }
else if(ingName.indexOf("T1_Plate_Boots") > -1) { searchStr = "Plate Boots +1"; }
else if(ingName.indexOf("T2_Plate_Boots") > -1) { searchStr = "Plate Boots +2"; }
else if(ingName.indexOf("Plate_Boots") > -1) { searchStr = "Plate Boots"; }
// Daggers
else if(ingName.indexOf("T2_Dagger_3") > -1) { searchStr = "Dagger +3"; }
else if(ingName.indexOf("T2_Dagger_2") > -1) { searchStr = "Dagger +2"; }
else if(ingName.indexOf("T1_Dagger_1") > -1) { searchStr = "Dagger +1"; }
// Icons
else if(ingName.indexOf("T3_Icon_Virtuous_4") > -1) { searchStr = "Virtuous Icon +4"; }
else if(ingName.indexOf("T2_Icon_Virtuous_3") > -1) { searchStr = "Virtuous Icon +3"; }
else if(ingName.indexOf("T1_Icon_Virtuous_2") > -1) { searchStr = "Virtuous Icon +2"; }
else if(ingName.indexOf("T1_Icon_Virtuous_1") > -1) { searchStr = "Virtuous Icon +1"; }
// Shields
else if(ingName.indexOf("T2_Shield") > -1) { searchStr = "Steel Shield"; }
else if(ingName.indexOf("T1_Shield") > -1) { searchStr = "Iron Shield"; }
else {
LOG("Found unhandled ingredient", ingName);
continue;
}
LOG("Searching for tasks for:", searchStr);
filterDiv.val(searchStr);
filterDiv.keyup();
// Find the ingredient task
var ingTaskIcon = $("table#tasklist div.task-rewards div.icon-slot").filter(function() {
return $(this).attr("data-tt-item") === ingName;
});
// Check ingredient task for requirements and return task if valid
if (ingTaskIcon.length) {
for (var k = 0; k < ingTaskIcon.length; k++) {
var ingTitle = $($(ingTaskIcon[k]).closest("div.task-list-entry").find("h4 span")).text();
LOG("Found ingredient task",ingTitle);
// Skip mass production tasks
if(ingTitle.indexOf("Mass ") == 0) { continue; }
// Skip deep gathering tasks
if(ingTitle.indexOf("Deep ") == 0) { continue; }
// Skip intensive gathering tasks
if(ingTitle.indexOf("Intensive ") == 0) { continue; }
// Skip Batch gathering tasks
if(ingTitle.indexOf("Batch ") == 0) { continue; }
// Skip Empowered gathering tasks
if(profLevel<8){
if(ingTitle.indexOf("Empowered ") == 0) { continue; }
}
// Search for correct level task for same named Artificing gather tasks
if(ingTitle.indexOf("Gather Ore and Wood") > -1 || ingTitle.indexOf("Craft Ornamental metal and Carved Wood") > -1) {
if (ingName.indexOf("_T1") > -1) { ingTitle = "1:" + ingTitle }
else if(ingName.indexOf("_T2") > -1) { ingTitle = "7:" + ingTitle }
else if(ingName.indexOf("_T3") > -1) { ingTitle = "14:" + ingTitle }
}
var ingTask = SearchForTaskByName(ingTitle, profName, profLevel);
LOG("Requesting required ingredient task", ingTitle);
return ingTask;
}
}
else { LOG("No ingredient tasks available:", taskname); }
}
// Not enough resources
LOG("Not enough resources:", taskname);
}
else {
return $(taskTitle[i]).closest("tr").find("button");
}
}
LOG("No valid tasks found:", taskname);
}
return false;
}
/**
* Will buy a given purchasable resource
*
* @param {String} item The data-tt-item id of the Resource to purchase
*/
function BuyResource(item) {
LOG("Purchasing resources:", item);
// Switch to overview
$("a.professions-overview").click();
WaitForState("").done(function() {
// Enter resource shop
$("button").filter(function() { return $(this).attr("data-url") === "/professions/vendor"; }).click();
WaitForState("ul.vendor-group.resources li.vendor-entry div").done(function() {
// Get shop section for required item
var shopItem = $("ul.vendor-group.resources li.vendor-entry div").filter(function() {
return $(this).attr("data-tt-item") === item;
}).closest("li");
shopItem.find("button").click();
WaitForState("input[name='inventoryBuyQty']").done(function() {
// Set purchase amount to 20 and buy
$("input[name='inventoryBuyQty']").val(20);
$("button:contains('OK')").click();
LOG("Resources purchased");
// Close the notification that never times out
WaitForState("button.closeNotification").done(function() {
$("button.closeNotification").click();
});
});
});
});
}
/**
* Waits for the loading symbol to be hidden.
*
* @return {Deferred} A jQuery defferred object that will be resolved when loading is complete
*/
function WaitForLoad() {
return WaitForState("");
}
/**
* Creates a deferred object that will be resolved when the state is reached
*
* @param {string} query The query for the state to wait for
* @return {Deferred} A jQuery defferred object that will be resolved when the state is reached
*/
function WaitForState(query) {
var dfd = $.Deferred();
window.setTimeout(function() {AttemptResolve(query, dfd);}, delayShort); // Doesn't work without a short delay
return dfd;
}
/**
* Will continually test for the given query state and resolve the given deferred object when the state is reached
* and the loading symbol is not visible
*
* @param {string} query The query for the state to wait for
* @param {Deferred} dfd The jQuery defferred object that will be resolved when the state is reached
*/
function AttemptResolve(query, dfd) {
if((query === "" || $(query).length) && $("div.loading-image:visible").length == 0) {
dfd.resolve();
}
else {
window.setTimeout(function() {AttemptResolve(query, dfd);}, delayShort); // Try again in a little bit
}
}
function updateHeartbeat() {
$("#settingsButton img").attr("title", "Running as of " + (new Date).toLocaleTimeString());
}
/**
* The main process loop:
* - Determine which page we are on and call the page specific logic
* - When processing is complete, process again later
* - Use a short timer when something changed last time through
* - Use a longer timer when waiting for tasks to complete
*/
function process() {
// Make sure the settings button exists
addSettings();
// Check if timer is paused
if(settings["paused"]) {
// Just continue later - the deferred object is still set and nothing will resolve it until we get past this point
var timerHandle = window.setTimeout(function() {process();}, delay);
return;
}
updateHeartbeat();
// check for errors
if($("title").text() == "Error" && settings["autologin"]) {
LOG("Error detected - relogging");
unsafeWindow.location.href = "http://gateway.playneverwinter.com";
return;
}
// Check if a reload is needed
if(reloadNeeded && settings["autologin"] && settings["autoreload"]) {
LOG("Reloading Gateway");
unsafeWindow.location.href = "http://gateway.playneverwinter.com";
reloadNeeded = false;
return;
}
// disconnected?
if ($("div#modal_content:contains('disconnected')").length) {
LOG("Disconnected, reloading.");
unsafeWindow.location.href = "http://gateway.playneverwinter.com";
return;
}
// Check for Gateway down
var url = window.location.href.toString();
if(url.indexOf("gatewaysitedown") > -1 ||
url.indexOf("locked") > -1) {
// Do a long delay and then retry the site
var minuty = (longDelay-longDelay%60000)/60000;
var sekundy = (longDelay%60000)/1000;
LOG("Gateway down detected - relogging in " + (minuty>0?(sekundy>0?minuty + " minute(s) and " + sekundy + " second(s)":minuty + " minute(s)"):sekundy + " second(s)"));
window.setTimeout(function() {unsafeWindow.location.href = "http://gateway.playneverwinter.com";}, longDelay);
return;
}
// Determine which page is displaying
var currentPage = GetCurrentPage();
if(currentPage == PAGES.LOGIN) { page_LOGIN(); }
else if(currentPage == PAGES.GUARD) { page_GUARD(); }
else if(currentPage == PAGES.CHARSELECT) { page_CHARSELECT(); }
else if(currentPage == PAGES.FRONTPAGE) { page_FRONTPAGE(); }
else if(currentPage == PAGES.PROFESSIONS) { page_PROFESSIONS(); }
else if(currentPage == PAGES.INVENTORY ) { page_INVENTORY(); }
else { page_DEFAULT(); }
// Continue again later
dfdNextRun.done(function(useShortDelay, charChangeDelay) {
dfdNextRun = $.Deferred();
if(charChangeDelay) {
var minuty = (longDelay-longDelay%60000)/60000;
var sekundy = (longDelay%60000)/1000;
LOG("To change characters script will relog in " + (minuty>0?(sekundy>0?minuty + " minute(s) and " + sekundy + " second(s)":minuty + " minute(s)"):sekundy + " second(s)"));
timerHandle = window.setTimeout(function() {unsafeWindow.location.href = "http://gateway.playneverwinter.com";}, longDelay);
}
else {
timerHandle = window.setTimeout(function() {process();}, useShortDelay===true?delayShort:delay);
}
if(!reloadTimerHandle) {
reloadTimerHandle = window.setTimeout(function() {
LOG("setting reloadNeeded @ " + new Date());
reloadNeeded = true; },
reloadDelay);
}
});
}
function addSettings() {
if($("#settingsButton").length)
return;
// Add the required CSS
AddCss("\
#settingsButton{border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); background: none repeat scroll 0% 0% rgb(238, 238, 238); display: block; position: fixed; overflow: auto; right: 0px; top: 0px; padding: 3px; z-index: 1000;}\
#settingsPanel{border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); background: none repeat scroll 0% 0% rgb(238, 238, 238); color: rgb(0, 0, 0); position: fixed; overflow: auto; right: 0px; top: 0px; width: 360px; font: 12px sans-serif; text-align: left; display: block; z-index: 1000;}\
#settings_title{font-weight: bolder; background: none repeat scroll 0% 0% rgb(204, 204, 204); border-bottom: 1px solid rgb(102, 102, 102); padding: 3px;}\
#settingsPanelButtonContainer {background: none repeat scroll 0% 0% rgb(204, 204, 204); border-top: 1px solid rgb(102, 102, 102);padding: 3px;text-align:center} \
#settingsPanel label.purple {font-weight:bold;color:#7C37F6}\
#settingsPanel label.blue {font-weight:bold;color:#007EFF}\
#settingsPanel label.green {font-weight:bold;color:#8AFF00}\
#settingsPanel label.white {font-weight:bold;color:#FFFFFF}\
#settingsPanel input.slot_count { width: 1.5em; }\
#settingsPanel input.charname { margin-right: 1em; }\
");
// Add settings panel to page body
$("body").append(
'
\
\
\
\
Settings\
\
\
'
);
// Add each setting input
var settingsList = $("#settingsPanel form ul");
for (var i=0;i')
$('#'+id).prop('checked', settings[settingnames[i].name]);
break;
case "text1":
appendingus = '
');
// Add open settings button to page
$("body").append('');
// Add the javascript
$("#settingsPanel").hide();
$("#settingsButton").click(function() {
$("#settingsButton").hide();
$("#settingsPanel").show();
});
$("#settings_close,settings_cancel").click(function() {
$("#settingsButton").show();
$("#settingsPanel").hide();
});
// Use setTimeout to workaround permission issues when calling GM functions from main window
$("#settings_save").click(function() { setTimeout(function() { SaveSettings();}, 0)});
}
function SaveSettings() {
// Get each value from the UI
for (var i=0;i