monster manuel

Version 0.1.7 - The [], spring break monsters

  1. // antimarty's monster manuel helper - pop up monster info for the monster you are fighting
  2. //
  3. // Released under the GPL license
  4. // http://www.gnu.org/copyleft/gpl.html
  5. //
  6. // ==UserScript==
  7. // @name monster manuel
  8. // @namespace antimarty
  9. // @include *kingdomofloathing.com/fight.php*
  10. // @include *127.0.0.1:600*/fight.php*
  11. // @include *localhost:*/fight.php*
  12. // @include *kingdomofloathing.com/questlog.php.php*
  13. // @include *127.0.0.1:600*/questlog.php*
  14. // @include *localhost:*/questlog.php*
  15. // @version 0.1.7
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @grant GM_xmlhttpRequest
  19. //
  20. // @description Version 0.1.7 - The [], spring break monsters
  21. //
  22. // ==/UserScript==
  23.  
  24. // released versions:
  25. // Version 0.0.1 - first try
  26. // Version 0.0.2 - fixes for special case monster names
  27. // Version 0.0.3 - more fixes, add update link, general cleanup
  28. // Version 0.0.4 - fernswarthy monster fixes
  29. // Version 0.0.5 - slime tube and hobo monsters, haunted sorority monsters
  30. // Version 0.0.6 - redo popup for non-mafia
  31. // Version 0.0.7 - clean up some minor error handling stuff
  32. // Version 0.0.8 - crimbo 2012 - taco elves and crimbokutown workers
  33. // Version 0.0.9 - "The" Cray-kin, other jar of psychoses stuff, game inform monsters
  34. // Version 0.1.0 - "The" Sierpinski Brothers, "The" Server, mafia wants a password
  35. // Version 0.1.1 - password bug fix
  36. // Version 0.1.2 - mostly undo the password bug fix, and a few things like angry angry bugbears
  37. // Version 0.1.3 - add custom monster entry, then remove it. Plus more names-from-images.
  38. // Version 0.1.4 - dreadsylvania monsters, fixes for GM/Firefox updates
  39. // Version 0.1.5 - add junksprites, halloween monsters
  40.  
  41. // Known bugs:
  42. // - shows all factoids for multiple monsters with the same name
  43.  
  44. /* var currentVersion = "0.1.6";
  45. var scriptSite = "http://userscripts.org/scripts/show/150102"
  46. // this is a small file autogenerated by userscripts.org from Userscript @ comments above, use to reduce bandwidth on version check
  47. var scriptURL = "http://userscripts.org/scripts/source/150102.meta.js";
  48. */
  49. ////////////////////////////////////////////////////////////////////////////////
  50. // Based on a function taken from OneTonTomato's UpUp skill script
  51. function GM_get(target, callback) {
  52. GM_xmlhttpRequest({
  53. method: 'GET',
  54. url: target,
  55. onload:function(details) {
  56. if( typeof callback=='function' ){
  57. callback(details.responseText);
  58. }
  59. }
  60. });
  61. }
  62.  
  63. // Check for an updated script version
  64. function CheckScriptVersion(data)
  65. {
  66. // Preemptively set error, in case request fails...
  67. GM_setValue("webVersion", "Error")
  68.  
  69. var m = data.match(/@version\s*([0-9.]+)/);
  70. if (m)
  71. {
  72. GM_setValue("webVersion", m[1]);
  73. }
  74. }
  75.  
  76. ////////////////////////////////////////////////////////////////////////////////
  77. // parse the char pane for the player name
  78. // revised version! now taken directly from kolpreviousnadventures to handle compact mode
  79. function getPlayerNameFromCharpane() {
  80. var failed = {'username': "", 'fullmode': true};
  81. if (!top.frames || !top.frames[0]) return failed;
  82. var username = top.frames[0].document.getElementsByTagName("b");
  83. if (!username || username.length < 1) return failed;
  84. username = username[0];
  85. if (!username) return failed;
  86. username = username.firstChild;
  87. if (!username) return failed;
  88. // in full mode the link is <a><b>Name</b></a>
  89. // in compact mode it's <b><a>Name</a></b>
  90. // so have to handle this, and also can use it to tell
  91. // whether it's in compact mode or not.
  92. var fullmode = true;
  93. while (username && username.nodeType == 1)
  94. {
  95. username = username.firstChild;
  96. fullmode = false;
  97. }
  98. if (!username) return failed;
  99. username = username.nodeValue;
  100. if (!username) return failed;
  101. username = username.toLowerCase();
  102. // alert("found username " + username + ", fullmode: " + fullmode);
  103. return {'username': username, 'fullmode': fullmode};
  104. }
  105.  
  106. // don't strip "The" (and maybe other stuff tbd) from the names of these
  107. var invariableMonsterNames = [
  108. "El Diablo",
  109. "five skeleton invaders", // this one shares an image name with procedural stuff
  110. "The Avatar of Sneaky Pete",
  111. "The Bat in the Spats",
  112. "The Beefhemoth",
  113. "The Big Wisniewski",
  114. "The Clownlord Beelzebozo",
  115. "The Cray-Kin",
  116. "The Frattlesnake",
  117. "The Free Man",
  118. "the ghost of Phil Bunion",
  119. "the gunk",
  120. "The Landscaper",
  121. "The Large-Bellied Snitch",
  122. "The Man",
  123. "The Sierpinski brothers",
  124. "The Server",
  125. "The Snake With Like Ten Heads",
  126. "The Terrible Pinch",
  127. "The Thing with No Name",
  128. "The Thorax",
  129. "The Unkillable Skeleton"];
  130.  
  131. // for monsters with name generators, or easier to look at the image names
  132. var monsterImages = {
  133. "sororghost": "sexy sorority ghost",
  134. "sororeton": "sexy sorority skeleton",
  135. "sororpire": "sexy sorority vampire",
  136. "sororwolf": "sexy sorority werewolf",
  137. "sororbie": "sexy sorority zombie",
  138. "coldhobo": "Cold hobo",
  139. "hothobo": "Hot hobo",
  140. "nhobo": "Normal hobo",
  141. "slhobo": "Sleaze hobo",
  142. "spookyhobo": "Spooky hobo",
  143. "stenchhobo": "Stench hobo",
  144. "elfhobo": "Hobelf",
  145. "animelf1": "tiny-screwing animelf",
  146. "animelf2": "plastic-extruding animelf",
  147. "animelf3": "circuit-soldering animelf",
  148. "animelf4": "quality control animelf",
  149. "animelf5": "toy assembling animelf",
  150. "beergolem": "X Bottles of Beer on a Golem",
  151. "stonegolem": "X Stone Golem",
  152. "dimhorror":" X-dimensional horror",
  153. "hydra": "X-headed Hydra",
  154. "earbeast": "Beast with X Ears",
  155. "eyebeast": "Beast with X Eyes",
  156. "fernghost": "Ghost of Fernswarthy's Grandfather",
  157. "tacoelf_sign": "sign-twirling Crimbo elf",
  158. "tacoelf_taco": "taco-clad Crimbo elf",
  159. "tacoelf_cart": "tacobuilding elf",
  160. "bigskeleton": "procedurally-generated skeleton",
  161. "faq_miniboss": "Video Game Miniboss",
  162. "faq_boss": "Video Game Boss",
  163. "faq_": "Video Game Minion", // no convenient way to tell strong vs. weak vs. moderate
  164. "bb_caveman": "angry cavebugbear", // and all the very very very... angry ones, too
  165. "dvhotbear": "hot bugbear",
  166. "dvcoldbear": "cold bugbear",
  167. "dvspookybear": "spooky bugbear",
  168. "dvsleazebear": "sleaze bugbear",
  169. "dvstenchbear": "stench bugbear",
  170. "dvhotghost": "hot ghost",
  171. "dvcoldghost": "cold ghost",
  172. "dvspookyghost": "spooky ghost",
  173. "dvsleazeghost": "sleaze ghost",
  174. "dvstenchghost": "stench ghost",
  175. "dvhotskel": "hot skeleton",
  176. "dvcoldskel": "cold skeleton",
  177. "dvspookyskel": "spooky skeleton",
  178. "dvsleazeskel": "sleaze skeleton",
  179. "dvstenchskel": "stench skeleton",
  180. "dvhotvamp": "hot vampire",
  181. "dvcoldvamp": "cold vampire",
  182. "dvspookyvamp": "spooky vampire",
  183. "dvsleazevamp": "sleaze vampire",
  184. "dvstenchvamp": "stench vampire",
  185. "dvhotwolf": "hot werewolf",
  186. "dvcoldwolf": "cold werewolf",
  187. "dvspookywolf": "spooky werewolf",
  188. "dvsleazewolf": "sleaze werewolf",
  189. "dvstenchwolf": "stench werewolf",
  190. "dvhotzom": "hot zombie",
  191. "dvcoldzom": "cold zombie",
  192. "dvspookyzom": "spooky zombie",
  193. "dvsleazezom": "sleaze zombie",
  194. "dvstenchzom": "stench zombie",
  195. "shopteacher": "X-fingered Shop Teacher",
  196. "js_bender": "junksprite bender",
  197. "js_melter": "junksprite melter",
  198. "js_sharpener": "junksprite sharpener",
  199. "vandalkid": "vandal kid",
  200. "paulblart": "suburban security civilian",
  201. // sloppy seconds sundae, spring break sunken party
  202. "ssd_cocktail": "Sloppy Seconds Cocktail",
  203. "ssd_sundae": "Sloppy Seconds Sundae",
  204. "ssd_burger": "Sloppy Seconds Burger",
  205. "fun-gal": "Fun-Guy Playmate",
  206. "srpainting": "ancestral Spookyraven portrait"
  207. };
  208. ////////////////////////////////////////////////////////////////////////////////
  209. // get monster name (monster manuel version)
  210. function getMonsterName(data) {
  211. var monsterName = /id=\"monname\"> *(.*?)<\/span>/i.exec(data);
  212. if(monsterName)
  213. monsterName = monsterName[1];
  214. else
  215. return "";
  216. var imageName = /adventureimages\/(.*?)\.gif/i.exec(data);
  217. if(imageName)
  218. imageName = imageName[1];
  219. else
  220. return "";
  221. // alert("initially found monster name: " + monsterName + " (image name: " + imageName + ")");
  222. // strip off leading articles, etc from monster names as seen during fight
  223. // maybe make this a list of prefixes, it's getting pretty long
  224. if(invariableMonsterNames.indexOf(monsterName) == -1) {
  225. if( monsterName.substring(0,2).toLowerCase()=="a ")
  226. monsterName = monsterName.substring(2,monsterName.length);
  227. else if( monsterName.substring(0,3).toLowerCase()=="an ")
  228. monsterName = monsterName.substring(3,monsterName.length);
  229. else if( monsterName.substring(0,5).toLowerCase()=="some ")
  230. monsterName = monsterName.substring(5,monsterName.length);
  231. else if( monsterName.substring(0,4).toLowerCase()=="the ")
  232. monsterName = monsterName.substring(4,monsterName.length);
  233. else if( monsterName.substring(0,3).toLowerCase()=="el ")
  234. monsterName = monsterName.substring(3,monsterName.length);
  235. else if( monsterName.substring(0,3).toLowerCase()=="la ")
  236. monsterName = monsterName.substring(3,monsterName.length);
  237.  
  238. // translate special cases
  239. if(monsterName.indexOf("'s butt") != -1)
  240. monsterName = "[somebody else's butt]";
  241. else if(monsterName.substring(0,6).toLowerCase()=="shadow" && monsterName.indexOf("Black Bubbles")==-1 && imageName.indexOf("faq_")==-1)
  242. monsterName = "(shadow opponent)";
  243. // all slime tube monsters are... "Slime Tube monster", ignore names, just check for the image
  244. else if(/slime[1-5]/i.exec(imageName) != null )
  245. monsterName = "Slime Tube monster";
  246. // more monsters-from-images
  247. // keep trailing numbers for anime elves (different monsters), strip otherwise (different images for same mob)
  248. else if(/animelf[1-5]/i.exec(imageName) != null )
  249. monsterName = monsterImages[imageName];
  250. else {
  251. var stripped = /(.*?)[0-9]+/i.exec(imageName);
  252. if(stripped != null) imageName=stripped[1];
  253. if(monsterImages[imageName] != undefined)
  254. monsterName = monsterImages[imageName];
  255. else if(imageName.substring(0,4).toLowerCase()=="faq_") // video game minions, how to tell strong vs. weak?
  256. monsterName = monsterImages["faq_"];
  257. }
  258. }
  259.  
  260. // alert("returning monster name: " + monsterName + " (image name: " + imageName + ")");
  261. return monsterName;
  262. }
  263. // from one ton tomato's mallsearch script
  264. function getParent(el, pTagName) {
  265. if (el == null) {
  266. return null;
  267. } else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) { // Gecko bug, supposed to be uppercase
  268. return el;
  269. } else {
  270. return getParent(el.parentNode, pTagName);
  271. }
  272. }
  273.  
  274. // fill my popup window with a reduced version of manuels facts, for our monster only
  275. function processManual(data){
  276. var myDocument = document.createElement('div');
  277. myDocument.innerHTML = data;
  278. // could just get monsterName directly, this is a relic from earlier versions
  279. var monsterName = /\&monster=(.+)/i.exec(decodeURIComponent(manuelURL));
  280. if(monsterName) monsterName = monsterName[1];
  281. // alert("monster name = " + monsterName);
  282.  
  283. // first find the table with our monster; find its parent; use that to find all the monster tables
  284. if(monsterName) {
  285. // strip out the javascript href change that loads the game main page
  286. var allscripts = myDocument.getElementsByTagName("script");
  287. allscripts[0].parentNode.removeChild(allscripts[0]);
  288. var allElements = myDocument.getElementsByTagName("font");
  289. var parentNode = 0;
  290. var monsterTables =0;
  291. var i;
  292. for(i=0; i < allElements.length; i++) {
  293. if(allElements[i].innerHTML.indexOf(monsterName) != -1) {
  294. parentNode = getParent(allElements[i], "table");
  295. parentNode = getParent(parentNode, "p");
  296. monsterTables = parentNode.getElementsByTagName("table");
  297. break;
  298. }
  299. }
  300.  
  301. // now delete all that parent's children, except our keeper monster
  302. // monsterTables is a live list, so as we delete items in the doc, list items disappear...
  303. if(monsterTables) {
  304. for(i=0; i < monsterTables.length; ) {
  305. // use the <font></font> tags to eliminate false matches in random text. Harem guards
  306. // is an example. (why did I change this?)
  307. // if(monsterTables[i].innerHTML.indexOf(">" + monsterName + "</font>") > -1) {
  308. if(monsterTables[i].innerHTML.indexOf(">" + monsterName + "<") >= 0
  309. || monsterName=="Video Game Minion" && monsterTables[i].innerHTML.indexOf(">" + monsterName)>= 0) {
  310. // our monster, skip it
  311. i++;
  312. }
  313. else {
  314. // not our monster, delete it (next target monster table will be at same index)
  315. // there is also an <a> node in front of each monster
  316. monsterTables[i].parentNode.removeChild(monsterTables[i].previousSibling);
  317. monsterTables[i].parentNode.removeChild(monsterTables[i]);
  318. }
  319. }
  320. }
  321. // delete some junk stuff - other quests header info, back to campsite footer, etc
  322. if(parentNode.parentNode) {
  323. parentNode.parentNode.removeChild(parentNode.parentNode.firstChild);
  324. parentNode.parentNode.removeChild(parentNode.parentNode.firstChild);
  325. parentNode.parentNode.removeChild(parentNode.parentNode.firstChild);
  326. parentNode.parentNode.removeChild(parentNode.parentNode.lastChild);
  327. parentNode.parentNode.removeChild(parentNode.parentNode.lastChild);
  328. }
  329. /* // if new script version available, add a link
  330. var webVer = GM_getValue("webVersion", "Error");
  331. if (webVer != "Error" && webVer > currentVersion) { // this is actually a text string comparison, not numerical
  332. var newElement = document.createElement('p');
  333. newElement.style.fontSize = "x-small";
  334. newElement.appendChild(document.createTextNode("New Monster Manuel script version " + webVer + " available: "));
  335.  
  336. var hrefElement = document.createElement('a');
  337. hrefElement.setAttribute('href', scriptSite);
  338. hrefElement.setAttribute('target', "_blank");
  339. hrefElement.appendChild(document.createTextNode("here"));
  340. newElement.appendChild(hrefElement);
  341. parentNode.parentNode.appendChild(newElement);
  342. }
  343. */
  344. // and finally fill our window
  345. myWindow.document.documentElement.innerHTML = myDocument.innerHTML;
  346. myWindow.document.close();
  347. }
  348. }
  349.  
  350. // pop up an empty window for our factoid, and call something to fill it
  351. // to do: figure out how to size this appropriately
  352. var myWindow;
  353. function manuelPopup()
  354. {
  355. myWindow = window.open(baseURL,'manuel','height=400,width=500,scrollbars=yes');
  356. GM_get(baseURL + manuelURL, processManual);
  357. }
  358.  
  359. // parse the charpane info for the password hash (use as a session ID)
  360. function getPwdHash(data){
  361. var pwdHash = /pwdhash \= \"(.*?)\"/i.exec(data); // the .*? is the non-greedy version of .*
  362. if(pwdHash)
  363. pwdHash = pwdHash[1];
  364. else
  365. pwdHash = "";
  366.  
  367. return pwdHash;
  368. }
  369.  
  370. ////////////////////////////////////////////////////////////////////////////////
  371. function monsterNameEntry() {
  372. var monsterName = prompt("Enter Monster Name to research:\n", "");
  373. var firstChar = monsterName.toLowerCase().charAt(0) ;
  374. if(firstChar < 'a' || firstChar > 'z') firstChar = '-';
  375.  
  376. manuelURL = "questlog.php?which=6&vl=" + firstChar + "&monster="+monsterName;
  377. manuelPopup();
  378. }
  379. ////////////////////////////////////////////////////////////////////////////////
  380. // currently unused - not really helpful to search unless can cross first-letter sections
  381. function processQuestPage() {
  382. var entries = document.getElementsByTagName("b");
  383. var i;
  384. for(i=0; i < entries.length; i++) {
  385. if(entries[i].innerHTML == "Other") {
  386. // append our item here
  387. var newElement = entries[i].parentNode.parentNode.appendChild(document.createElement('b'));
  388. newElement.setAttribute("onmouseover", 'this.style.opacity="0.5"');
  389. newElement.setAttribute("onmouseout", 'this.style.opacity="1"');
  390. newElement.innerHTML = "&nbsp;Search";
  391. newElement.addEventListener("click", monsterNameEntry);
  392. }
  393. }
  394. }
  395.  
  396. ////////////////////////////////////////////////////////////////////////////////
  397. function processFight() {
  398. var playerName = getPlayerNameFromCharpane().username;
  399. var pwdHash = "";
  400. // once per login, check if a new version is available
  401. // why all this top level stuff sometimes fails at each level, who knows
  402. if(playerName && top && top.frames[0] && top.frames[0].document && top.frames[0].document.documentElement) {
  403. pwdHash = getPwdHash(top.frames[0].document.documentElement.innerHTML);
  404. var oldPwdHash = GM_getValue(playerName + "_pwdHash", 0);
  405. if(pwdHash != oldPwdHash) {
  406. // new session
  407. GM_setValue(playerName + "_pwdHash", pwdHash);
  408. /* // check for a new version of script if none seen already
  409. var webVer = GM_getValue("webVersion", "Error");
  410. if(webVer == "Error" || webVer <= currentVersion)
  411. GM_get(scriptURL, CheckScriptVersion);
  412. */
  413. }
  414. // clear any lingering flag that was set with empty player name by old script versions
  415. GM_setValue("_hasManuel", false);
  416. }
  417. var monsterName = getMonsterName(document.body.innerHTML);
  418. var firstChar = monsterName.toLowerCase().charAt(0) ;
  419. if(firstChar < 'a' || firstChar > 'z') firstChar = '-';
  420. // apparently mafia now wants a password
  421. // yay, that change got reverted, and was causing problems anyway
  422. // manuelURL = "questlog.php?which=6&vl=" + firstChar + "&pwd=" + pwdHash + "&monster="+monsterName;
  423. manuelURL = "questlog.php?which=6&vl=" + firstChar + "&monster="+monsterName;
  424.  
  425. // find the monster's HTML element on the page; add our link
  426. var monsterSpan = document.getElementById("monname");
  427. var monsterTable = getParent(monsterSpan, "table");
  428. if(monsterSpan && monsterTable) {
  429. var newElement = document.createElement("FONT");
  430. // make sure we have a factoid to popup - signalled by the attack/defense/hp display
  431. // or by monster manuel saying something
  432. if(monsterTable.innerHTML.indexOf("Enemy's Attack Power") != -1
  433. || document.body.innerHTML.indexOf("Monster Manuel") != -1 ){
  434. if(playerName != "")
  435. GM_setValue(playerName+"_hasManuel", true); // current monster has factoids, ergo player has manuel
  436.  
  437. newElement.innerHTML = "<font size=2> (<u>factoids</u>)</font>"; ;
  438. newElement.setAttribute("onmouseover", 'this.style.opacity="0.5"');
  439. newElement.setAttribute("onmouseout", 'this.style.opacity="1"');
  440. newElement.setAttribute("id", 'manuel');
  441. newElement.addEventListener("click", manuelPopup, true);
  442. // always insert a factoid link if there are factoids
  443. monsterSpan.parentNode.insertBefore(newElement, monsterSpan.nextSibling);
  444. } else {
  445. newElement.innerHTML = "<font size=2> (no factoids)</font>"; ;
  446. newElement.setAttribute("onmouseout", 'this.style.opacity="0.5"');
  447. newElement.setAttribute("id", 'manuel');
  448. // no factoids, only insert the link if we know the player has manuel
  449. if(GM_getValue(playerName+"_hasManuel", false) == true )
  450. monsterSpan.parentNode.insertBefore(newElement, monsterSpan.nextSibling);
  451. }
  452. }
  453. }
  454.  
  455. ////////////////////////////////////////////////////////////////////////////////
  456. // main prog, just call the proper routine if we are on a pane we care about
  457. var nodeBody = document.getElementsByTagName("body").item(0);
  458. var baseURL = "";
  459. var manuelURL = "";
  460.  
  461. if (nodeBody) {
  462. baseURL = nodeBody.baseURI.substring(0,nodeBody.baseURI.lastIndexOf('/')+1);
  463. }
  464.  
  465. // our popup gets named after the originating window, which is fight.php, so don't process it
  466. if(window.name != "manuel" && document.location.pathname.indexOf("fight.php") != -1 ) {
  467. processFight();
  468. }
  469. else if(document.location.pathname.indexOf("questlog.php") != -1 ) {
  470. // deprecated until I can figure out how to search across different first letters
  471. // processQuestPage();
  472. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址