KOL Consumption Tracker

Adds info about inventory items, tracks fullness, spleen.

  1. // ==UserScript==
  2. // @name KOL Consumption Tracker
  3. // @namespace KoLCtH
  4. // @description Adds info about inventory items, tracks fullness, spleen.
  5. // @include *www.kingdomofloathing.com/game.php*
  6. // @include *www.kingdomofloathing.com/charpane.php*
  7. // @include *www.kingdomofloathing.com/inventory.php*
  8. // @include *www.kingdomofloathing.com/skills.php*
  9. // @include *www.kingdomofloathing.com/cafe.php*
  10. // @include *www.kingdomofloathing.com/account.php*
  11. // @include *www.kingdomofloathing.com/multiuse.php*
  12. // @include *www.kingdomofloathing.com/afterlife.php*
  13. // @include *www.kingdomofloathing.com/storage.php*
  14. // @include *www.kingdomofloathing.com/closet.php*
  15. // @include http://127.0.0.1:*game.php*
  16. // @include http://127.0.0.1:*charpane.php*
  17. // @include http://127.0.0.1:*inventory.php*
  18. // @include http://127.0.0.1:*skills.php*
  19. // @include http://127.0.0.1:*cafe.php*
  20. // @include http://127.0.0.1:*account.php*
  21. // @include http://127.0.0.1:*multiuse.php*
  22. // @include http://127.0.0.1:*afterlife.php*
  23. // @include http://127.0.0.1:*storage.php*
  24. // @include http://127.0.0.1:*closet.php*
  25. // @include http://localhost:*game.php*
  26. // @include http://localhost:*charpane.php*
  27. // @include http://localhost:*inventory.php*
  28. // @include http://localhost:*skills.php*
  29. // @include http://localhost:*cafe.php*
  30. // @include http://localhost:*account.php*
  31. // @include http://localhost:*multiuse.php*
  32. // @include http://localhost:*afterlife.php*
  33. // @include http://localhost:*storage.php*
  34. // @include http://localhost:*closet.php*
  35. // @version 1.1.6
  36. // ==/UserScript==
  37. //An all new script based in part on JHunz's KOL Consumption Management. Should have most, if not all of the same features, with an updated database. Tracks current Full and Spleen in the charpane above Drunk. Will warn you when you are about to overdrink and allow you to contine or stop. Also works on multiple items consumed at the same time. Allows sorting of consumables by name, fullness or quality. Database and script should check for updates every two weeks. Great thanks to Dibzannia, Muroni, Mutant Pickles and Knitbone for their testing help.
  38.  
  39. var TOP = window.top.wrappedJSObject
  40. switch(window.location.pathname)
  41. {
  42. case '/game.php':
  43. GM_deleteValue("CurrentCharName");
  44. GM_deleteValue("CurrentPWD");
  45. loadDB();
  46. /*GM_get('/api.php?what=status&for=CTHConsumptionTracker', function (res)
  47. {
  48. console.log(res)
  49. });*/
  50. break
  51. case '/account.php':
  52. buildPrefs();
  53. break
  54. case '/charpane.php':
  55. var charName = GM_getValue('CurrentCharName', null)
  56. if (!charName)
  57. {
  58. charNameNode = $('b');
  59. if (charNameNode)
  60. {
  61. charName = charNameNode.textContent;
  62. GM_setValue('CurrentCharName', charName);
  63. }
  64. else return;
  65. }
  66. if (!GM_getValue('CurrentPWD', null))
  67. {
  68. if (unsafeWindow.pwdhash)
  69. var pwd = unsafeWindow.pwdhash
  70. else if ($('a[href ^= "/kolproxy-frame-page"]'))
  71. var pwd = $('a[href ^= "/kolproxy-frame-page"]').href.match(/pwd=(\w+)/)[1]
  72. GM_setValue('CurrentPWD', pwd)
  73. }
  74. //if script has not been run before for this char or new ascension, set values to default
  75. if (!GM_getValue(charName + '.overdrunk', null))
  76. {
  77. GM_setValue(charName + '.overdrunk', 15);
  78. GM_setValue(charName + '.overfull', 16);
  79. GM_setValue(charName + '.overspleen', 16);
  80. }
  81. //if rollover has occurred since last login, reset
  82. if (unsafeWindow.rollover > GM_getValue(charName + '.rollover', 0))
  83. {
  84. GM_setValue(charName + '.rollover', unsafeWindow.rollover)
  85. GM_setValue(charName + '.currentDrunk', 0);
  86. GM_setValue(charName + '.currentFull', 0);
  87. GM_setValue(charName + '.currentSpleen', 0);
  88. }
  89. var moxTextNode = $('td:contains("Mox")');
  90. if (!moxTextNode)
  91. return;
  92. var drunkTextNode = moxTextNode.parentNode.nextSibling
  93. if (!$('img[src $= "hourglass.gif"]'))
  94. //in compact mode, there's one more sibling here
  95. drunkTextNode = drunkTextNode.nextSibling;
  96. //get drunkenness
  97. if (drunkTextNode)
  98. {
  99. //native full counter is first
  100. if (drunkTextNode.textContent.indexOf('Full') != -1)
  101. drunkTextNode = drunkTextNode.nextSibling
  102. if (drunkTextNode)
  103. {
  104. var currentDrunk = parseInt(drunkTextNode.childNodes[1].textContent);
  105. GM_setValue(charName + '.currentDrunk', currentDrunk);
  106. }
  107. }
  108. //build and insert fullness and spleen readouts after the moxie node.
  109. var showFull = GM_getValue(charName + '.showFull', true);
  110. if (showFull)
  111. {
  112. var currentFull = GM_getValue(charName + '.currentFull', 0);
  113. if (currentFull != 0)
  114. {
  115. var fullDisplayTR = moxTextNode.parentNode.parentNode.appendChild(CE('tr'));
  116. fullDisplayTR.appendChild(CE('td', 'align|right', 'text|Full: '));
  117. fullDisplayTR.appendChild(CE('td', 'style|font-weight:bold', 'text|' + currentFull));
  118. fullDisplayTR.addEventListener('dblclick', function (e) {setThreshhold("Full", e.target.parentNode)}, false)
  119. }
  120. }
  121. var currentSpleen = GM_getValue(charName + '.currentSpleen', 0);
  122. if (currentSpleen != 0)
  123. {
  124. var spleenDisplayTR = moxTextNode.parentNode.parentNode.appendChild(CE('tr'));
  125. spleenDisplayTR.appendChild(CE('td', 'align|right', 'text|Spleen: '));
  126. spleenDisplayTR.appendChild(CE('td', 'style|font-weight:bold', 'text|' + currentSpleen));
  127. spleenDisplayTR.addEventListener('dblclick', function (e) {setThreshhold("Spleen", e.target.parentNode)}, false)
  128. }
  129. break
  130. case '/inventory.php':
  131. checkForUpdates();
  132. if (!TOP.Consumption_DB)
  133. return;
  134. var colors = {'EPIC':'purple', 'Awesome':'blue', 'Good':'green', 'Decent':'black', 'Crappy':'gray', undefined:'firebrick'};
  135. $('td[id]', true).forEach(function (td)
  136. {
  137. var itemNum = parseInt(td.id.substr(1));
  138. var temp = TOP.Consumption_DB.items[itemNum];
  139. if (!temp)
  140. {
  141. var descID = td.previousSibling
  142. if (!descID)
  143. return
  144. descID = descID.firstChild.getAttribute('onclick').match(/(\d+)/)[1]
  145. classify([itemNum, descID, td]);
  146. return;
  147. }
  148. var newNode = td.appendChild(CE("strong", 'style|font-size:8pt; color:' + colors[temp.qual]));
  149. if (temp.amt)
  150. newNode.appendChild(document.createTextNode(" [" + temp.amt + (temp.qual ? ' ' + temp.qual : '') + (temp.level ? " L" + temp.level : '') + "]"));
  151. if (temp.eff)
  152. {
  153. newNode.appendChild(CE("br"));
  154. newNode.appendChild(document.createTextNode(temp.eff));
  155. }
  156. if (itemNum == 2614) //mojo filter cleans 1 spleen
  157. {
  158. $('a', false, td).addEventListener('click', function ()
  159. {
  160. var charName = GM_getValue('CurrentCharName')
  161. var spleen = GM_getValue(charName + '.currentSpleen', 0);
  162. GM_setValue(charName + '.currentSpleen', Math.max(spleen - 1, 0));
  163. }, false);
  164. }
  165. else if (itemNum == 3433) //spice melange cleans 3 full and 3 drunk
  166. {
  167. $('a', false, td).addEventListener('click', function ()
  168. {
  169. var charName = GM_getValue('CurrentCharName')
  170. var full = GM_getValue(charName + '.currentFull', 0);
  171. GM_setValue(charName + '.currentFull', Math.max(full - 3, 0));
  172. }, false);
  173. }
  174. });
  175. if ($('[name="Food"]'))
  176. {
  177. //set event listener to catch clicks on eat/drink/use links on inv page 1
  178. document.addEventListener('click', stealThisFood, true);
  179. var sortButtonHolder = $('td>center').parentNode.appendChild(CE('div', 'align|center'));
  180. var sortTypes = ['name', 'quality', 'full'];
  181. for (var n=0;n<3;n++)
  182. {
  183. var b = sortButtonHolder.appendChild(CE('input', 'type|button', 'class|button', 'style|margin:5px', 'value|Sort by ' + sortTypes[n]));
  184. b.addEventListener('mousedown', function (e) {sortConsumables(this.value.substr(8), e.button)}, false);
  185. b.addEventListener('contextmenu', function (e) {e.preventDefault()}, false);
  186. }
  187. sortConsumables();
  188. }
  189. break;
  190. case "/multiuse.php":
  191. var startSpleen = GM_getValue(GM_getValue('CurrentCharName') + '.currentSpleen', 0);
  192. $('.button').addEventListener('click', function (e)
  193. {
  194. e.preventDefault();
  195. var quant = $('[name=quantity]').value;
  196. var item = $('[name=whichitem]').options[$('[name=whichitem]').selectedIndex].value;
  197. for (var n=0;n<quant;n++)
  198. {
  199. //if multiusing spleen items goes overspleen, KoL uses none of them
  200. //so reset spleen to pre-use level
  201. if (!addSpleenness(itemNum))
  202. {
  203. GM_setValue(GM_getValue('CurrentCharName') + '.currentSpleen', startSpleen);
  204. return;
  205. }
  206. }
  207. this.parentNode.submit();
  208. }, false);
  209. break;
  210. case "/skills.php":
  211. //add spleen from using medicinal herbs at the tiny skills menu
  212. if (window.location.search == '?tiny=1')
  213. {
  214. var select = $('select', true)[1];
  215. var tinybutton = $('.tinybutton', true)[1];
  216. if (tinybutton)
  217. {
  218. tinybutton.addEventListener('click', function (e)
  219. {
  220. if (select.options[select.selectedIndex].value == 1274)
  221. addSpleenness(1274);
  222. },false);
  223. }
  224. }
  225. break
  226. case "/afterlife.php":
  227. //Reset thresholds upon ascension
  228. var charName = GM_getValue('CurrentCharName', null);
  229. if (charName)
  230. {
  231. GM_setValue(charName + '.currentDrunk', 0);
  232. GM_setValue(charName + '.currentFull', 0);
  233. GM_setValue(charName + '.currentSpleen', 0);
  234. GM_setValue(charName + '.overdrunk', 15);
  235. GM_setValue(charName + '.overfull', 16);
  236. GM_setValue(charName + '.overspleen', 16);
  237. }
  238. break
  239. case "/cafe.php":
  240. processCafe();
  241. break
  242. }
  243.  
  244. function classify(item)
  245. {
  246. var URL = "/desc_item.php?whichitem=" + item[1];
  247. GM_get(URL, function (results)
  248. {
  249. var div = $("#classify") || CE('div', 'id|classify');
  250. div.innerHTML = results;
  251. var bs = $('b', true, div);
  252. var index = 0;
  253. var isItConsumable = bs.some(function (b, i)
  254. {
  255. if (b.textContent.indexOf('booze') != -1 || b.textContent.indexOf('food') != -1)
  256. {
  257. index = i
  258. return true;
  259. }
  260. else
  261. return false;
  262. })
  263. if (!isItConsumable)
  264. return false;
  265. var type = bs[index].textContent.indexOf('booze') != -1 ? 'D' : 'F'
  266. var temp = {};
  267. temp.amt = bs[index + 1].textContent + type
  268. var qual = bs[index].textContent.match(/\((\w+)\)/)[1]
  269. temp.qual = qual.charAt(0).toUpperCase() + qual.slice(1);
  270. var level = bs[index + 2].textContent;
  271. if (level.indexOf('Meat') == -1 && level.indexOf('Quest') == -1)
  272. temp.level = level
  273. var newNode = item[2].appendChild(CE("strong", 'style|font-size:8pt; color:' + colors[temp.qual]));
  274. newNode.appendChild(document.createTextNode(" [" + temp.amt + " " + temp.qual + (temp.level ? " L" + temp.level : '') + "]"));
  275. TOP.Consumption_DB.items[item[0]] = temp;
  276. GM_setValue('Consumption_DB', JSON.stringify(TOP.Consumption_DB));
  277. //race condition!?
  278. })
  279. }
  280.  
  281. function sortConsumables(sortBy, direction)
  282. {
  283. if (!sortBy)
  284. {
  285. var sortVal = GM_getValue('sortBy', null);
  286. if (!sortVal)
  287. return
  288. sortBy = sortVal.match(/name|full|quality/)[0]
  289. direction = sortVal.match(/\s\((a|d)/)[1]
  290. }
  291. var numCols = unsafeWindow.numcols || 3;
  292. $('.guts', true).forEach(function (table)
  293. {
  294. var items = $('.i', true, table);
  295. if (sortBy == 'full')
  296. items.sort(sortByFull);
  297. else if (sortBy == 'quality')
  298. items.sort(sortByQuality);
  299. else if (sortBy == 'name')
  300. items.sort(sortByName);
  301. items.forEach(function (item, index)
  302. {
  303. var thisRow = table.firstChild.childNodes[Math.floor(index/numCols)];
  304. thisRow.appendChild(item);
  305. })
  306. function sortByFull(a, b)
  307. {
  308. var aS = $('font+strong', false, a);
  309. if (aS)
  310. aS = aS.textContent.match(/(\d+)(F|D|S)/);
  311. aS = aS ? aS[1] : 0;
  312. var bS = $('font+strong', false, b);
  313. if (bS)
  314. bS = bS.textContent.match(/(\d+)(F|D|S)/);
  315. bS = bS ? bS[1] : 0;
  316. if (direction == 0)
  317. return aS > bS;
  318. else
  319. return aS < bS;
  320. }
  321. function sortByQuality(a, b)
  322. {
  323. var aS = $('font+strong', false, a);
  324. if (aS)
  325. aS = aS.textContent.match(/Crappy|Decent|Good|Awesome|EPIC/);
  326. aS = aS || 'none';
  327. var bS = $('font+strong', false, b);
  328. if (bS)
  329. bS = bS.textContent.match(/Crappy|Decent|Good|Awesome|EPIC/);
  330. bS = bS || 'none';
  331. const qual = {none:0, Crappy:1, Decent:2, Good:3, Awesome:4, EPIC:5};
  332. if (direction == 0)
  333. return qual[aS] > qual[bS];
  334. else
  335. return qual[aS] < qual[bS];
  336. }
  337. function sortByName(a, b)
  338. {
  339. var aS = $('b', false, a).textContent.toLowerCase();
  340. var bS = $('b', false, b).textContent.toLowerCase();
  341. if (direction == 0)
  342. return aS > bS;
  343. else
  344. return aS < bS;
  345. }
  346. })
  347. }
  348.  
  349. function consumeThis(link)
  350. {
  351. var itemNum = link.href.match(/\d+$/)[0];
  352. if (!itemNum)
  353. return false;
  354. var type = link.href.match(/_(eat|booze|use)/)[1];
  355. if (type == 'booze')
  356. {
  357. if (!overdrinkProtect(itemNum, link))
  358. //oP() says this is would be an overdrink, do not allow to continue to the consumption section
  359. return false;
  360. }
  361. else if (type == 'eat' && GM_getValue(charName + '.showFull', true))
  362. addFullness(itemNum);
  363. else if (type == 'use')
  364. addSpleenness(itemNum);
  365. if (itemNum == 3325 || itemNum == 3327)
  366. {
  367. //jar of fermented pickle juice and extra-greasy slider clean 5 spleen
  368. var spleen = GM_getValue(charName + '.currentSpleen', 0);
  369. GM_setValue(charName + '.currentSpleen', Math.max(spleen - 5, 0));
  370. }
  371. //use the page functions to show the results in a loading div
  372. var thisHref = link.href.replace('&which=', '&ajax=');
  373. unsafeWindow.loadingDiv();
  374. unsafeWindow.dojax(thisHref);
  375. }
  376.  
  377. function overdrinkProtect(itemNum, link, multi)
  378. {
  379. //check if the drink attempted would cause overdrunkness, returns true for overdrunk, false for not
  380. //Get current values for drunkenness and overdrunk, since they might have changed
  381. var charName = GM_getValue('CurrentCharName', null);
  382. var currentDrunk = parseInt(GM_getValue(charName + '.currentDrunk', 0));
  383. if (itemNum == 2743) //steel margarita
  384. GM_setValue(charName + '.overdrunk', 20);
  385. var overdrunk = parseInt(GM_getValue(charName + '.overdrunk', 15));
  386. var addDrunk = parseInt(howMuchConsumption(itemNum));
  387. if (addDrunk == 999) {
  388. var risky = confirm("Unknown drink. Drink at your own risk.");
  389. if (risky)
  390. return true;
  391. link.textContent = "[risky drink]";
  392. setTimeout(function () {link.textContent = "[drink]"}, 10000);
  393. return false;
  394. }
  395. if (multi) {
  396. if (currentDrunk + addDrunk * multi >= overdrunk)
  397. return confirm("That many drinks will make you overdrunk.");
  398. }
  399. if (currentDrunk + addDrunk >= overdrunk) {
  400. var OD = confirm("This drink will make you overdrunk.");
  401. if (OD)
  402. return true;
  403. link.textContent = "[overdrink]";
  404. setTimeout(function () {link.textContent = "[drink]"}, 10000);
  405. return false;
  406. }
  407. //not unknown or overdrunk, it gets the oP() seal of approval
  408. return true;
  409. }
  410.  
  411. function addFullness(itemNum)
  412. {
  413. var charName = GM_getValue('CurrentCharName', null)
  414. var addFull = howMuchConsumption(itemNum);
  415. if (addFull == 999)
  416. return;
  417. var currentFull = GM_getValue(charName + '.currentFull');
  418. var overfull = GM_getValue(charName + '.overfull');
  419. var newFull = parseInt(currentFull) + parseInt(addFull)
  420. if (itemNum == 2742) //steel lasagna
  421. GM_setValue(charName + '.overfull', 21);
  422. if (newFull < overfull)
  423. GM_setValue(charName + '.currentFull', newFull);
  424. return
  425. }
  426.  
  427. function addSpleenness(itemNum, multi)
  428. {
  429. var charName = GM_getValue('CurrentCharName', null);
  430. var addSpleen = howMuchConsumption(itemNum);
  431. if (addSpleen == 999)
  432. return true;
  433. var currentSpleen = GM_getValue(charName + '.currentSpleen');
  434. var overspleen = GM_getValue(charName + '.overspleen');
  435. var newSpleen = parseInt(currentSpleen) + parseInt(addSpleen);
  436. if (itemNum == 2744) //steel air
  437. GM_setValue(charName + '.overspleen', 21);
  438. if (newSpleen < overspleen)
  439. GM_setValue(charName + '.currentSpleen', newSpleen);
  440. else
  441. return false;
  442. return true;
  443. }
  444.  
  445. //returns drunkenness/fullness/spleen, or 999 if unknown
  446. function howMuchConsumption(itemNum)
  447. {
  448. //cafe food has negative itemNum
  449. if (itemNum < 0)
  450. return 2 - itemNum;
  451. var temp = TOP.Consumption_DB.items[itemNum];
  452. if (temp && temp.amt)
  453. return temp.amt.match(/\d+/)[0];
  454. return 999;
  455. }
  456.  
  457. function stealThisFood(e)
  458. {
  459. if (e.button != 0 || e.target.tagName.toLowerCase() != 'a')
  460. return;
  461. if (e.target.textContent.match(/\[(use|eat|drink)/))
  462. {
  463. var itemNum = e.target.href.match(/\d+$/) || e.target.id.substr(2);
  464. if (!TOP.Consumption_DB.items[itemNum])
  465. return;
  466. e.preventDefault();
  467. e.stopPropagation();
  468. if (e.target.textContent.match(/(some|multiple)/))
  469. {
  470. var offset = getOffset(e.target);
  471. offset[0] -= 25; offset[1] -= 90;
  472. var popup = $('#MUpopup');
  473. if (popup)
  474. {
  475. popup.style.display = 'block';
  476. popup.style.top = offset[1];
  477. popup.style.left = offset[0];
  478. popup.childNodes[1].focus();
  479. popup.setAttribute('rel', itemNum);
  480. return;
  481. }
  482. popup = $('body').appendChild(CE('center', 'id|MUpopup', 'rel|' + itemNum, 'style|position:absolute; width:130px; top:' + offset[1] + '; left:' + offset[0] + '; background-color:white; border:solid black 1px;'));
  483. popup.appendChild(CE('div', 'style|background-color:blue; color:white; font-weight:bold;', 'text|How Many?'));
  484. var quant = popup.appendChild(CE('input', 'id|quant', 'type|text', 'style|width:100px; margin:4px'));
  485. quant.focus();
  486. var close = popup.appendChild(CE('img', 'src|http://images.kingdomofloathing.com/closebutton.gif', 'style|position:absolute; top:1px; right:1px'));
  487. close.addEventListener('click', function () {this.parentNode.style.display = 'none'}, false);
  488. var button = popup.appendChild(CE('input', 'type|button', 'class|button', 'value|Consume!'));
  489. button.addEventListener('click', multiuse, false);
  490. quant.addEventListener('keyup', multiuse, false);
  491. }
  492. else
  493. consumeThis(e.target);
  494. }
  495.  
  496. function multiuse(e)
  497. {
  498. if (e.type == 'keyup' && e.keyCode != 13)
  499. return;
  500. var itemNum = $('#MUpopup').getAttribute('rel');
  501. var quant = $('#quant').value;
  502. $('#quant').value = null;
  503. $('#MUpopup').style.display = 'none';
  504. var temp = TOP.Consumption_DB.items[itemNum];
  505. if (quant < 1 || isNaN(quant) || !temp)
  506. return false;
  507. var type = temp.amt ? temp.amt.charAt(1) : 'S';
  508. //var url = 'http://' + window.location.host;
  509. var url = type == 'F' ? 'inv_eat.php?' : type == 'D' ? 'inv_booze.php?' : 'multiuse.php?action=useitem&';
  510. url += 'pwd=' + GM_getValue('CurrentPWD') + '&which=1&whichitem=' + itemNum + '&ajax=1&quantity=' + quant;
  511. var startSpleen = GM_getValue(GM_getValue('CurrentCharName') + '.currentSpleen', 0);
  512. for (var j=0;j<quant;j++)
  513. {
  514. if (type == 'D' && !overdrink)
  515. {
  516. var overdrink = overdrinkProtect(itemNum, null, quant);
  517. if (!overdrink)
  518. return false;
  519. }
  520. if (type == 'F' && GM_getValue(charName + '.showFull', true))
  521. addFullness(itemNum);
  522. else if (type == 'S')
  523. {
  524. if (!addSpleenness(itemNum))
  525. {
  526. GM_setValue(GM_getValue('CurrentCharName') + '.currentSpleen', startSpleen);
  527. return;
  528. }
  529. }
  530. }
  531. //console.log(url, itemNum, quant, unsafeWindow.dojax)
  532. unsafeWindow.dojax(url)
  533. //console.log(unsafeWindow.dojax(url));
  534. unsafeWindow.updateInv({itemNum:'-' + quant});
  535. }
  536. }
  537.  
  538. function processCafe()
  539. {
  540. var cafeForm = document.forms[0];
  541. var cafeId = cafeForm.firstChild.value;
  542. if (!cafeId)
  543. return;
  544. var charName = GM_getValue('CurrentCharName');
  545. var cafeFood = {"1":{"-1":"[3F]", "-2":"[4F]", "-3":"[5F]"},
  546. "2":{"-1":"[3D]", "-2":"[3D]", "-3":"[3D]"},
  547. "3":{"571":"[1F] -X HP +10X MP", "570":"[3F] +Mox -Mus", "569":"[3F] +Mys -Mox", "568":"[3F] +Mus -Mys", "470":"[1D] +Mus -Mox +9 MP"}};
  548. var submitButton = $('.button');
  549. submitButton.type = 'button';
  550. cafeForm.addEventListener('change', function (e)
  551. {
  552. submitButton.value = 'Order ' + e.target.parentNode.parentNode.nextSibling.textContent;
  553. }, false);
  554. $('[type="radio"]', true, cafeForm).forEach(function (r)
  555. {
  556. var place = r.parentNode.parentNode;
  557. var newRow = place.parentNode.insertBefore(CE('tr'), place.nextSibling);
  558. var newCell = newRow.appendChild(CE('td', 'colspan|3', 'style|text-align:center; color:blue; font-weight:bold'));
  559. if (cafeFood[cafeId] && cafeFood[cafeId][r.value])
  560. var fdAmount = cafeFood[cafeId][r.value];
  561. else if (TOP.Consumption_DB && TOP.Consumption_DB.items[r.value])
  562. var fdAmount = '[' + TOP.Consumption_DB.items[r.value].amt + ']';
  563. else
  564. var fdAmount = '[??]';
  565. newCell.textContent = fdAmount;
  566. })
  567. submitButton.addEventListener('click', function ()
  568. {
  569. var itemNum = $('input:checked').value;
  570. var type = this.value.substr(-2, 1);
  571. if (type == 'F' && GM_getValue(charName + '.showFull', true))
  572. //add full amount and submit
  573. addFullness(itemNum);
  574. else if (type == 'D')
  575. {
  576. //overdrink protection
  577. var currentDrunk = GM_getValue(charName + '.currentDrunk');
  578. var overdrunk = GM_getValue(charName + '.overdrunk');
  579. var drunkAmt = this.value.substr(-3, 1);
  580. if ((currentDrunk + parseInt(drunkAmt)) >= overdrunk)
  581. {
  582. if (this.value.indexOf('(overdrink)') == -1)
  583. {
  584. alert("This drink will cause you to overdrink");
  585. this.value = 'Order (overdrink) ' + this.value.substr(-4, 4);
  586. return;
  587. }
  588. }
  589. }
  590. cafeForm.submit();
  591. },false);
  592. }
  593.  
  594. function getOffset(node)
  595. {
  596. var X = 0, Y = 0;
  597. while (node.offsetParent)
  598. {
  599. X += node.offsetLeft;
  600. Y += node.offsetTop;
  601. node = node.offsetParent;
  602. }
  603. return [X, Y];
  604. }
  605.  
  606. function CE(tag/*,attributes*/)
  607. {
  608. var node = document.createElement(tag);
  609. for (var i=1,len=arguments.length;i<len;i++)
  610. {
  611. var attr = arguments[i].split('|');
  612. if (attr[0] == 'text')
  613. node.textContent = attr[1];
  614. else
  615. node.setAttribute(attr[0], attr[1]);
  616. }
  617. return node;
  618. }
  619.  
  620. function $(selector, all, scope)
  621. {
  622. scope = scope || document;
  623. if (selector.indexOf(':contains') != -1)
  624. {
  625. var test = selector.match(/([^:]+):contains\(["'](.+)["']\)/);
  626. var res = scope.querySelectorAll(test[1])
  627. for (a in res) {
  628. if (res[a].textContent && res[a].textContent.indexOf(test[2]) != -1)
  629. return res[a]
  630. }
  631. }
  632. else if (all)
  633. return Array.prototype.slice.call(scope.querySelectorAll(selector));
  634. else
  635. return scope.querySelector(selector);
  636. }
  637.  
  638. function GM_get(target, callback, err) {
  639. if (target.indexOf('http') != 0)
  640. target = 'http://' + window.location.host + target;
  641. GM_xmlhttpRequest({
  642. method: 'GET',
  643. url: target,
  644. onload: function(details) {
  645. callback(details.responseText);
  646. },
  647. onerror: function () {
  648. if (err)
  649. err();
  650. }
  651. });
  652. }
  653.  
  654. function loadDB()
  655. {
  656. const CURRENT_DB = GM_getValue('Consumption_DB', null);
  657. if (!CURRENT_DB)
  658. //DB is blank, get new one
  659. getDB();
  660. else
  661. initializeScript(CURRENT_DB);
  662. }
  663.  
  664. function getDB()
  665. {
  666. //DB has new version, download it
  667. GM_get('http://sites.google.com/site/kolcthproject/Consumable_DB.txt', function (DBText)
  668. {
  669. if (DBText.charAt(0) != '{')
  670. {
  671. //downloaded DBText is not the DB we want
  672. getDBFailure('Bad');
  673. return;
  674. }
  675. //cache DB and load to top, overriding the version already loaded
  676. GM_setValue('Consumption_DB', DBText);
  677. GM_setValue('DB_VERSION', DBText.match(/([\d\.]+)/)[1]);
  678. initializeScript(DBText);
  679. }, getDBFailure);
  680.  
  681. function getDBFailure(bad)
  682. {
  683. //want to get new DB but failed
  684. GM_log(bad ? 'Bad database' : 'Download request failed');
  685. }
  686. }
  687.  
  688. function initializeScript(DB)
  689. {
  690. //make sure another script hasn't loaded it before loading DB
  691. if (!TOP.Consumption_DB)
  692. TOP.Consumption_DB = JSON.parse(DB);
  693. }
  694.  
  695. function checkForUpdates(force)
  696. {
  697. const NOW = new Date().getTime();
  698. var lastUpdateCheck = parseInt(GM_getValue('lastUpdateCheck', 0));
  699. if (NOW > lastUpdateCheck + 1209600000 || force)
  700. {
  701. const SCRIPT_NAME = "Consumption Tracker";
  702. const SCRIPT_VERSION = "1.1.6";
  703. //two weeks since last update
  704. GM_setValue('lastUpdateCheck', NOW.toString());
  705. GM_get('http://sites.google.com/site/kolcthproject/allversions.txt', function (versionText)
  706. {
  707. var allVersions = JSON.parse(versionText);
  708. var build = 0;
  709. if (allVersions[SCRIPT_NAME] != SCRIPT_VERSION)
  710. //script has new version, put download banner on main pane
  711. build++;
  712. if (allVersions.masterDB != GM_getValue('DB_VERSION', null))
  713. getDB();
  714. buildTickler(build);
  715. });
  716. }
  717. function buildTickler(build)
  718. {
  719. var alert = document.body.appendChild(CE('center', 'id|tickler', 'style|position:fixed; top:10%; left:20%; width:30%; border:solid blue; background-color:white; border-width:24px 1px 1px; padding:10px 40px;'));
  720. alert.appendChild(CE('div', 'style|position:relative; top:-32px; color:white; font-weight:bold; margin-bottom:-1em', 'text|' + SCRIPT_NAME + ' Update:'));
  721. var close = alert.appendChild(CE('img', 'src|http://images.kingdomofloathing.com/closebutton.gif', 'style|position:absolute; top:-22px; right:1px'));
  722. close.addEventListener('click', function () {this.parentNode.style.display = 'none'}, false);
  723. switch (build)
  724. {
  725. case 1:
  726. //tell user about new script version
  727. alert.appendChild(CE('a', 'text|Download new version of ' + SCRIPT_NAME, 'href|https://userscripts-mirror.org/scripts/source/96897.user.js', 'target|_blank'));
  728. break;
  729. case 0:
  730. //tell user both are fine
  731. alert.appendChild(CE('span', 'text|Script and Database are up to date'));
  732. setTimeout(function () {alert.style.display = 'none'}, 5000);
  733. break;
  734. }
  735. }
  736. }
  737.  
  738. function buildPrefs()
  739. {
  740. if (!$('#privacy'))
  741. return;
  742. if (!$('#scripts'))
  743. {
  744. //scripts tab is not built, do it here
  745. var scripts = $('ul').appendChild(CE('li', 'id|scripts'));
  746. var a = scripts.appendChild(CE('a', 'href|#'));
  747. var img = a.appendChild(CE('img', 'style|paddingRight:10px', 'border|0', 'align|absmiddle', 'src|http://images.kingdomofloathing.com/itemimages/cmonkey1.gif'));
  748. a.appendChild(document.createTextNode('Scripts'));
  749. a.addEventListener('click', function (e)
  750. {
  751. //make our new tab active when clicked, clear out the #guts div and add our settings to it
  752. e.stopPropagation();
  753. $('.active').className = '';
  754. $('#scripts').className = 'active';
  755. $('#guts').innerHTML = '<div class="scaffold"></div>';
  756. $('#guts').appendChild(getSettings());
  757. }, false);
  758. }
  759. else
  760. {
  761. //script tab already exists
  762. $('#scripts').firstChild.addEventListener('click', function (e)
  763. {
  764. //some other script is doing the activation work, just add our settings
  765. e.stopPropagation();
  766. $('#guts').appendChild(getSettings());
  767. }, false);
  768. }
  769.  
  770. function getSettings()
  771. {
  772. //build our settings and return them for appending
  773. var contents = CE('div', 'id|CTH_CT');
  774. var fieldset = contents.appendChild(CE('fieldset', 'style|margin-top:20px; width:40%'));
  775. fieldset.appendChild(CE('legend', 'class|subhead', 'text|Consumption Tracker'))
  776. var section = fieldset.appendChild(CE('div', 'class|indent'));
  777. //call function in main script to actually make the settings
  778. section.appendChild(buildSettings());
  779. return contents;
  780. }
  781.  
  782. function buildSettings()
  783. {
  784. var section = CE('div');
  785. var types = [["Liver", "drunk"], ["Stomach", "full"], ["Spleen", "spleen"]];
  786. var charName = GM_getValue('CurrentCharName');
  787. var over = 15;
  788. for (var i=0;i<3;i++)
  789. {
  790. var val = GM_getValue(charName + '.over' + types[i][1]);
  791. over = Math.max(val, over);
  792. section.appendChild(CE('span', 'text|Current over' + types[i][1] + ': ' + val));
  793. var button = section.appendChild(CE('input', 'id|' + types[i][1], 'class|button', 'type|button', 'style|margin:10px;', 'value|Steel ' + types[i][0] + '?'));
  794. section.appendChild(CE('br'));
  795. button.addEventListener('click', changeSettings, false);
  796. }
  797. if (over > 19)
  798. {
  799. for (var i=0;i<3;i++)
  800. {
  801. section.childNodes[i*3+1].disabled = true;
  802. section.childNodes[i*3+1].style.color = 'gray';
  803. }
  804. }
  805. button = section.appendChild(CE('input', 'id|reset', 'class|button', 'type|button', 'style|margin:10px 25%;', 'align|center', 'value|Reset threshholds'));
  806. button.addEventListener('click', function ()
  807. {
  808. for (var i=0;i<3;i++)
  809. {
  810. var thisButton = section.childNodes[i*3+1];
  811. var thisText = thisButton.previousSibling.textContent.match(/(.+)\s(\d+)/);
  812. if (thisText[2] > 16)
  813. thisText[2] -= 5;
  814. thisButton.previousSibling.textContent = thisText[1] + ' ' + thisText[2];
  815. thisButton.disabled = false;
  816. thisButton.style.color = 'black';
  817. GM_setValue(charName + '.overdrunk', 15);
  818. GM_setValue(charName + '.overspleen', 16);
  819. GM_setValue(charName + '.overfull', 16);
  820. }
  821. }, false);
  822. section.appendChild(CE('br'));
  823. var showFull = GM_getValue(charName + '.showFull', true);
  824. button = section.appendChild(CE('input', 'id|showFood', 'class|button', 'type|button', 'style|margin:10px 25%;', 'align|center', 'value|Track fullness: ' + showFull));
  825. button.addEventListener('click', function ()
  826. {
  827. showFull = !showFull
  828. GM_setValue(charName + '.showFull', showFull);
  829. this.value = 'Track fullness: ' + showFull
  830. }, false);
  831. section.appendChild(CE('br'));
  832. section.appendChild(CE('span', 'text|Sort consumables by: '));
  833. var select = section.appendChild(CE('select', 'style|margin:10px;'));
  834. var selVals = ["name (ascending)", "name (descending)", "full (ascending)", "full (descending)", "quality (ascending)", "quality (descending)"];
  835. for (var n=0;n<6;n++)
  836. {
  837. select.appendChild(CE('option', 'value|' + n, 'text|' + selVals[n]));
  838. if (GM_getValue('sortBy') == selVals[n])
  839. select.selectedIndex = n;
  840. }
  841. select.addEventListener('change', function ()
  842. {
  843. GM_setValue('sortBy', selVals[this.selectedIndex]);
  844. }, false);
  845. button = section.appendChild(CE('input', 'id|update', 'class|button', 'type|button', 'style|margin:10px 25%;', 'align|center', 'value|Check for updates'));
  846. button.addEventListener('click', function () {checkForUpdates(true)}, false);
  847. return section;
  848. }
  849.  
  850. function changeSettings()
  851. {
  852. var text = this.previousSibling.textContent.match(/(.+)\s(\d+)/);
  853. var val = parseInt(text[2]) + 5;
  854. this.previousSibling.textContent = text[1] + ' ' + val;
  855. for (var i=0;i<3;i++)
  856. {
  857. this.parentNode.childNodes[i*3+1].disabled = true;
  858. this.parentNode.childNodes[i*3+1].style.color = 'gray';
  859. }
  860. GM_setValue(GM_getValue('CurrentCharName') + '.over' + this.id, val);
  861. }
  862. }
  863.  
  864. function setThreshhold(which, tgt)
  865. {
  866. var newT = prompt("Change your current " + which + " amount?")
  867. if (newT && !isNaN(newT))
  868. {
  869. tgt.lastChild.textContent = newT
  870. GM_setValue(charName + '.current' + which, newT);
  871. }
  872. }