BvS Quickfield

GPS for fields

  1. // ==UserScript==
  2. // @name BvS Quickfield
  3. // @namespace http://userscripts.org/users/dtkarlsson
  4. // @description GPS for fields
  5. // @include http*://*animecubed.com/billy/bvs/villagefields*
  6. // @include http*://*animecubedgaming.com/billy/bvs/villagefields*
  7. // @licence MIT; http://www.opensource.org/licenses/mit-license.php
  8. // @copyright 2009, Daniel Karlsson
  9. // @version 1.0.4
  10. // @history 1.0.4 New domain - animecubedgaming.com - Channel28
  11. // @history 1.0.3 Now https compatible (Updated by Channel28)
  12. // @history 1.0.2 Fixed various issues (Updated by Sena)
  13. // @history 1.0.1 Added grant permissions (Updated by Channel28)
  14. // @history 1.0.0 Changed to a more console-y design
  15. // @history 1.0.0 Simplified field reordering
  16. // @history 1.0.0 Script now calculates field difficulty range
  17. // @history 1.0.0 Minor tweaks here and there
  18. // @history 0.3.0 Added key reordering by drag and drop
  19. // @history 0.2.0 Change from GM_set/getValue to DOM Storage API
  20. // @history 0.2.0 Redesigned the UI
  21. // @grant GM_log
  22. // @grant GM_addStyle
  23. // ==/UserScript==
  24. /*
  25. DOM Storage wrapper class
  26. Constructor:
  27. var store = new DOMStorage({"session"|"local"}, [<namespace>]);
  28. Set item:
  29. store.setItem(<key>, <value>);
  30. Get item:
  31. store.getItem(<key>[, <default value>]);
  32. Remove item:
  33. store.removeItem(<key>);
  34. Get all keys in namespace as array:
  35. var array = store.keys();
  36. */
  37. function DOMStorage(type, namespace)
  38. {
  39. var my = this;
  40. if (typeof(type) != "string")
  41. type = "session";
  42. switch (type) {
  43. case "local": my.storage = localStorage; break;
  44. case "session": my.storage = sessionStorage; break;
  45. default: my.storage = sessionStorage;
  46. }
  47. if (!namespace || typeof(namespace) != "string")
  48. namespace = "Greasemonkey";
  49. my.ns = namespace + ".";
  50. my.setItem = function(key, val) {
  51. try {
  52. my.storage.setItem(encodeURIComponent(my.ns + key), val);
  53. }
  54. catch (e) {
  55. GM_log(e);
  56. }
  57. },
  58. my.getItem = function(key, def) {
  59. try {
  60. var val = my.storage.getItem(encodeURIComponent(my.ns + key));
  61. if (val)
  62. return val;
  63. else
  64. return def;
  65. }
  66. catch (e) {
  67. return def;
  68. }
  69. }
  70. my.removeItem = function(key) {
  71. try {
  72. // Kludge, avoid Firefox crash
  73. my.storage.setItem(encodeURIComponent(my.ns + key), null);
  74. }
  75. catch (e) {
  76. GM_log(e);
  77. }
  78. }
  79. my.keys = function() {
  80. // Return array of all keys in this namespace
  81. var arr = [];
  82. var i = 0;
  83. do {
  84. try {
  85. if (i>my.storage.length) {
  86. throw 'error';
  87. }
  88. var key = decodeURIComponent(my.storage.key(i));
  89. if (key.indexOf(my.ns) == 0)
  90. arr.push(key.slice(my.ns.length));
  91. }
  92. catch (e) {
  93. break;
  94. }
  95. i++;
  96. } while (true);
  97. return arr;
  98. }
  99. }
  100. var DS = new DOMStorage("local", "BvSQuickfield");
  101. const KEYDIFF = {
  102. simple: 0, training: 0, aquafield: 0,
  103. dimlylit: 5, crumbling: 5, dealersroom: 5,
  104. blaring: 10, hairy: 10, parkinglot: 10,
  105. pulsating: 15, worst: 15, core: 15,
  106. hidden: 20, forbidden: 20, holyground: 20,
  107. darkside: 20,
  108. raging: 25, passionate: 25, melody: 25,
  109. cursed: 40, filthy: 40, noodleshop: 40,
  110. chosen: 60, hopeless: 60, nothingness: 60,
  111. brilliant: 65, delicious: 65, dancefloor: 65,
  112. beautiful: 70, passedover: 70, touchstone: 70,
  113. corrupted: 80, despaired: 80, paradise: 80,
  114. sickened: 100, imprisoned: 100, fallenangel: 100,
  115. merciless: 130, grieving: 130, furnace: 130
  116. };
  117. function Field()
  118. {
  119. var my = this;
  120. my.keys = [];
  121. my.notes = "";
  122. my.unpack = function(str) {
  123. if (!str || typeof(str) != "string")
  124. return false;
  125. // Restore from string "<key>|<key>|<key>|<notes>"
  126. var match = str.match(/(.*)\|(.*)\|(.*)\|(.*)/);
  127. if (match) {
  128. var field = new Field();
  129. my.keys = [];
  130. my.keys[0] = match[1];
  131. my.keys[1] = match[2];
  132. my.keys[2] = match[3];
  133. my.notes = match[4];
  134. return true;
  135. }
  136. return false;
  137. }
  138. my.pack = function() {
  139. // Return string "<key>|<key>|<key>|<notes>"
  140. return my.keys.join("|") + "|" + my.notes;
  141. }
  142. my.difficulty = function() {
  143. return KEYDIFF[keyStyle(my.keys[0])] + KEYDIFF[keyStyle(my.keys[1])] +
  144. KEYDIFF[keyStyle(my.keys[2])];
  145. }
  146. }
  147. function FieldList()
  148. {
  149. var my = this;
  150. my.fields = [];
  151. my.load = function(store) {
  152. my.fields = [];
  153. var keys = store.keys();
  154. for (var i in keys) {
  155. if (/field(\d+)/.test(keys[i])) {
  156. var index = parseInt(RegExp.lastParen);
  157. var field = new Field();
  158. if (field.unpack(store.getItem(keys[i])))
  159. my.fields[index] = field;
  160. else
  161. store.removeItem(keys[i]);
  162. } else
  163. store.removeItem(keys[i]);
  164. }
  165. }
  166. my.save = function(store) {
  167. var keys = store.keys();
  168. for (var i in keys)
  169. store.removeItem(keys[i]);
  170. var i = 0;
  171. for (var j in my.fields) {
  172. store.setItem("field" + i, my.fields[j].pack());
  173. i++;
  174. }
  175. }
  176. }
  177. var FIELDLIST = new FieldList();
  178. FIELDLIST.load(DS);
  179. // Event handlers
  180. // Update Add button and difficulty hint
  181. function updateDifficulty()
  182. {
  183. var f = new Field();
  184. f.keys = getField();
  185. var dnode = document.evaluate("//form/table/tbody/tr/td[@colspan='2'][contains(text(), 'Stamina')]", document, null,
  186. XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue;
  187. if (dnode) {
  188. var d = f.difficulty();
  189. var sta = dnode.textContent.replace(/Stamina.*/, "Stamina");
  190. dnode.textContent = sta + ", Difficulty: " + d + " - " + (d + 5) + ")";
  191. }
  192. var button = document.getElementById("addfield");
  193. if (button)
  194. button.textContent = "Add " + f.keys.join(" - ");
  195. }
  196. function buttonClick(event)
  197. {
  198. var id = event.target.getAttribute("id");
  199. if (/remove(\d+)/.test(id)) {
  200. var index = parseInt(RegExp.lastParen);
  201. removeField(index);
  202. } else if (/setfield(\d+)/.test(id)) {
  203. var index = parseInt(RegExp.lastParen);
  204. setField(FIELDLIST.fields[index].keys);
  205. updateDifficulty();
  206. } else if (/addfield/.test(id)) {
  207. addField();
  208. }
  209. }
  210. var dragging = null;
  211. var hover = null;
  212. var prevHover = null;
  213. function mousedownEvent(event)
  214. {
  215. // Find td with class = "key"
  216. var cl = event.target.getAttribute("class");
  217. if (!/key/.test(cl))
  218. return;
  219. // Get parent tr
  220. dragging = event.target.parentNode;
  221. event.preventDefault();
  222. window.addEventListener('mouseup', mouseupEvent, true);
  223. window.addEventListener('mousemove', moveEvent, true);
  224. }
  225. function moveEvent(event)
  226. {
  227. if (!dragging)
  228. return;
  229. event.preventDefault();
  230. var cl = event.target.getAttribute("class");
  231. if (!/key/.test(cl)) {
  232. window.removeEventListener('mouseup', mouseupEvent, true);
  233. window.removeEventListener('mousemove', moveEvent, true);
  234. dragging = null;
  235. hover = null;
  236. prevHover = null;
  237. }
  238. hover = event.target.parentNode;
  239. var r1 = parseInt(dragging.id.replace(/row/, ""));
  240. var r2 = parseInt(hover.id.replace(/row/, ""));
  241. if (r1 != r2) {
  242. var f = FIELDLIST.fields[r1];
  243. FIELDLIST.fields[r1] = FIELDLIST.fields[r2];
  244. FIELDLIST.fields[r2] = f;
  245. FIELDLIST.save(DS);
  246. updateTable();
  247. dragging = hover;
  248. }
  249. }
  250. function mouseupEvent(event)
  251. {
  252. if (!dragging)
  253. return;
  254. window.removeEventListener('mouseup', mouseupEvent, true);
  255. window.removeEventListener('mousemove', moveEvent, true);
  256. dragging = null;
  257. hover = null;
  258. prevHover = null;
  259. }
  260. // UI
  261. // Return currently selected item from dropdown
  262. function getSelectedItem(xpath)
  263. {
  264. var dropdown = document.evaluate(xpath, document, null,
  265. XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue;
  266. return dropdown.options[dropdown.selectedIndex].text;
  267. }
  268. // Current field keys
  269. function getField()
  270. {
  271. var keys = [];
  272. keys[0] = getSelectedItem("//select[@name='key_1']");
  273. keys[1] = getSelectedItem("//select[@name='key_2']");
  274. keys[2] = getSelectedItem("//select[@name='key_3']");
  275. return keys;
  276. }
  277. // Set field keys
  278. function setField(keys)
  279. {
  280. for (var i = 0; i < 3; i++) {
  281. var dropdown = document.evaluate("//select[@name='key_" + (i + 1) + "']", document,
  282. null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue;
  283. for (var j in dropdown.options)
  284. if (dropdown.options[j].text == keys[i]) {
  285. dropdown.selectedIndex = j
  286. break;
  287. }
  288. }
  289. uselessCrap(keys, "cd ");
  290. }
  291. // Add a field to DB
  292. function addField(keys)
  293. {
  294. if (!keys)
  295. keys = getField();
  296. var notes = prompt("Notes for " + keys.join(" - "), "-");
  297. if (notes) {
  298. var field = new Field();
  299. field.keys = keys;
  300. field.notes = notes;
  301. FIELDLIST.fields.push(field);
  302. FIELDLIST.save(DS);
  303. updateTable();
  304. uselessCrap(keys, "mkdir -p ");
  305. }
  306. }
  307. // Change field notes
  308. function changeNotes(index)
  309. {
  310. var notes = prompt("Notes for " + FIELDLIST.fields[index].keys.join(" - "),
  311. FIELDLIST.fields[index].notes);
  312. if (notes) {
  313. FIELDLIST.fields[index].notes = notes;
  314. FIELDLIST.save(DS);
  315. updateTable();
  316. }
  317. }
  318. // Remove a field from DB
  319. function removeField(index)
  320. {
  321. if (FIELDLIST.fields[index] && confirm("Remove field " + FIELDLIST.fields[index].keys.join(" - ") + "?")) {
  322. var keys = FIELDLIST.fields[index].keys;
  323. FIELDLIST.fields.splice(index, 1);
  324. FIELDLIST.save(DS);
  325. updateTable();
  326. uselessCrap(keys, "rm -rf ");
  327. }
  328. }
  329. // Create the UI table
  330. function createTable()
  331. {
  332. var addtable = document.getElementById("quickfield");
  333. if (!addtable) {
  334. addtable = document.createElement("table");
  335. addtable.setAttribute("id", "quickfield");
  336. addtable.innerHTML = '<caption><span id="cli">//Quickfield</span>' +
  337. '<span class="blink">_</span></caption>' +
  338. '<col width="auto"/><col/><col/><col/>' +
  339. '<col/><col width="auto"/><thead><tr><td/><td>Key 1</td><td>Key 2</td>' +
  340. '<td>Key 3</td><td>Notes</td><td/></tr></thead><tfoot><tr><td colspan="6">' +
  341. '<div id="addfield">Add Field</div></td></tr></tfoot><tbody/>';
  342. }
  343. var form = document.evaluate("//form[@name='field']", document, null,
  344. XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue;
  345. form.parentNode.appendChild(addtable);
  346. document.getElementById("addfield").addEventListener('click', buttonClick, false);
  347. updateTable();
  348. }
  349. // Helper: create TD with class=cls and style=sty
  350. function tableData(html, cls, sty)
  351. {
  352. var td = document.createElement("td");
  353. td.innerHTML = html;
  354. if (cls)
  355. td.setAttribute("class", cls);
  356. if (sty)
  357. td.setAttribute("style", sty);
  358. return td;
  359. }
  360. // Helper: remove non-alpha characters and convert to lower case
  361. function keyStyle(key)
  362. {
  363. if (key) {
  364. return key.replace(/\W/g, "").toLowerCase();
  365. }
  366. return "";
  367. }
  368. // Create a div with class="button" and attach eventlistener
  369. function createButton(text, id, onclick)
  370. {
  371. var div = document.createElement("div");
  372. div.appendChild(document.createTextNode(text));
  373. div.setAttribute("class", "button");
  374. div.setAttribute("id", id);
  375. div.addEventListener('click', onclick, false);
  376. return div;
  377. }
  378. // Create a field row
  379. function tableRow(n, key1, key2, key3, notes)
  380. {
  381. n = parseInt(n);
  382. var tr = document.createElement("tr");
  383. tr.setAttribute("id", "row" + n);
  384. var td = document.createElement("td");
  385. var b = createButton("X", "remove" + n, buttonClick);
  386. b.setAttribute("title", "Remove Field");
  387. td.appendChild(b);
  388. tr.appendChild(td);
  389. tr.appendChild(tableData(key1, "key " + keyStyle(key1)));
  390. tr.appendChild(tableData(key2, "key " + keyStyle(key2)));
  391. tr.appendChild(tableData(key3, "key " + keyStyle(key3)));
  392. td = document.createElement("td");
  393. td.setAttribute("class", "notes");
  394. td.appendChild(document.createTextNode(notes));
  395. td.addEventListener('mousedown', function(event) {changeNotes(n);}, false);
  396. tr.appendChild(td);
  397. td = document.createElement("td");
  398. b = createButton("Set", "setfield" + n, buttonClick);
  399. b.setAttribute("title", "Set Field");
  400. td.appendChild(b);
  401. tr.appendChild(td);
  402. return tr;
  403. }
  404. // Updates UI table, creates and removes rows if needed
  405. function updateTable()
  406. {
  407. var tbody = document.getElementById("quickfield").getElementsByTagName("tbody")[0];
  408. tbody.innerHTML = "";
  409. var i;
  410. for (i in FIELDLIST.fields) {
  411. var tr = document.getElementById("row" + i);
  412. if (tr) {
  413. // Row exists, update content
  414. var tds = tr.getElementsByTagName("td");
  415. for (var k = 0; k < 3; k++) {
  416. tds[k + 1].textContent = FIELDLIST.fields[i].keys[k];
  417. tds[k + 1].setAttribute("class", "key " + keyStyle(FIELDLIST.fields[i].keys[k]));
  418. }
  419. tds[4].textContent = FIELDLIST.fields[i].notes;
  420. tds[4].setAttribute("title", "Difficulty: " + FIELDLIST.fields[i].difficulty() + " - " +
  421. (FIELDLIST.fields[i] + 5));
  422. } else {
  423. // Create new row
  424. tr = tableRow(i, FIELDLIST.fields[i].keys[0], FIELDLIST.fields[i].keys[1],
  425. FIELDLIST.fields[i].keys[2], FIELDLIST.fields[i].notes);
  426. tr.getElementsByTagName("td")[4].setAttribute("title",
  427. "Difficulty: " + FIELDLIST.fields[i].difficulty() + " - " +
  428. (FIELDLIST.fields[i].difficulty() + 5));
  429. tbody.appendChild(tr);
  430. }
  431. }
  432. // Remove remaining rows
  433. do {
  434. var tr = document.getElementById("row" + (++i));
  435. if (tr)
  436. tr.parentNode.removeChild(tr);
  437. } while (tr);
  438. }
  439. var tehStuff = {};
  440. function uselessCrap(keys, pre)
  441. {
  442. if (tehStuff.timer)
  443. clearTimeout(tehStuff.timer);
  444. tehStuff.i = 0;
  445. tehStuff.str = pre;
  446. if (keys) {
  447. tehStuff.str += keys.join("/").replace(/\s/g, "_").toLowerCase().replace(/[^a-z_\/]/g, "");
  448. }
  449. tehStuff.el = document.getElementById("cli");
  450. tehStuff.timer = setTimeout(moarCrap, 50);
  451. }
  452. function moarCrap()
  453. {
  454. if (tehStuff.i > tehStuff.str.length) {
  455. tehStuff.timer = null;
  456. } else {
  457. tehStuff.el.textContent = "# " + tehStuff.str.substr(0, tehStuff.i++);
  458. tehStuff.timer = setTimeout(moarCrap, 50);
  459. }
  460. }
  461. GM_addStyle("table#quickfield {width: 100%; background-color: black; color: rgb(33%, 100%, 0%); border-collapse: collapse; font-size: 12px;}");
  462. GM_addStyle("#quickfield thead {font-weight: bold;}");
  463. GM_addStyle("#quickfield caption {background-color: black; font-size: 18px; font-weight: bold; margin-top: 6px;}");
  464. GM_addStyle("#quickfield td {border: 1px solid black; text-align: center;}");
  465. GM_addStyle("#quickfield input {width: auto;}");
  466. GM_addStyle("#quickfield td.key {font-variant: small-caps; cursor: move;}");
  467. GM_addStyle("#quickfield td.notes {color: rgb(100%, 66%, 0%); cursor: pointer;}");
  468. GM_addStyle("#addfield {border: 1px solid rgb(25%, 75%, 0%); background-color: black; color: rgb(25%, 75%, 0%); cursor: pointer;}");
  469. GM_addStyle("#addfield:hover {border: 1px solid rgb(33%, 100%, 0%); background-color: rgb(16%, 50%, 0%); color: rgb(33%, 100%, 0%);}");
  470. GM_addStyle("div.button {border: 1px solid rgb(25%, 75%, 0%); background-color: black; color: rgb(25%, 75%, 0%); cursor: pointer;}");
  471. GM_addStyle("div.button:hover {border: 1px solid rgb(33%, 100%, 0%); background-color: rgb(16%, 50%, 0%); color: rgb(33%, 100%, 0%);}");
  472. GM_addStyle("span.blink {text-decoration: blink;}"); // YA RLY
  473. // tier 1 key style
  474. GM_addStyle(".simple {color: #FFFFFF; background-color: #006600;}");
  475. GM_addStyle(".training {color: #FFFFFF; background-color: #006666;}");
  476. GM_addStyle(".aquafield {color: #FFFFFF; background-color: #006600;}");
  477. // tier 2 key style
  478. GM_addStyle(".dimlylit {color: #FFFFFF; background-color: #006666;}");
  479. GM_addStyle(".crumbling {color: #FFFFFF; background-color: #006600;}");
  480. GM_addStyle(".dealersroom {color: #FFFFFF; background-color: #006666;}");
  481. // tier 3 key style
  482. GM_addStyle(".blaring {color: #FFFFFF; background-color: #660000;}");
  483. GM_addStyle(".hairy {color: #FFFFFF; background-color: #660000;}");
  484. GM_addStyle(".parkinglot {color: #FFFFFF; background-color: #660000;}");
  485. // tier 4 key style
  486. GM_addStyle(".pulsating {color: #FFFFFF; background-color: #666600;}");
  487. GM_addStyle(".worst {color: #FFFFFF; background-color: #663300;}");
  488. GM_addStyle(".core {color: #FFFFFF; background-color: #666666;}");
  489. // tier 5 key style
  490. GM_addStyle(".hidden {color: #000000; background-color: #CCCC00;}");
  491. GM_addStyle(".forbidden {color: #000000; background-color: #CCCC00;}");
  492. GM_addStyle(".holyground {color: #000000; background-color: #CCCC00;}");
  493. // tier 6 key style
  494. GM_addStyle(".raging {color: #000000; background-color: #CC0000;}");
  495. GM_addStyle(".passionate {color: #000000; background-color: #CC0000;}");
  496. GM_addStyle(".melody {color: #000000; background-color: #CC0000;}");
  497. // tier 7 key style
  498. GM_addStyle(".cursed {color: #FFFFFF; background-color: #006666;}");
  499. GM_addStyle(".filthy {color: #FFFFFF; background-color: #006666;}");
  500. GM_addStyle(".noodleshop {color: #FFFFFF; background-color: #006666;}");
  501. // tier 8 key style
  502. GM_addStyle(".chosen {color: #FFFFFF; background-color: #000000;}");
  503. GM_addStyle(".hopeless {color: #FFFFFF; background-color: #000000;}");
  504. GM_addStyle(".nothingness {color: #FFFFFF; background-color: #000000;}");
  505. // tier 9 key style
  506. GM_addStyle(".brilliant {color: #FFFFCC; background-color: #666600;}");
  507. GM_addStyle(".delicious {color: #FFFFCC; background-color: #666600;}");
  508. GM_addStyle(".dancefloor {color: #FFFFCC; background-color: #666600;}");
  509. // tier 10 key style
  510. GM_addStyle(".beautiful {color: #FFCCCC; background-color: #660000;}");
  511. GM_addStyle(".passedover {color: #FFCCCC; background-color: #660000;}");
  512. GM_addStyle(".touchstone {color: #FFCCCC; background-color: #660000;}");
  513. // tier 11 key style
  514. GM_addStyle(".corrupted {color: #660000; background-color: #FF5555;}");
  515. GM_addStyle(".despaired {color: #660000; background-color: #FF5555;}");
  516. GM_addStyle(".paradise {color: #660000; background-color: #FF5555;}");
  517. // tier 12 key style
  518. GM_addStyle(".sickened {color: #222222; background-color: #DDDDDD;}");
  519. GM_addStyle(".imprisoned {color: #222222; background-color: #DDDDDD;}");
  520. GM_addStyle(".fallenangel {color: #222222; background-color: #DDDDDD;}");
  521. createTable();
  522. document.getElementById("quickfield").addEventListener('mousedown', mousedownEvent, true);
  523. for (var i = 1; i < 4; i++) {
  524. var input = document.evaluate("//select[@name='key_" + i + "']", document, null,
  525. XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue;
  526. if (input)
  527. input.addEventListener('change', updateDifficulty, false);
  528. }
  529. updateDifficulty();

QingJ © 2025

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