monster manuel

Version 0.1.7 - The [], spring break monsters

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// antimarty's monster manuel helper - pop up monster info for the monster you are fighting
//
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
//
// ==UserScript==
// @name	   monster manuel
// @namespace	antimarty
// @include	   *kingdomofloathing.com/fight.php*
// @include	   *127.0.0.1:600*/fight.php*
// @include	   *localhost:*/fight.php*
// @include	   *kingdomofloathing.com/questlog.php.php*
// @include	   *127.0.0.1:600*/questlog.php*
// @include	   *localhost:*/questlog.php*
// @version		0.1.7
// @grant       GM_getValue
// @grant       GM_setValue
// @grant		GM_xmlhttpRequest
// 
// @description	   Version 0.1.7 - The [], spring break monsters
//
// ==/UserScript==

// released versions:
// Version 0.0.1 - first try
// Version 0.0.2 - fixes for special case monster names
// Version 0.0.3 - more fixes, add update link, general cleanup
// Version 0.0.4 - fernswarthy monster fixes
// Version 0.0.5 - slime tube and hobo monsters, haunted sorority monsters
// Version 0.0.6 - redo popup for non-mafia
// Version 0.0.7 - clean up some minor error handling stuff
// Version 0.0.8 - crimbo 2012 - taco elves and crimbokutown workers
// Version 0.0.9 - "The" Cray-kin, other jar of psychoses stuff, game inform monsters
// Version 0.1.0 - "The" Sierpinski Brothers, "The" Server, mafia wants a password
// Version 0.1.1 - password bug fix
// Version 0.1.2 - mostly undo the password bug fix, and a few things like angry angry bugbears
// Version 0.1.3 - add custom monster entry, then remove it. Plus more names-from-images.
// Version 0.1.4 - dreadsylvania monsters, fixes for GM/Firefox updates
// Version 0.1.5 - add junksprites, halloween monsters

// Known bugs:
// - shows all factoids for multiple monsters with the same name

