// antimarty's fortune cookie script, based in part on:
// csemaj's KoL Script
// Copyright (c) 2007, James Cammarata
// Based on code written by Byung Kim (Tard) http://kol.dashida.com and OneTonTomato's scripts
// toggle preference code from lukifer's mrscript
// http://www.noblesse-oblige.org/lukifer/scripts/
// script update code based on DrEvi1's hatrack helper, which credits Picklish
// fixes for lag combatting inventory screens by Firvagor
// now using Charon's account options tab code
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
//
// ==UserScript==
// @name antimarty fortune cookie script
// @namespace antimarty
// @include *kingdomofloathing.com/*.php*
// @include *127.0.0.1:600*/*.php*
// @include *localhost:600*/*.php*
// @include localhost:18481/*.php*
// @version 0.5.8
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
//
// @description Kingdom of Loathing fortune cookie tracker
// ==/UserScript==
// released versions:
// Version 0.5.7 - clan speakeasy, new topmenu frame rearrangement, gm grant stuff, move to Greasy Fork
// Version 0.5.5 - kolhs semis; red fox; bram the stoker
// Version 0.5.4 - new undersea SRs
// Version 0.5.3 - new giant castle SRs, fix oxygenarian detection
// Version 0.5.2 - updated for new level 8 and level 9 semis, and typo for friars
// Version 0.5.1 - new hidden temple semi rare
// Version 0.5.0 - add last location info to char pane if no popups
// Version 0.4.9 - bug fix for deep fat friars' adventure name
// Version 0.4.8 - bug fix for auto-attack combat macros, change version check
// Version 0.4.7 - add disable popups option as FF4 workaround
// Version 0.4.6 - bug fix for noncombat semis
// Version 0.4.5 - Show initial bracket for new ascension, bug fix for puttied/faxed/arrow'd SR monsters
// Version 0.4.4 - ascii, kge fights from fax/putty/camera/arrowing aren't semis; support new options page
// Version 0.4.3 - add elf alley semi
// Version 0.4.2 - add the pixel stopwatch
// Version 0.4.1, use the turnsplayed native counter
// Version 0.4.0, display a predicted cookie window if no cookie eaten
// Version 0.3.9, support for billiards room semi
// Version 0.3.8, sanity check numbers when user loads charsheet
// Version 0.3.7, misc bug fixes
// Version 0.3.6, fixes (courtesy Firvagor) for crimbo anti-lag changes to inventory screens; clear counter after semi-rare
// Version 0.3.5, filter impossible cookie values; check for updates automatically
// Version 0.3.4, support for hobopolis adventures
// Version 0.3.3, bugfixes encore
// Version 0.3.2, bugfixes
// Version 0.3.1, bugfixes
// Version 0.3.0, remember most recent semi-rare. Re-fix bugs re-introduced in 0.2.6
// for unascended chars and compact mode, thanks to bad version control.
// Version 0.2.6, reduced overhead, and bug fixes
// Version 0.2.5, compact mode support and support for unascended chars
// Version 0.2.3, improved sanity checker
// Version 0.2.0, includes sanity checker
// Version 0.1.1, bugfixes
// Version 0.1.0 initial beta
// Known bugs:
// doesn't work in the kolmafia relay browser if you have this option set:
// [] Force results to reload inline for [use] links
// ... because the results page never reloads, so greasemonkey scripts don't run
// may not save data correctly if Firefox doesn't exit cleanly (Greasemonkey issue)
// basic logic to do this without retrieving character pane data:
// - store an internal counter for adventures used
// - look at the adventures remaining
// - every time the pane is refreshed:
// - if the remaining adventures decreases, decrease the counter by that amount
// - if stays the same, do nothing
// - if increases, do nothing
// warn when hit zero
//
// read char sheet as a sanity check when the counter doesn't seem to be
// decrementing normally
// (temporarily?) unused, with move to Greasy Fork
/* var currentVersion = "0.5.7";
var scriptSite = "http://userscripts.org/scripts/show/13180"
// this is autogenerated by userscripts.org from Userscript @ comments above, use to reduce bandwidth on version check
var scriptURL = "http://userscripts.org/scripts/source/13180.meta.js";
*/
////////////////////////////////////////////////////////////////////////////////
// Based on a function taken from OneTonTomato's UpUp skill script
function GM_get(target, callback) {
GM_xmlhttpRequest({
method: 'GET',
url: target,
onload:function(details) {
if( typeof callback=='function' ){
callback(details.responseText);
}
}
});
}
// Check version number of script on the web
function CheckScriptVersion(data)
{
// alert("inside version check");
// Preemptively set error, in case request fails...
GM_setValue("webVersion", "Error")
var m = data.match(/@version\s*([0-9.]+)/);
if (m) {
GM_setValue("webVersion", m[1]);
}
}
////////////////////////////////////////////////////////////////////////////////
// parse the char pane for the player name
// revised version! now taken directly from kolpreviousnadventures to handle compact mode
function getPlayerNameFromCharpane() {
var username = document.getElementsByTagName("b");
if (!username || username.length < 1) return false;
username = username[0];
if (!username) return false;
username = username.firstChild;
if (!username) return false;
// in full mode the link is <a><b>Name</b></a>
// in compact mode it's <b><a>Name</a></b>
// so have to handle this, and also can use it to tell
// whether it's in compact mode or not.
var fullmode = true;
while (username && username.nodeType == 1)
{
username = username.firstChild;
fullmode = false;
}
if (!username) return false;
username = username.nodeValue;
if (!username) return false;
username = username.toLowerCase();
// alert("found username " + username + ", fullmode: " + fullmode);
return {'username': username, 'fullmode': fullmode};
}
////////////////////////////////////////////////////////////////////////////////
// parse the char sheet (not the sidepane) for the player name
function getPlayerNameFromCharsheet(data) {
// it's an href with syntax something like
// showplayer.php?who=PlayerID">PlayerName</a>
var playerName = /showplayer\.php\?who\=\d+\">([^<]+)<\/a/i.exec(data); // sometimes this fails, don't know why
if(playerName)
return playerName[1].toLowerCase();
else
return null;
}
////////////////////////////////////////////////////////////////////////////////
// parse the char sheet (not pane) for the total adventure count
// taken from the csemaj cookie script
// the "(this run)" only appears if you have ascended
function getTurnsPlayed(data) {
var turncount = "0";
if(data.indexOf("Turns Played (this run)") >= 0) {
// alert("parsing datasheet for turns for an ascended char");
turncount = /Turns Played \(this run\)[^>]*>(<[^>]+>)*([\d,]+)/i.exec(data)[2];
}
else {
// alert("parsing datasheet for turns for an UNascended char");
turncount = /Turns Played[^>]*>(<[^>]+>)*([\d,]+)/i.exec(data)[2];
}
// alert("found turncount=" + turncount);
return parseInt(turncount.replace(',',''),10);
}
function getDaysPlayed(data) {
var dayCount = 0;
if(data.indexOf("Days Played (this run)") >= 0) {
// alert("parsing datasheet for days for an ascended char");
dayCount = /Days Played \(this run\)[^>]*>(<[^>]+>)*([\d,]+)/i.exec(data)[2];
}
else {
// alert("parsing datasheet for days for an UNascended char");
dayCount = /Days Played[^>]*>(<[^>]+>)*([\d,]+)/i.exec(data)[2];
}
// alert("found dayCount=" + dayCount);
return parseInt(dayCount.replace(',',''),10);
}
// parse the charpane info for the password hash (use as a session ID)
function getPwdHash(data){
var pwdHash = /pwdhash \= \"(.*?)\"/i.exec(data)[1]; // the .*? is the non-greedy version of .*
// alert("got pwdHash: " + pwdHash);
return pwdHash;
}
// parse the charpane info for the turnsplayed counter
// updates at the end of the adventure, not during the fight
function getTurnsplayedVar(data){
var turnsplayed = /turnsthisrun \= (\d+)/i.exec(data)[1];
return parseInt(turnsplayed);
}
////////////////////////////////////////////////////////////////////////////////
// parse the main page for the name of the (noncombat) adventure - assuming
// it's the next bolded thing after "Adventure Results:"
function getAdventureName(data) {
var adventureName = "";
if ( data.indexOf( "Adventure Results:" ) != -1 ) { // kol mafia can put up stuff that requires this check
adventureName = /Results:<\/b>.*?<b>(.*?)<\/b>/i.exec(data)[1];
}
// the hobopolis rares are choice adventures, not normal noncombats
else if(document.location.pathname.indexOf("choice.php") != -1) {
// look for adventure name as something in bold on blue background
adventureName = /bgcolor=\"blue\"><b>(.*?)<\/b>/i.exec(data)[1];
}
// check for known broken adventures
if(adventureName == "") {
if(data.indexOf( "There once was a bleary-eyed cyclops" ) != -1 ){
adventureName = "The Bleary-Eyed Cyclops";
}
}
// alert("got adventure name: " + adventureName);
return adventureName;
}
////////////////////////////////////////////////////////////////////////////////
// parse the main page for the name of the adventure
function getMonsterName(data) {
var monsterName = /id=\"monname\"> *(.*?)<\/span>/i.exec(data);
if(monsterName)
monsterName = monsterName[1].toLowerCase();
else
return {'monsterName': "", 'fromPutty': false};
// don't count monsters from putty/fax/camera/arrowing
var fromPutty = data.indexOf("You put the copied monster on the ground") != -1 ? true : false;
if(!fromPutty)
fromPutty = data.indexOf("You gaze at the photocopy") != -1 ? true : false;
if(!fromPutty)
fromPutty = data.indexOf("You reach down to open up the camera") != -1 ? true : false;
if(!fromPutty)
fromPutty = data.indexOf("hear a wolf whistle from behind you") != -1 ? true : false;
if(!fromPutty)
fromPutty = data.indexOf("You play back the recording") != -1 ? true : false;
if(!fromPutty)
fromPutty = data.indexOf("%%") != -1 ? true : false; // some sort of error we see a lot with copied monsters
// alert("got monster name: *" + monsterName + "*; from putty = " + fromPutty);
return {'monsterName': monsterName, 'fromPutty': fromPutty};
}
// process a noncombat result for semi-rareness
var semis =
{
noncombats: {
"Play Misty For Me" : "the Haunted Kitchen",
"Like the Sunglasses, But Less Comfortable" : "the Haunted Library",
"The Pilsbury Doughjerk" : "the Haunted Pantry",
"The Bleary-Eyed Cyclops" : "the Limerick Dungeon", // I don't see the title appear when I get this adv.
"In the Still of the Alley" : "the Sleazy Back Alley",
"Natural Selection" : "the Goatlet",
"Not Quite as Cold as Ice" : "the Lair of the Ninja Snowmen",
"Prior to Always" : "a Battlefield",
"How Does He Smell?" : "the Batrat and Ratbat Burrow",
"All The Rave" : "the Castle Top Floor",
"Le Chauve-Souris du Parfum" : "Guano Junction",
"Hands On" : "the Harem",
"You Can Top Our Desserts, But You Can't Beat Our Meats" : "the Laboratory",
"Rokay, Raggy!" : "the Menagerie Level 2",
"A Menacing Phantom" : "the Misspelled Cemetery",
"Lunchboxing" : "the Outskirts of Cobb's Knob",
"Filth, Filth, and More Filth" : "South of the Border",
"It's The Only Way To Be Sure" : "the Deep Fat Friars",
"It's the Only Way to be Sure" : "Pandamonium slums",
"Two Sizes Too Small" : "the Hidden City",
"Some Bricks Do, In Fact, Hang in the Air" : "the Inexplicable Door",
"Blaaargh! Blaaargh!" : "the Spooky Forest",
"Monty of County Crisco" : "Whitey's Grove",
"The Latest Sorcerous Developments" : "the Hippy Camp (pre-war)",
"Sand in the Vaseline" : "the Orcish Frat House (pre-war)",
"Yo Ho Ho and a Bottle of Whatever This Is" : "the Obligatory Pirate's Cove",
"A Tight Squeeze" : "BurnBarrel Blvd.",
"Cold Comfort" : "Exposure Esplanade",
"Juicy!" : "the Heap",
"Flowers for You" : "the Ancient Hobo Burial Ground",
"Maybe It's a Sexy Snake!" : "the Purple Light District",
"What a Tosser" : "Elf Alley",
"A Shark's Chum" : "The Haunted Billiards Room",
"The Time This Fire" : "Vanya's Castle Chapel",
"Oh, <i>There</i> Have it Gone" : "A-Boo Peek",
"Synecdoche, Twin Peak" : "Twin Peak",
"It's a Gas Gas Gas" : "Oil Peak",
"Fit and Finish" : "the Castle Basement",
"Ahead of the Game" : "the Castle Ground Floor",
"Razor, Scooter" : "the Dive Bar",
"Deeps Impact" : "the Briny Deeps",
"The Haggling" : "the Brinier Deepers",
"Camera On, James" : "the Wreck of the Edgar Fitzsimmons",
"Dragon the Line" : "Madness Reef",
"A Drawer of Chests" : "the Mer-Kin Outpost",
"Through the Locking Glass" : "the Hallowed Halls",
"Clay Is Great, But Leather Is Bether" : "Ye Olde Medievale Villagee",
"Where There's Smoke...": "The Copperhead Club",
"Methinks the Protesters Doth Protest Too Little": "A Mob of Zeppelin Protesters",
// fake ones for testing
// "A Dash of Boulder" : "the Dungeons of Doom - Boulder",
// "Meat Score!" : "the Treasury - Meat Score",
},
combats: { // use lowercase
"some bad ascii art" : "the Valley Beyond The Orc Chasm",
"a knob goblin elite guard captain" : "the Cobb's Knob Kitchens",
"a knob goblin embezzler" : "the Treasury",
"a c. h. u. m. chieftain" : "a Maze of Sewer Tunnels",
"baa'baa'bu'ran" : "the Hidden Temple",
"a 7-foot dwarf foreman" : "Itznotyerzitz Mine",
"a moister oyster" : "An Octopus's Garden",
"françois verte, art teacher" : "Art Class", // some encoding issue with this one
"x-fingered shop teacher" : "Shop Class", // need to resolve the X (1-11)
"mrs. k, the chemistry teacher" : "Chemistry class",
"the red fox" : "The Red Zeppelin",
"bram the stoker" : "The Haunted Boiler Room",
"a full-length mirror" : "The Haunted Storage Room",
// fake ones for testing
// "a knob goblin elite guardsman" : "the Treasury",
// "a swarm of killer bees" : "the Dungeons of Doom",
},
};
function clearCounters(playerName) {
GM_setValue(playerName+"_lastSemiTurn",-1);
GM_setValue(playerName+"_lastSemiLocation","");
GM_setValue(playerName+"_luckyTurn1", -1);
GM_setValue(playerName+"_luckyTurn2", -1);
GM_setValue(playerName+"_luckyTurn3", -1);
}
function checkForNoncombatSemi(data) {
theAdv = getAdventureName(document.body.innerHTML);
// alert("in checkForNoncombatSemi(), adv name = " + theAdv);
// alert("semis.noncombats[theAdv] = " + semis.noncombats[theAdv]);
if(semis.noncombats[theAdv] != undefined) {
var playerName = GM_getValue("currentPlayer");
var turncount = GM_getValue(playerName+"_turncount", 0);
// var turncount = getTurnsplayedVar(top.frames[0].document);
// alert("Found semirare adv \'" + theAdv + "\' in area " + semis.noncombats[theAdv] + " on tuncount " + turncount + " for player " + playerName);
// found a semi, clear counters
// this might backfire if using the counter as a generic countdown for other purposes, e.g. wossname tracking
clearCounters(playerName);
GM_setValue(playerName+"_lastSemiTurn",turncount+1); // the turncount var isn't updated yet
GM_setValue(playerName+"_allowBracket",true);
GM_setValue(playerName+"_lastSemiLocation",semis.noncombats[theAdv]);
// trigger char pane refresh with the new info
top.frames[0].location.reload();
}
}
function checkForCombatSemi(data) {
var monster = getMonsterName(document.body.innerHTML);
theAdv = monster.monsterName;
if(monster.fromPutty == false && semis.combats[theAdv] != undefined) {
var playerName = GM_getValue("currentPlayer");
var turncount = GM_getValue(playerName+"_turncount", 0);
// var turncount = getTurnsplayedVar(top.frames[0].document);
// alert("Found semi rare adv \'" + theAdv + "\' in area " + semis.combats[theAdv] + " - for player " + playerName + " on turn " + turncount );
// found a semi, clear counters
// this might backfire if using the counter as a generic countdown for other purposes, e.g. wossname tracking
clearCounters(playerName);
GM_setValue(playerName+"_lastSemiTurn",turncount+1); // the turncount var isn't updated yet
GM_setValue(playerName+"_allowBracket",true);
GM_setValue(playerName+"_lastSemiLocation",semis.combats[theAdv]);
// trigger char pane refresh with the new info
top.frames[0].location.reload();
}
}
////////////////////////////////////////////////////////////////////////////////
// callback function to process the main charsheet as a sanity check after
// something questionable was spotted, or just on a new session
// **** mostly deprecated ****
function sanityCheckCharsheet(data) {
// alert("entering sanity check");
var playerName = getPlayerNameFromCharsheet(data);
GM_setValue("currentPlayer", playerName);
var turncount = getTurnsPlayed(data); // total turns played
if(isNaN(turncount) ) {
// alert("sanityCheckCharsheet - unable to parse either turncount (" + turncount + ") or remaining adv count (" + remainingAdventureCount + "), aborting");
return; // hopefully will try again with more success, can't continue
}
GM_setValue(playerName+"_turncount", turncount); // I'll try to keep this up to date
// if the previous turncount makes no sense, zero it. After ascending would be an example
var lastSemiTurn = GM_getValue(playerName+"_lastSemiTurn",-1);
if( lastSemiTurn > turncount+1) {
GM_setValue(playerName+"_lastSemiTurn",-1);
GM_setValue(playerName+"_lastSemiLocation","");
}
var prevDayCount = GM_getValue(playerName+"_dayCount",-1);
var dayCount = getDaysPlayed(data);
if(!isNaN(dayCount)) {
if(dayCount < prevDayCount) { // must have ascended
GM_setValue(playerName+"_lastSemiTurn",-1);
GM_setValue(playerName+"_lastSemiLocation","");
}
GM_setValue(playerName+"_dayCount",dayCount);
}
// set a flag is we are oxy, since it affects the cookie spacing
if( data.match("Oxygenarian"))
GM_setValue(playerName+"_isOxy", true);
else
GM_setValue(playerName+"_isOxy", false);
}
////////////////////////////////////////////////////////////////////////////////
// watch for the lucky numbers result on the main page
function processMain(doc) {
var countdown1 = -1;
var countdown2 = -1;
var countdown3 = -1;
var pos = -1;
// alert("in processMain()");
///////NEW STUFF (Firvagor) - changed the if line to use the div instead of the body, since the results, and a hidden field is made inside the div so infinite looping doesn't occur//////////
//if the hidden field is found, don't do anything
if (doc.getElementById("cookiedone")!=null){return;}
var playerName = GM_getValue( "currentPlayer");
var divElement = doc.getElementById("effdiv");
var bqElement = doc.getElementsByTagName("blockquote");
// if(bqElement[0])
// alert("processMain: speakeasy quote = " + bqElement[0]);
if ( divElement ) {
// alert("found divElement");
doc.getElementById("effdiv").innerHTML += "<input type='hidden' id='cookiedone' name='completed' value='yes'>";
if((pos = doc.getElementById("effdiv").innerHTML.indexOf( "Lucky numbers:" )) != -1 ) {
// alert("found lucky numbers");
// need to parse the text, format is "Lucky numbers: ###, ###, ###"
var cookieText = doc.getElementById("effdiv").innerHTML.substring(pos, pos+30);
// first one starts after the colon, next two after commas
pos = cookieText.indexOf(":");
cookieText = cookieText.substring(pos+1);
countdown1 = parseInt(cookieText);
pos = cookieText.indexOf(",");
cookieText = cookieText.substring(pos+1);
countdown2 = parseInt(cookieText);
pos = cookieText.indexOf(",");
cookieText = cookieText.substring(pos+1);
countdown3 = parseInt(cookieText);
// elegantly sort them
{
var temp;
if(countdown1 > countdown2) {
temp = countdown1; countdown1 = countdown2; countdown2 = temp;
}
if(countdown2 > countdown3) {
temp = countdown2; countdown2 = countdown3; countdown3 = temp;
}
if(countdown1 > countdown2) {
temp = countdown1; countdown1 = countdown2; countdown2 = temp;
}
}
// alert("processMain: cookie cookieText=" + cookieText + ", lucky numbers are " + countdown1 + ", " + countdown2 + ", " + countdown3);
}
}
// speakeasy drink; format is "burp-speak the number <b>30</b>"
// doesn't work, is there really an effdiv for this? didn't see it in the text
// try a blockquote instead... doesn't sound too reliable
// else if((pos = doc.getElementById("effdiv").innerHTML.indexOf( "burp-speak the number" )) != -1 ) {
if(bqElement[0] && (pos = bqElement[0].innerHTML.indexOf( "burp-speak the number" )) != -1 ) {
// alert("processing speakeasy");
bqElement[0].innerHTML += "<input type='hidden' id='cookiedone' name='completed' value='yes'>";
var cookieText = bqElement[0].innerHTML.substring(pos, pos+30);
// just one number, bolded
pos = cookieText.indexOf("<b>");
cookieText = cookieText.substring(pos+3);
countdown1 = parseInt(cookieText);
countdown2 = -1;
countdown3 = -1;
// alert("processMain: speakeasy cookieText=" + cookieText + ", lucky numbers are " + countdown1 + ", " + countdown2 + ", " + countdown3);
}
if(countdown1>=0 || countdown2>=0 || countdown3>=0) {
// filter out nonsense values:
// - too high ( > 200 since last semi, 120 for oxy)
// - too low ( < 160 since last semi, or < 100 if oxy)
// the filters only apply if you found a semi recently; otherwise the counter starts
// after a would-be semi, or ascension, with your first adventure.php location
// adventure, but it's too much trouble to keep track
GM_setValue(playerName+"_allowSanityCheck", false); // lock out the other pane from panicking
// must call processCharsheet now, to clear this
var allowBracket = GM_getValue(playerName+"_allowBracket",false);
GM_setValue(playerName+"_allowBracket",false); // no reason to put up reminder after this
var turncount = GM_getValue(playerName+"_turncount", 0);
var lastSemiTurn = GM_getValue(playerName+"_lastSemiTurn",-1);
var wasOxy = GM_getValue(playerName+"_isOxy", false);
GM_setValue(playerName+"_isOxy", false); // ate a cookie, must not be oxy
var minCount = 160; var maxCount = 200;
if( wasOxy ) {
minCount = 100; maxCount = 120;
}
if(turncount < 70) { // newly ascended
minCount = 70; maxCount = 80; lastSemiTurn = 0;
}
var turnsSinceSemi = turncount - lastSemiTurn;
// alert("filtering lucky numbers for " + playerName + " based on turncount=" + turncount + ", lastSemiTurn=" + lastSemiTurn + ", wasOxy=" + wasOxy + ", turnsSinceSemi = " + turnsSinceSemi );
// eliminate counters that are too high on an absolute basis, then try to
// eliminate ones too high and low based on last semi
if(countdown1 > maxCount) countdown1 = -1;
if(countdown2 > maxCount) countdown2 = -1;
if(countdown3 > maxCount) countdown3 = -1;
if(lastSemiTurn != -1 && turnsSinceSemi <= maxCount && allowBracket) {
if( countdown1 + turnsSinceSemi > maxCount ) countdown1 = -1;
if( countdown2 + turnsSinceSemi > maxCount ) countdown2 = -1;
if( countdown3 + turnsSinceSemi > maxCount ) countdown3 = -1;
}
if(lastSemiTurn != -1) {
if( countdown1 + turnsSinceSemi < minCount ) countdown1 = -1;
if( countdown2 + turnsSinceSemi < minCount ) countdown2 = -1;
if( countdown3 + turnsSinceSemi < minCount ) countdown3 = -1;
}
// now to compare them vs existing numbers
// if there is no match, stick them in as the current numbers
// if there is a match, use that and zero the other two
var oldLuckyTurn1 = GM_getValue(playerName+"_luckyTurn1",-1);
var oldLuckyTurn2 = GM_getValue(playerName+"_luckyTurn2",-1);
var oldLuckyTurn3 = GM_getValue(playerName+"_luckyTurn3",-1);
var oldCountdown1 = oldLuckyTurn1 != -1 ? oldLuckyTurn1 - turncount : -1;
var oldCountdown2 = oldLuckyTurn2 != -1 ? oldLuckyTurn2 - turncount : -1;
var oldCountdown3 = oldLuckyTurn3 != -1 ? oldLuckyTurn3 - turncount : -1;
var noPopups = GM_getValue("noPopups", false);
// need to watch out for -1 matching -1 since we reset values above
// check for self-match in new cookie values first
if(countdown1 != -1 && (countdown1==countdown2 || countdown1 == countdown3)) {
if(!noPopups) alert(countdown1 + " turns until a semi-rare!");
countdown2 = -1;
countdown3 = -1;
}
else if(countdown2 != -1 && countdown2 == countdown3) {
if(!noPopups) alert(countdown2 + " turns until a semi-rare!");
countdown1 = -1;
countdown3 = -1;
}
else if (countdown1 != -1 && (countdown1 == oldCountdown1 || countdown1 == oldCountdown2 || countdown1 == oldCountdown3 || countdown2 == -1 && countdown3 == -1)) {
if(!noPopups) alert(countdown1 + " turns until a semi-rare!");
countdown2 = -1;
countdown3 = -1;
}
else if (countdown2 != -1 && (countdown2 == oldCountdown1 || countdown2 == oldCountdown2 || countdown2 == oldCountdown3 || countdown1 == -1 && countdown3 == -1)) {
if(!noPopups) alert(countdown2 + " turns until a semi-rare!");
countdown1 = -1;
countdown3 = -1;
}
else if (countdown3 != -1 && (countdown3 == oldCountdown1 || countdown3 == oldCountdown2 || countdown3 == oldCountdown3 || countdown1 == -1 && countdown2 == -1)) {
if(!noPopups) alert(countdown3 + " turns until a semi-rare!");
countdown1 = -1;
countdown2 = -1;
}
GM_setValue(playerName+"_luckyTurn1",countdown1 != -1 ? countdown1 + turncount : -1);
GM_setValue(playerName+"_luckyTurn2",countdown2 != -1 ? countdown2 + turncount : -1);
GM_setValue(playerName+"_luckyTurn3",countdown3 != -1 ? countdown3 + turncount : -1);
// load and parse the main charsheet to set the behind the scene variables that
// convert the cookie countdown into actual adventure number for each cookie
// GM_get(baseURL + charSheet, processCharsheet);
// trigger char pane refresh with the new info
top.frames[0].location.reload();
}
}
////////////////////////////////////////////////////////////////////////////////
function manualCookieEntry() {
var playerName = getPlayerNameFromCharpane().username;
// alert("player name: " + playerName);
GM_setValue("currentPlayer", playerName); // store for other functions that need to know who's playing
var noPopups = GM_getValue("noPopups", false);
if(noPopups) return; // need to figure out some way to enter values without using a popup
var turncount = GM_getValue(playerName+"_turncount", -1);
var luckyTurn1 = GM_getValue(playerName+"_luckyTurn1", -1);
var luckyTurn2 = GM_getValue(playerName+"_luckyTurn2", -1);
var luckyTurn3 = GM_getValue(playerName+"_luckyTurn3", -1);
var oldCountdown1 = luckyTurn1 > 0 ? luckyTurn1 - turncount : -1;
var oldCountdown2 = luckyTurn2 > 0 ? luckyTurn2 - turncount : -1;
var oldCountdown3 = luckyTurn3 > 0 ? luckyTurn3 - turncount : -1;
var countdown1 = -1;
var countdown2 = -1;
var countdown3 = -1;
var displayText = "";
if(oldCountdown1 > -1)
displayText += oldCountdown1;
if(oldCountdown2 > -1) {
if(displayText != "") displayText += ", ";
displayText += oldCountdown2;
}
if(oldCountdown3 > -1) {
if(displayText != "") displayText += ", ";
displayText += oldCountdown3;
}
var lastSemiTurn = GM_getValue(playerName+"_lastSemiTurn",-1);
var lastSemiLocation = GM_getValue(playerName+"_lastSemiLocation","");
var promptText = "";
if(lastSemiTurn > -1 && turncount >= lastSemiTurn ) {
promptText = "Last semirare found " + (turncount - lastSemiTurn) + " turns ago, at " + lastSemiLocation + ".\n\n";
}
else {
promptText = "Last semirare unknown.\n\n"
}
cookieText = prompt(promptText + "Enter 1-3 new cookie vals (-1 to clear)\n", displayText);
if(cookieText) { // will be null if user cancels
countdown1 = parseInt(cookieText);
var pos = cookieText.indexOf(",");
if(pos > 0) {
cookieText = cookieText.substring(pos+1);
countdown2 = parseInt(cookieText);
pos = cookieText.indexOf(",");
if(pos > 0) {
cookieText = cookieText.substring(pos+1);
countdown3 = parseInt(cookieText);
}
}
// alert("manual text entry, got values " + countdown1 + ", " + countdown2 + ", " + countdown3);
GM_setValue(playerName+"_luckyTurn1",countdown1+turncount);
GM_setValue(playerName+"_luckyTurn2",countdown2+turncount);
GM_setValue(playerName+"_luckyTurn3",countdown3+turncount);
GM_setValue(playerName+"_needSanityCheck", false); // just in case we were in the middle of something
GM_setValue(playerName+"_allowBracket",false); // no reason to put up reminder after this
// load and parse the main charsheet to set the behind the scene variables that
// convert the cookie countdown into actual adventure number for each cookie
// GM_get(baseURL + charSheet, processCharsheet);
// trigger char pane refresh with the new info
top.frames[0].location.reload();
}
}
////////////////////////////////////////////////////////////////////////////////
// this function must be called when we are in the char sidepane
function getPlayerLevel(data) {
var playerLevel = /Level (\d+)/i.exec(data); // full mode
if( !playerLevel)
playerLevel = /Lvl\. (\d+)/i.exec(data); // compact mode
if( playerLevel)
return parseInt(playerLevel[1],10);
// normal level checks fail if astral spirit
if(data.indexOf("Astral Spirit") != -1)
return 0; // astral spirit
else
return -1; // error
}
////////////////////////////////////////////////////////////////////////////////
// this function must be called when we are in the char sidepane
function updateCharacterPane() {
var a = getPlayerNameFromCharpane();
var playerName = a.username;
var fullmode = a.fullmode;
if( playerName == null ) // not sure why we sometimes see this, but doesn't seem to be at critical times
return;
GM_setValue("currentPlayer", playerName); // store for other functions that need to know who's playing
// if astral plane, need to reset counters
// getPlayerLevel() returns 0 for astral plane
var playerLevel = getPlayerLevel(document.documentElement.innerHTML);
if(playerLevel == 0) {
// clear the counters, no point in doing anything else
clearCounters(playerName);
return;
}
// check the session ID to see if we are still in the same session
// if a new session, check if an update is available
var pwdHash = getPwdHash(document.documentElement.innerHTML);
var oldPwdHash = GM_getValue(playerName + "_pwdHash", 0);
if(pwdHash != oldPwdHash) {
// new session
GM_setValue(playerName + "_pwdHash", pwdHash);
// run sanity check on new session, make sure we haven't missed anything
// alert("calling sanity check");
GM_get(baseURL + charSheet, sanityCheckCharsheet);
// check for a new version of script if none seen already (asynch call, will run in parallel)
// (temporarily?) removed for use with greasy fork
/* var webVer = GM_getValue("webVersion", "Error");
if(webVer == "Error" || webVer <= currentVersion) {
// alert("calling version check");
GM_get(scriptURL, CheckScriptVersion);
}
*/
}
// new native counter!
var turncount = getTurnsplayedVar(document.documentElement.innerHTML);
var oldTurncount = GM_getValue(playerName+"_turncount", 0);
GM_setValue(playerName+"_turncount", turncount);
var lastSemiTurn = GM_getValue(playerName+"_lastSemiTurn",-1);
var lastSemiLocation = GM_getValue(playerName+"_lastSemiLocation","");
if(lastSemiTurn > turncount + 1) { // turncount var lags behind when fighting, so can have an off-by-1 issue
// could happen if ascended
lastSemiTurn = -1;
GM_setValue(playerName+"_lastSemiTurn",-1);
GM_setValue(playerName+"_lastSemiLocation","");
}
var luckyTurn1 = GM_getValue(playerName+"_luckyTurn1", -1);
var luckyTurn2 = GM_getValue(playerName+"_luckyTurn2", -1);
var luckyTurn3 = GM_getValue(playerName+"_luckyTurn3", -1);
// if anything hit zero, warn the user
var noPopups = GM_getValue("noPopups", false);
var atSemi = false;
if( turncount == luckyTurn1 || turncount == luckyTurn2 || turncount == luckyTurn3) {
atSemi = true;
if(!noPopups && turncount != oldTurncount) {
var alertText = "Fortune cookie countdown hit zero!";
if(lastSemiTurn > -1 && turncount >= lastSemiTurn) {
alertText += "\n\nLast semirare found " + (turncount - lastSemiTurn) + " turns ago, at " + lastSemiLocation + ".";
}
else {
alertText += "\n\nLast semirare unknown.";
}
confirm(alertText);
}
}
var displayText = "";
if(luckyTurn1 >= turncount)
displayText += luckyTurn1 - turncount;
if(luckyTurn2 >= turncount) {
if(displayText != "") displayText += ", ";
displayText += luckyTurn2 - turncount;
}
if(luckyTurn3 >= turncount) {
if(displayText != "") displayText += ", ";
displayText += luckyTurn3 - turncount;
}
if(atSemi && noPopups)
displayText += " (last SR: " + (lastSemiLocation == "" ? "unknown" : lastSemiLocation) + ")";
if(atSemi && fullmode) { // shows the html literally in compact mode
displayText = "<font color=\"red\"><b>" + displayText + "</b></font>";
}
// display turn var to figure out exactly how it works
// displayText += " (tp=" + turnsplayedVar+")";
var hideIfZeros = GM_getValue("hideIfZeros", false);
if(displayText != ""
|| hideIfZeros == false ) {
// display an FC bracket if no cookie eaten, and have found a semi recently
var isOxy = GM_getValue(playerName+"_isOxy", false);
var turnsSinceSemi = turncount - lastSemiTurn;
var minCount = 160; var maxCount = 200;
if( isOxy ) {
minCount = 100; maxCount = 120;
}
if(displayText == "" && lastSemiTurn != -1 && turnsSinceSemi <= maxCount ) {
displayText = "[" + Math.max(0, minCount - turnsSinceSemi) + "...";
displayText += Math.max(0, maxCount - turnsSinceSemi) + "]";
} else if (displayText == "" && turncount < 70) { // default just-ascended bracket
displayText = "[" + Math.max(0, 70 - turncount) + "...";
displayText += Math.max(0, 80 - turncount) + "]";
}
if(fullmode) {
var newElement = document.createElement("FONT");
newElement.innerHTML = "<b>"
+ "<font size=2>Cookie Count: "
+ displayText
+ "</font></b><br><br>"; ;
newElement.setAttribute("onmouseover", 'this.style.opacity="0.5"');
newElement.setAttribute("onmouseout", 'this.style.opacity="1"');
newElement.setAttribute("id", 'fortuneCookieCounter');
newElement.addEventListener("click", manualCookieEntry, true);
newElement.align = "center";
var elements = document.getElementsByTagName( "FONT" );
for ( var i = 0; i < elements.length; ++i ){
if ( elements[i].innerHTML.indexOf( "Last Adventure" ) != -1 ){
// insert our before this one
elements[i].parentNode.insertBefore(newElement,elements[i]);
break;
}
}
}
else { // compact mode - different layout, make a table row and two data element
var newTR = document.createElement('tr');
var newElement = document.createElement("td");
newElement.appendChild(document.createTextNode("Cookie:"));
newElement.align = "right";
newTR.appendChild(newElement);
newElement = document.createElement("td");
newElement.setAttribute("onmouseover", 'this.style.opacity="0.5"');
newElement.setAttribute("onmouseout", 'this.style.opacity="1"');
newElement.setAttribute("id", 'fortuneCookieCounter');
newElement.addEventListener("click", manualCookieEntry, true);
newElement.align = "left";
newElement.style.fontWeight = "bold";
newElement.appendChild(document.createTextNode(displayText));
newTR.appendChild(newElement);
var elements = document.getElementsByTagName( "TR" );
var done = false;
// if the last adventures script is running, insert before, else append to table
for ( var i = 1; i < elements.length; ++i ){
// normally "Adv", might be "Last Adventures" if that script is running
if ( elements[i].innerHTML.indexOf( "Last Adventures" ) != -1 ){
// insert ours before this one - experiment, back up one more
elements[i].parentNode.insertBefore(newTR,elements[i-1]);
done = true;
break;
}
}
if(!done) { // normal, no last adv script
for ( var i = 0; i < elements.length; ++i ){
if ( elements[i].innerHTML.indexOf( "Adv" ) != -1 ){
// insert ours at end of the table in compact mode
elements[i].parentNode.appendChild(newTR);
break;
}
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// set preferences
// code stolen from mr script
// not set on a per-player basis, would need to add code to parse account pane for char name
function toggleHideCountdown() {
// alert("in toggleHideCountdown()");
var hideIfZeros = GM_getValue("hideIfZeros", false);
hideIfZeros = !hideIfZeros;
GM_setValue("hideIfZeros", hideIfZeros);
// trigger char pane refresh with the new info
top.frames[0].location.reload();
var msg = document.getElementById('cookiecountertoggle');
msg.innerHTML = (hideIfZeros ? "Hiding" : "Showing") + " Inactive Counter - Click to Change";
}
function togglePopups() {
// alert("in toggleHideCountdown()");
var noPopups = GM_getValue("noPopups", false);
noPopups = !noPopups;
GM_setValue("noPopups", noPopups);
// trigger char pane refresh with the new info
var msg = document.getElementById('cookiepopups');
msg.innerHTML = (noPopups ? "Popups Disabled " : "Using Popups") + " (disable for FF4) - Click to Change";
top.frames[0].location.reload();
}
// --------------------------------------------
// ---------- account menu option -------------
// --------------------------------------------
// Charon's code
function buildPrefs() {
if (!document.querySelector('#privacy'))
return;
if (!document.querySelector('#scripts')) {
//scripts tab is not built, do it here
var scripts = document.querySelector('ul').appendChild(document.createElement('li'));
scripts.id = 'scripts';
var a = scripts.appendChild(document.createElement('a'));
a.href = '#';
var img = a.appendChild(document.createElement('img'));
img.src = 'http://images.kingdomofloathing.com/itemimages/cmonkey1.gif';
img.align = 'absmiddle';
img.border = '0';
img.style.paddingRight = '10px';
a.appendChild(document.createTextNode('Scripts'));
a.addEventListener('click', function (e) {
//make our new tab active when clicked, clear out the #guts div and add our settings to it
e.stopPropagation();
document.querySelector('.active').className = '';
document.querySelector('#scripts').className = 'active';
document.querySelector('#guts').innerHTML = '<div class="scaffold"></div>';
document.querySelector('#guts').appendChild(getSettings());
}, false);
} else {
//script tab already exists
document.querySelector('#scripts').firstChild.addEventListener('click', function (e) {
//some other script is doing the activation work, just add our settings
e.stopPropagation();
document.querySelector('#guts').appendChild(getSettings());
}, false);
}
}
function getSettings() {
//build our settings and return them for appending
var guts = document.body.appendChild(document.createElement('div'));
guts.id = 'cookieprefs';
var subhead = guts.appendChild(document.createElement('div'));
subhead.className = 'subhead';
subhead.textContent = 'Fortune Cookie Counter';
var section = guts.appendChild(document.createElement('div'));
section.className = 'indent';
//call function in main script to actually make the settings
section.appendChild(buildSettings());
return guts;
}
function buildSettings() {
var prefLink = document.createElement('a');
var hideIfZeros = GM_getValue("hideIfZeros", false);
prefLink.setAttribute('href','javascript:void(0)');
prefLink.setAttribute('id','cookiecountertoggle');
prefLink.innerHTML = (hideIfZeros ? "Hiding" : "Showing") + " Inactive Counter - Click to Change";
prefLink.addEventListener("click", toggleHideCountdown, true);
var prefAnchor = document.createElement('a');
prefAnchor.setAttribute('name','opt'); prefAnchor.innerHTML = " ";
var pDiddy = document.createElement('p');
with(pDiddy)
{ appendChild(prefAnchor);
appendChild(prefLink);
}
prefLink = document.createElement('a');
var noPopups = GM_getValue("noPopups", false);
prefLink.setAttribute('href','javascript:void(0)');
prefLink.setAttribute('id','cookiepopups');
prefLink.innerHTML = (noPopups ? "Popups Disabled " : "Using Popups") + " (disable for FF4) - Click to Change";
prefLink.addEventListener("click", togglePopups, true);
prefAnchor = document.createElement('p');
prefAnchor.setAttribute('name','opt'); prefAnchor.innerHTML = " ";
with(pDiddy)
{ appendChild(prefAnchor);
appendChild(prefLink);
}
// add a link to update, if an update is available
// (temporarily?) removed for use with Greasy Fork
/* var webVer = GM_getValue("webVersion");
if (webVer != "Error" && webVer > currentVersion) { // this is actually a text string comparison, not numerical
var newElement = document.createElement('p');
newElement.style.fontSize = "x-small";
newElement.appendChild(document.createTextNode("New cookie script version " + webVer + " available: "));
var hrefElement = document.createElement('a');
hrefElement.setAttribute('href', scriptSite);
hrefElement.setAttribute('target', "_blank");
hrefElement.appendChild(document.createTextNode("here"));
newElement.appendChild(hrefElement);
pDiddy.appendChild(newElement);
}
*/
return pDiddy;
}
////////////////////////////////////////////////////////////////////////////////
// main prog, just call the proper routine if we are on a pane we care about
var nodeBody = document.getElementsByTagName("body").item(0);
var textBody = "";
var baseURL = "";
var charSheet = "charsheet.php";
var playerName = GM_getValue( "currentPlayer");
if (nodeBody) {
if (nodeBody.textContent) {
textBody = nodeBody.textContent;
}
else if (nodeBody.text) {
textBody = nodeBody.text;
}
baseURL = nodeBody.baseURI.substring(0,nodeBody.baseURI.lastIndexOf('/')+1);
}
// do normal page processing independently of whether started sanity check
if ( document.location.pathname.indexOf("charpane.php") != -1 ) {
//////NEW STUFF (Firvagor) - since the charpane refreshes every time something is used, if the results section is found, run main function using manually located mainpane (can only use indexing for some reason)/////
if (top.frames[2].document.getElementById("effdiv")!=null) // was frames[1] until aug 2014 topmenu update
processMain(top.frames[2].document);
updateCharacterPane();
}
else if ( document.location.pathname.indexOf("inventory.php") != -1
|| document.location.pathname.indexOf("clan_viplounge.php")!= -1){
//alert("calling processMain() for " + document.location.pathname);
//////NEW STUFF (Firvagor) - passes in current document instead of nothing, just to define the frame used/////
GM_setValue(playerName+"_fighting",0);
processMain(document);
}
else if ( document.location.pathname.indexOf("charsheet.php") != -1){
// alert("calling sanityCheckCharsheet() for " + document.location.pathname);
GM_setValue(playerName+"_fighting",0);
sanityCheckCharsheet(document.body.innerHTML);
}
else if ( document.location.pathname.indexOf("account.php") != -1 ) {
// alert("calling set prefs for " + document.location.pathname);
GM_setValue(playerName+"_fighting",0);
buildPrefs();
// setPrefs();
}
else if ( document.location.pathname.indexOf("adventure.php") != -1
|| document.location.pathname.indexOf("choice.php") != -1) {
// adventure.php should trap all "normal" noncombats
// choice noncombats are in choice.php - now needed for hobopolis
// for the fortune cookie combat rares, need to process fight.php
GM_setValue(playerName+"_fighting",0);
checkForNoncombatSemi(document.body.innerHTML);
}
else if ( document.location.pathname.indexOf("fight.php") != -1 ) {
// see if we are actively fighting (no asynch calls allowed while fighting!)
var fighting = GM_getValue(playerName+"_fighting",0)
// alert("prefight check - page = " + document.location.pathname + ", fighting = " + fighting);
{
if( fighting == 0) {
// just started a fight, see if it's a combat semi (orc chasm, kitchens, treasury)
checkForCombatSemi(document.body.innerHTML);
}
// alert("incrementing fighting from " + fighting);
fighting = fighting + 1; // also count rounds, prevents doing things twice in same fight
// fighting, but maybe we're done, look for adventure again tag
// I forget why we look at paragraphs here!
// var paragraphs = document.getElementsByTagName( "p" );
// for ( var i = 0; i < paragraphs.length; ++i ) {
// if ( paragraphs[i].innerHTML.indexOf( "Adventure Again (" ) != -1
// || paragraphs[i].innerHTML.indexOf("Go back to Your Inventory") != -1) {
if ( document.body.innerHTML.indexOf( "Adventure Again (" ) != -1
|| document.body.innerHTML.indexOf("Go back to Your Inventory") != -1) {
fighting = 0;
// break;
}
// }
}
GM_setValue(playerName+"_fighting",fighting);
// alert("post fight - fighting = " + fighting);
}
else {
// alert("ignoring " + document.location.pathname);
}
// do sanity check (load charsheet asynchronously) if requested - not while fighting
if(GM_getValue(playerName+"_needSanityCheck",false) == true
&& GM_getValue(playerName+"_allowSanityCheck",false) == true ) {
// alert("need sanity check flag detected!");
// no sanity check allowed if fighting
if( GM_getValue(playerName+"_fighting",0) == 0) {
// alert("calling sanity check");
GM_setValue(playerName+"_needSanityCheck", false);
// alert("calling sanity check");
GM_get(baseURL + charSheet, sanityCheckCharsheet);
}
}