/* var currentVersion = "0.1.6";
var scriptSite = "http://userscripts.org/scripts/show/150102"
// this is a small file autogenerated by userscripts.org from Userscript @ comments above, use to reduce bandwidth on version check
var scriptURL = "http://userscripts.org/scripts/source/150102.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 for an updated script version
function CheckScriptVersion(data)
{
    // 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 failed = {'username': "", 'fullmode': true};
	
	if (!top.frames || !top.frames[0]) return failed;
	var username = top.frames[0].document.getElementsByTagName("b");
	
	if (!username || username.length < 1) return failed;
	username = username[0];
	if (!username) return failed;
	username = username.firstChild;
	if (!username) return failed;
	// 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 failed;
	username = username.nodeValue;
	if (!username) return failed;
	username = username.toLowerCase();
//	alert("found username " + username + ", fullmode: " + fullmode);
	return {'username': username, 'fullmode': fullmode};
}

// don't strip "The" (and maybe other stuff tbd) from the names of these
var invariableMonsterNames = [
	"El Diablo",
	"five skeleton invaders", // this one shares an image name with procedural stuff
	"The Avatar of Sneaky Pete",
	"The Bat in the Spats",
	"The Beefhemoth",
	"The Big Wisniewski",
	"The Clownlord Beelzebozo",
	"The Cray-Kin",
	"The Frattlesnake",
	"The Free Man",
	"the ghost of Phil Bunion",
	"the gunk",
	"The Landscaper",
	"The Large-Bellied Snitch",
	"The Man",
	"The Sierpinski brothers",
	"The Server",
	"The Snake With Like Ten Heads",
	"The Terrible Pinch",
	"The Thing with No Name",
	"The Thorax",
	"The Unkillable Skeleton"];

// for monsters with name generators, or easier to look at the image names
var monsterImages = {
	"sororghost": "sexy sorority ghost", 
	"sororeton": "sexy sorority skeleton",
	"sororpire": "sexy sorority vampire",
	"sororwolf": "sexy sorority werewolf",
	"sororbie": "sexy sorority zombie",
	"coldhobo": "Cold hobo",
	"hothobo": "Hot hobo",
	"nhobo": "Normal hobo",
	"slhobo": "Sleaze hobo",
	"spookyhobo": "Spooky hobo",
	"stenchhobo": "Stench hobo",
	"elfhobo": "Hobelf",
	"animelf1": "tiny-screwing animelf",
	"animelf2": "plastic-extruding animelf",
	"animelf3": "circuit-soldering animelf",
	"animelf4": "quality control animelf",
	"animelf5": "toy assembling animelf",
	"beergolem": "X Bottles of Beer on a Golem",
	"stonegolem": "X Stone Golem",
	"dimhorror":" X-dimensional horror",
	"hydra": "X-headed Hydra",
	"earbeast": "Beast with X Ears",
	"eyebeast": "Beast with X Eyes",
	"fernghost": "Ghost of Fernswarthy's Grandfather",
	"tacoelf_sign":	"sign-twirling Crimbo elf",
	"tacoelf_taco": "taco-clad Crimbo elf",
	"tacoelf_cart":	"tacobuilding elf",
	"bigskeleton": "procedurally-generated skeleton",
	"faq_miniboss": "Video Game Miniboss",
	"faq_boss": "Video Game Boss",
	"faq_": "Video Game Minion",  // no convenient way to tell strong vs. weak vs. moderate
	"bb_caveman": "angry cavebugbear",  // and all the very very very... angry ones, too
	"dvhotbear": "hot bugbear",
	"dvcoldbear": "cold bugbear",
	"dvspookybear":  "spooky bugbear",
	"dvsleazebear": "sleaze bugbear",
	"dvstenchbear": "stench bugbear",
	"dvhotghost": "hot ghost",
	"dvcoldghost": "cold ghost",
	"dvspookyghost":  "spooky ghost",
	"dvsleazeghost": "sleaze ghost",
	"dvstenchghost": "stench ghost",
	"dvhotskel": "hot skeleton",
	"dvcoldskel": "cold skeleton",
	"dvspookyskel":  "spooky skeleton",
	"dvsleazeskel": "sleaze skeleton",
	"dvstenchskel": "stench skeleton",
	"dvhotvamp": "hot vampire",
	"dvcoldvamp": "cold vampire",
	"dvspookyvamp":  "spooky vampire",
	"dvsleazevamp": "sleaze vampire",
	"dvstenchvamp": "stench vampire",
	"dvhotwolf": "hot werewolf",
	"dvcoldwolf": "cold werewolf",
	"dvspookywolf":  "spooky werewolf",
	"dvsleazewolf": "sleaze werewolf",
	"dvstenchwolf": "stench werewolf",
	"dvhotzom": "hot zombie",
	"dvcoldzom": "cold zombie",
	"dvspookyzom":  "spooky zombie",
	"dvsleazezom": "sleaze zombie",
	"dvstenchzom": "stench zombie",
	"shopteacher": "X-fingered Shop Teacher",
	"js_bender":	"junksprite bender",
	"js_melter":	"junksprite melter",
	"js_sharpener":	"junksprite sharpener",
	"vandalkid": "vandal kid",
	"paulblart":	"suburban security civilian",
	// sloppy seconds sundae, spring break sunken party
	"ssd_cocktail": "Sloppy Seconds Cocktail",
	"ssd_sundae": "Sloppy Seconds Sundae",
	"ssd_burger":	"Sloppy Seconds Burger",
	"fun-gal": "Fun-Guy Playmate",
	"srpainting": "ancestral Spookyraven portrait"
};	  
	
////////////////////////////////////////////////////////////////////////////////
// get monster name (monster manuel version)
function getMonsterName(data) {
	var monsterName = /id=\"monname\"> *(.*?)<\/span>/i.exec(data);
	if(monsterName) 
		monsterName = monsterName[1];
	else 
		return "";
	var imageName = /adventureimages\/(.*?)\.gif/i.exec(data);
	if(imageName) 
		imageName = imageName[1];
	else 
		return "";
// alert("initially found monster name: " + 	monsterName + " (image name: " + imageName + ")");
		
	// strip off leading articles, etc from monster names as seen during fight
	// maybe make this a list of prefixes, it's getting pretty long
	if(invariableMonsterNames.indexOf(monsterName) == -1) {
		if( monsterName.substring(0,2).toLowerCase()=="a ")
		  monsterName = monsterName.substring(2,monsterName.length);
		else if( monsterName.substring(0,3).toLowerCase()=="an ")
		  monsterName = monsterName.substring(3,monsterName.length);
		else if( monsterName.substring(0,5).toLowerCase()=="some ")
		  monsterName = monsterName.substring(5,monsterName.length);
		else if( monsterName.substring(0,4).toLowerCase()=="the ")
		  monsterName = monsterName.substring(4,monsterName.length);
		else if( monsterName.substring(0,3).toLowerCase()=="el ")
		  monsterName = monsterName.substring(3,monsterName.length);
		else if( monsterName.substring(0,3).toLowerCase()=="la ")
		  monsterName = monsterName.substring(3,monsterName.length);

		// translate special cases
		if(monsterName.indexOf("'s butt") != -1)
			monsterName = "[somebody else's butt]";
		else if(monsterName.substring(0,6).toLowerCase()=="shadow" && monsterName.indexOf("Black Bubbles")==-1 && imageName.indexOf("faq_")==-1)
			monsterName = "(shadow opponent)";
		// all slime tube monsters are... "Slime Tube monster", ignore names, just check for the image
		else if(/slime[1-5]/i.exec(imageName) != null )
			monsterName = "Slime Tube monster";
		// more monsters-from-images
		// keep trailing numbers for anime elves (different monsters), strip otherwise (different images for same mob)
		else if(/animelf[1-5]/i.exec(imageName) != null )
			monsterName = monsterImages[imageName];
		else {
			var stripped = /(.*?)[0-9]+/i.exec(imageName);
			if(stripped != null) imageName=stripped[1];
			if(monsterImages[imageName] != undefined)
				monsterName = monsterImages[imageName];
			else if(imageName.substring(0,4).toLowerCase()=="faq_")  // video game minions, how to tell strong vs. weak?
				monsterName = monsterImages["faq_"];
		}
	}

 //  alert("returning monster name: " + 	monsterName + " (image name: " + imageName + ")");
	return monsterName;
}
	
// from one ton tomato's mallsearch script
function getParent(el, pTagName) {
	if (el == null) {
		return null;
	} else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) {	// Gecko bug, supposed to be uppercase
		return el;
	} else {
		return getParent(el.parentNode, pTagName);
	}
}

// fill my popup window with a reduced version of manuels facts, for our monster only
function processManual(data){
	var myDocument = document.createElement('div');
	myDocument.innerHTML = data;
	
	// could just get monsterName directly, this is a relic from earlier versions
	var monsterName = /\&monster=(.+)/i.exec(decodeURIComponent(manuelURL));  
	if(monsterName) monsterName = monsterName[1];
 // alert("monster name = " + monsterName);

	// first find the table with our monster; find its parent; use that to find all the monster tables
	if(monsterName) {
		// strip out the javascript href change that loads the game main page
		var allscripts = myDocument.getElementsByTagName("script");
		allscripts[0].parentNode.removeChild(allscripts[0]);
	
		var allElements = myDocument.getElementsByTagName("font");
		var parentNode = 0;
		var monsterTables =0;
		var i;
		for(i=0; i < allElements.length; i++) {
			if(allElements[i].innerHTML.indexOf(monsterName) != -1) {
				parentNode = getParent(allElements[i], "table");
				parentNode = getParent(parentNode, "p");
				monsterTables = parentNode.getElementsByTagName("table");
				break;
			}	 
		}

		// now delete all that parent's children, except our keeper monster
		// monsterTables is a live list, so as we delete items in the doc, list items disappear...
		if(monsterTables) {
			for(i=0; i < monsterTables.length; ) {
				// use the <font></font> tags to eliminate false matches in random text. Harem guards
				// is an example. (why did I change this?)
//				if(monsterTables[i].innerHTML.indexOf(">" + monsterName + "</font>") > -1) {
				if(monsterTables[i].innerHTML.indexOf(">" + monsterName + "<") >= 0
				|| monsterName=="Video Game Minion" && monsterTables[i].innerHTML.indexOf(">" + monsterName)>= 0) {
					// our monster, skip it
					i++;
				}
				else {
					// not our monster, delete it (next target monster table will be at same index)
					// there is also an <a> node in front of each monster
					monsterTables[i].parentNode.removeChild(monsterTables[i].previousSibling);
					monsterTables[i].parentNode.removeChild(monsterTables[i]);
				}
			}
		}
		// delete some junk stuff - other quests header info, back to campsite footer, etc
		if(parentNode.parentNode) {
			parentNode.parentNode.removeChild(parentNode.parentNode.firstChild);
			parentNode.parentNode.removeChild(parentNode.parentNode.firstChild);
			parentNode.parentNode.removeChild(parentNode.parentNode.firstChild);
			parentNode.parentNode.removeChild(parentNode.parentNode.lastChild);
			parentNode.parentNode.removeChild(parentNode.parentNode.lastChild);
		}
		
/* 		// if new script version available, add a link
		var webVer = GM_getValue("webVersion", "Error");
		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 Monster Manuel 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);
			
			parentNode.parentNode.appendChild(newElement); 
		}
 */
		// and finally fill our window
		myWindow.document.documentElement.innerHTML = myDocument.innerHTML;
		myWindow.document.close();
	}
}

// pop up an empty window for our factoid, and call something to fill it
// to do: figure out how to size this appropriately
var myWindow;
function manuelPopup()
{
	myWindow = window.open(baseURL,'manuel','height=400,width=500,scrollbars=yes');
	GM_get(baseURL + manuelURL, processManual);
}

// parse the charpane info for the password hash (use as a session ID)
function getPwdHash(data){
    var pwdHash = /pwdhash \= \"(.*?)\"/i.exec(data); // the .*? is the non-greedy version of .*
	if(pwdHash)
		pwdHash = pwdHash[1];
	else
		pwdHash = "";

	return pwdHash;
}

////////////////////////////////////////////////////////////////////////////////
function monsterNameEntry() {
	var monsterName = prompt("Enter Monster Name to research:\n", "");
	var firstChar = monsterName.toLowerCase().charAt(0) ;
	if(firstChar < 'a' || firstChar > 'z') firstChar = '-';

	manuelURL = "questlog.php?which=6&vl=" + firstChar + "&monster="+monsterName;
	manuelPopup();
}
////////////////////////////////////////////////////////////////////////////////
// currently unused - not really helpful to search unless can cross first-letter sections
function processQuestPage() {
	var entries = document.getElementsByTagName("b");
	var i;
	for(i=0; i < entries.length; i++) {
		if(entries[i].innerHTML == "Other") {
			// append our item here
			var newElement = entries[i].parentNode.parentNode.appendChild(document.createElement('b'));
			newElement.setAttribute("onmouseover", 'this.style.opacity="0.5"');
			newElement.setAttribute("onmouseout", 'this.style.opacity="1"');
			newElement.innerHTML = "&nbsp;Search";
			newElement.addEventListener("click", monsterNameEntry);
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
function processFight() {
	var playerName = getPlayerNameFromCharpane().username;
	var pwdHash = "";
	
	// once per login, check if a new version is available
	// why all this top level stuff sometimes fails at each level, who knows
	if(playerName && top && top.frames[0] && top.frames[0].document && top.frames[0].document.documentElement) {
		pwdHash = getPwdHash(top.frames[0].document.documentElement.innerHTML);
		var oldPwdHash = GM_getValue(playerName + "_pwdHash", 0);
		if(pwdHash != oldPwdHash) {
			// new session
			GM_setValue(playerName + "_pwdHash", pwdHash);
			
/* 			// check for a new version of script if none seen already
			var webVer = GM_getValue("webVersion", "Error");
			if(webVer == "Error" || webVer <= currentVersion)
				GM_get(scriptURL, CheckScriptVersion);
 */		
		}
		// clear any lingering flag that was set with empty player name by old script versions
		GM_setValue("_hasManuel", false);
	}
	
	var monsterName = getMonsterName(document.body.innerHTML);
	var firstChar = monsterName.toLowerCase().charAt(0) ;
	if(firstChar < 'a' || firstChar > 'z') firstChar = '-';
	// apparently mafia now wants a password
	// yay, that change got reverted, and was causing problems anyway
//	manuelURL = "questlog.php?which=6&vl=" + firstChar + "&pwd=" + pwdHash + "&monster="+monsterName;
	manuelURL = "questlog.php?which=6&vl=" + firstChar + "&monster="+monsterName;

	// find the monster's HTML element on the page; add our link
	var monsterSpan = document.getElementById("monname");
	var monsterTable = getParent(monsterSpan, "table");
	if(monsterSpan && monsterTable) {
		var newElement = document.createElement("FONT");
		// make sure we have a factoid to popup - signalled by the attack/defense/hp display
		// or by monster manuel saying something
		if(monsterTable.innerHTML.indexOf("Enemy's Attack Power") != -1
		|| document.body.innerHTML.indexOf("Monster Manuel") != -1 ){
			if(playerName != "")
				GM_setValue(playerName+"_hasManuel", true);  // current monster has factoids, ergo player has manuel

			newElement.innerHTML = "<font size=2> (<u>factoids</u>)</font>"; ;
			newElement.setAttribute("onmouseover", 'this.style.opacity="0.5"');
			newElement.setAttribute("onmouseout", 'this.style.opacity="1"');
			newElement.setAttribute("id", 'manuel');
			newElement.addEventListener("click", manuelPopup, true);
			// always insert a factoid link if there are factoids
			monsterSpan.parentNode.insertBefore(newElement, monsterSpan.nextSibling);
		} else {
			newElement.innerHTML = "<font size=2> (no factoids)</font>"; ;
			newElement.setAttribute("onmouseout", 'this.style.opacity="0.5"');
			newElement.setAttribute("id", 'manuel');
			// no factoids, only insert the link if we know the player has manuel
			if(GM_getValue(playerName+"_hasManuel", false) == true ) 
				monsterSpan.parentNode.insertBefore(newElement, monsterSpan.nextSibling);
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// main prog, just call the proper routine if we are on a pane we care about
var nodeBody   = document.getElementsByTagName("body").item(0);
var baseURL	   = "";
var manuelURL = "";

if (nodeBody) {
   baseURL = nodeBody.baseURI.substring(0,nodeBody.baseURI.lastIndexOf('/')+1);
}

// our popup gets named after the originating window, which is fight.php, so don't process it
if(window.name != "manuel" && document.location.pathname.indexOf("fight.php") != -1 ) {
	processFight();
}
else if(document.location.pathname.indexOf("questlog.php") != -1 ) {
	// deprecated until I can figure out how to search across different first letters
//	processQuestPage();
}