您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tabulated and sorted spy report for Billy vs SNAKEMAN.
当前为
// ==UserScript== // @name BvS Improved Spy Report // @namespace http://userscripts.org/users/dtkarlsson // @description Tabulated and sorted spy report for Billy vs SNAKEMAN. // @include http*://*animecubed.com/billy/bvs/village.* // @include http*://*animecubed.com/billy/bvs/villagespyreport.* // @include http*://*animecubedgaming.com/billy/bvs/village.* // @include http*://*animecubedgaming.com/billy/bvs/villagespyreport.* // @match http*://*animecubed.com/billy/bvs/village.* // @match http*://*animecubed.com/billy/bvs/villagespyreport.* // @match http*://*animecubedgaming.com/billy/bvs/village.* // @match http*://*animecubedgaming.com/billy/bvs/villagespyreport.* // @licence MIT; http://www.opensource.org/licenses/mit-license.php // @copyright 2009, Daniel Karlsson (http://userscripts.org/users/dtkarlsson) // @version 1.2.3 // @history 1.2.3 New domain - animecubedgaming.com - Channel28 // @history 1.2.2 Now https compatible (Updated by Channel28) // @history 1.2.1 Added grant permissions (Updated by Channel28) // @history 1.2.0 Added tier filter options // @history 1.2.0 Stable sort for Chrome // @history 1.1.1 Support for Google Chrome // @grant GM_log // ==/UserScript== var log; if (console && console.log) log = console.log; else if (GM_log) log = GM_log; else log = function() {} /* Village objects have the following members: name - Village name (string) order - Original spy report order (integer, 1 - <n>) ryo - Ryo Stores (integer) bc - Brilliant Crystals (integer) mw - Medicinal Water (integer) pm - Precious Metals (integer) sf - Solid Fire (integer) ui - Unmelting Ice (integer) defense - Village defended against (string) target - Current target (string) attackers - Number of villages attacking (integer) tier - 0 - Pre-NRF, 1 - NRF, 2 - Beacon peace - Peacetime (integer) weight - Sorting weight (number) */ function Village() { // Init members that might be missing from spy report this.defense = ""; this.target = ""; this.peace = 0; this.tier = 0; } /* DOM Storage wrapper class Constructor: var store = new DOMStorage({"session"|"local"}, [<namespace>]); Set item: store.setItem(<key>, <value>); Get item: store.getItem(<key>[, <default value>]); Remove item: store.removeItem(<key>); Get all keys in namespace as array: var array = store.keys(); */ function DOMStorage(type, namespace) { var my = this; if (typeof(type) != "string") type = "session"; switch (type) { case "local": my.storage = localStorage; break; case "session": my.storage = sessionStorage; break; default: my.storage = sessionStorage; } if (!namespace || typeof(namespace) != "string") namespace = "Userscript"; my.ns = namespace + "."; my.setItem = function(key, val) { try { my.storage.setItem(escape(my.ns + key), val); } catch (e) { log(e); } }, my.getItem = function(key, def) { try { var val = my.storage.getItem(escape(my.ns + key)); if (val) return val; else return def; } catch (e) { return def; } } my.removeItem = function(key) { try { // Kludge, avoid Firefox crash my.storage.setItem(escape(my.ns + key), null); } catch (e) { log(e); } } my.keys = function() { // Return array of all keys in this namespace var arr = []; var i = 0; do { try { var key = unescape(my.storage.key(i)); if (key.indexOf(my.ns) == 0 && my.storage.getItem(key)) arr.push(key.slice(my.ns.length)); } catch (e) { break; } i++; } while (true); return arr; } } // Merge sort: merge function function _merge(array, left, right, cmp) { var ai = 0; var li = 0; var ri = 0; while (li < left.length && ri < right.length) if (cmp(left[li], right[ri]) <= 0) array[ai++] = left[li++]; else array[ai++] = right[ri++]; while (li < left.length) array[ai++] = left[li++]; while (ri < right.length) array[ai++] = right[ri++]; } // Merge sort function mergeSort(array, cmp) { if (array.length <= 1) return array; var left = []; var right = []; var mid = Math.floor(array.length / 2); // Divide for (var i = 0; i < mid; i++) left.push(array[i]); for (var i = mid; i < array.length; i++) right.push(array[i]); // Sort parts mergeSort(left, cmp); mergeSort(right, cmp); // Merge parts _merge(array, left, right, cmp); } function getPlayerName() { return document.evaluate("//input[@name='player']", document, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue.value; } function f(n) { if (!n) return 0; // mapping from [0, inf) to [0, 1) return n / (n + 1); } function weight(village, wbc, wmw, wpm, wsf, wui) { // Village resource weight used for sorting, higher weight on top var w = 0; var wsum = wbc + wmw + wpm + wsf + wui; if (wsum == 0) return 0; // Tiebreaker w = f(village.bc) * wbc + f(village.mw) * wmw + f(village.pm) * wpm + f(village.sf) * wsf + f(village.ui) * wui; w /= wsum; for (var n = 4; n >= 0; n--) { if (village.bc > n) w += wbc; if (village.mw > n) w += wmw; if (village.pm > n) w += wpm; if (village.sf > n) w += wsf; if (village.ui > n) w += wui; w /= wsum; } return w; } function numberToString(n) { // Add thousand separator // 1234567 -> "1,234,567" if (n < 0) return "-" + numberToString(-n); var str = "" + Math.round(n); return str.replace(/\d{1,3}(?=(\d{3})+(?!\d))/g, "$&,"); } function stringToNumber(n) { // n should be a string, possibly with "," as thousand separator if (n) return parseInt(n.replace(/,/g, "")); return 0; } function cmpOrder(v1, v2) { // Sort by ascending order return cmpTier(v1, v2) || v1.order - v2.order; } function cmpAttackers(v1, v2) { // Sort by ascending number of attackers return cmpTier(v1, v2) || v1.attackers - v2.attackers; } function cmpName(v1, v2) { // Sort by ascending name if (cmpTier(v1, v2)) return cmpTier(v1, v2); if (v1.name < v2.name) return -1; else if (v1.name > v2.name) return 1; else return 0; } function cmpPeace(v1, v2) { // Sort by ascending peacetime return cmpTier(v1, v2) || v1.peace - v2.peace; } function cmpRyo(v1, v2) { // Sort by descending ryo return cmpTier(v1, v2) || v2.ryo - v1.ryo; } function cmp9(v1, v2) { // Sort by descending UI return cmpTier(v1, v2) || v2.ui - v1.ui; } function cmpDefense(v1, v2) { // Sort by ascending defense name if (cmpTier(v1, v2)) return cmpTier(v1, v2); if (v1.defense < v2.defense) return -1; else if (v1.defense > v2.defense) return 1; else return 0; } function cmpTarget(v1, v2) { // Sort by ascending target if (cmpTier(v1, v2)) return cmpTier(v1, v2); if (v1.target < v2.target) return -1; else if (v1.target > v2.target) return 1; else return 0; } function cmpRes(v1, v2) { // Sort by descending resources return cmpTier(v1, v2) || v2.weight - v1.weight; } function cmpTier(v1, v2) { // Sort by Tier return v2.tier - v1.tier; } // Global-ish variables var tierFilter = [true, true, true]; var villageList; var comparator = [ cmpOrder, cmpAttackers, cmpName, cmpPeace, cmpRyo, cmpRes, cmpRes, cmpRes, cmpRes, cmpRes, cmpDefense, cmpTarget ]; function setButton(n, state) { var b = document.getElementById("isr_button" + n); if (state) b.setAttribute("class", "active"); else b.removeAttribute("class"); } function toggleButton(n) { setButton(n, !isActive(n)); } function isActive(n) { // Return true is button <n> is activated if (/active/.test(document.getElementById("isr_button" + n).getAttribute("class"))) return true; return false; } function sortOption(n) { // Click event handler sends button id, grab number from the id var match = n.match(/\d+/); if (match) n = parseInt(match[0]); else return; if (n >= 5 && n <= 9) { for (var i = 1; i <= 4; i++) setButton(i, false); for (var i = 10; i <= 11; i++) setButton(i, false); toggleButton(n); } else for (var i = 1; i <= 11; i++) if (i == n) toggleButton(i); else setButton(i, false); n = 0; for (var i = 1; i <= 11; i++) if (isActive(i)) { n = i; break; } if (n >= 5 && n <= 9) { // Sorting by resources, recalculate weights for (var i = 0; i < villageList.length; i++) { var v = villageList[i]; v.weight = weight(v, isActive(5) ? 1 : 0, isActive(6) ? 1 : 0, isActive(7) ? 1 : 0, isActive(8) ? 1.1 : 0, isActive(9) ? 1.1 : 0); } } mergeSort(villageList, comparator[n]); fillSpyTable(); } function createElement(tag, attributes, html) { var node = document.createElement(tag); if (attributes) { for (var a in attributes) node.setAttribute(a, attributes[a]); } if (html) { node.innerHTML = html; } return node; } // Create table row and fill with td from array function tableRow(data) { var tr = document.createElement("tr"); for (var i in data) { var td = document.createElement("td"); tr.appendChild(td); switch (typeof(data[i])) { case "string": case "number": td.textContent = data[i]; break; case "object": if (data[i].element) td.appendChild(data[i].element); if (data[i].text) td.textContent = data[i].text; if (data[i].html) td.innerHTML = data[i].html; if (data[i].cls) td.setAttribute("class", data[i].cls); if (data[i].id) td.setAttribute("id", data[i].id); break; } } return tr; } function addHeaderNode(tag, type, text) { var head = document.getElementsByTagName('head')[0]; if (!head) return; var node = document.createElement(tag); node.type = type; node.textContent = text; head.appendChild(node); } function findSpyReportTd() { // villagespyreport.html spy report var bSnap = document.evaluate("//tbody/tr/td/b[1]", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); for (var i = 0; i < bSnap.snapshotLength; i++) if (/spy report/i.test(bSnap.snapshotItem(i).innerHTML)) return bSnap.snapshotItem(i).parentNode; return null; } function parseSpyReport() { // node should be the <td> containing spy report var spyTable = findSpyReportTd(); if (!spyTable) return null; // Scan the spy report and collect spy data villageList = []; var villageMap = {}; var header; var currentVillage = new Village(); for (var line = spyTable.firstChild; line != null; line = line.nextSibling) { if (line.tagName) { if (/b/i.test(line.tagName) && !header) { header = line.textContent; } else if (/hr/i.test(line.tagName)) { // Village done, don't store unless we found a name if (currentVillage.name) { villageList.push(currentVillage); villageMap[currentVillage.name] = villageList.length - 1; } currentVillage = new Village(); } continue; } // Skip non-text nodes if (line.nodeType != 3) continue; var txt = line.textContent; if (/NRF Found/.test(txt)) currentVillage.tier = 1; else if (/Beacon Found/.test(txt)) currentVillage.tier = 2; else if (/Defended.from\:.(.*).Village/.test(txt)) currentVillage.defense = RegExp.lastParen; else if (/Attack Plans: (.*) Village/.test(txt)) currentVillage.target = RegExp.lastParen; else if (/Current Stores: (.*) Ryo/.test(txt)) currentVillage.ryo = stringToNumber(RegExp.lastParen); else if (/Brilliant Crystals: (\d+)/.test(txt)) currentVillage.bc = stringToNumber(RegExp.lastParen); else if (/Medicinal Water: (\d+)/.test(txt)) currentVillage.mw = stringToNumber(RegExp.lastParen); else if (/Precious Metals: (\d+)/.test(txt)) currentVillage.pm = stringToNumber(RegExp.lastParen); else if (/Solid Fire: (\d+)/.test(txt)) currentVillage.sf = stringToNumber(RegExp.lastParen); else if (/Unmelting Ice: (\d+)/.test(txt)) currentVillage.ui = stringToNumber(RegExp.lastParen); else if (/Peacetime Bonus: (.?\d+)/.test(txt)) currentVillage.peace = stringToNumber(RegExp.lastParen); else if (/(.*) Village/.test(txt)) currentVillage.name = RegExp.lastParen; } if (currentVillage.name) { villageList.push(currentVillage); villageMap[currentVillage.name] = villageList.length - 1; } // Calculate weight and assign ordinal numbers var n = 1; for (var i in villageList) { villageList[i].weight = weight(villageList[i], 1, 1, 1, 1.1, 1.1); villageList[i].order = n++; villageList[i].attackers = 0; } for (var v1 in villageList) { if (villageList[v1].target) { var v2 = villageMap[villageList[v1].target]; if (v2) { if (villageList[v1].tier == villageList[v2].tier) villageList[v2].attackers++; else villageList[v1].target = "(" + villageList[v1].target + ")"; } } if (villageList[v1].defense) { var v2 = villageMap[villageList[v1].defense]; if (v2 && villageList[v1].tier != villageList[v2].tier) villageList[v1].defense = "(" + villageList[v1].defense + ")"; } } header = header.replace(/.*(Spy Report.*\)).*/, "$1"); return header; } function createSpyTable(id, header) { // Create table, headers and empty tbody var table = document.createElement("table"); table.id = id; table.innerHTML = "<thead><tr class='isrtitle'><th colspan='11'/></tr>" + "<tr class='isrbuttonrow'>" + "<th id='isr_button1'>#Att</th>" + "<th id='isr_button2'>Village</th>" + "<th id='isr_button3'>Peace</th>" + "<th id='isr_button4'>Ryo</th>" + "<th id='isr_button5' class='active'>BC</th>" + "<th id='isr_button6' class='active'>MW</th>" + "<th id='isr_button7' class='active'>PM</th>" + "<th id='isr_button8' class='active'>SF</th>" + "<th id='isr_button9' class='active'>UI</th>" + "<th id='isr_button11'>Target</th>" + "<th id='isr_button10'>Defense</th>" + "</tr></thead>"; table.getElementsByTagName("th")[0].innerHTML = header; var tbody = document.createElement("tbody"); table.appendChild(tbody); tbody.id = "isrbody"; return table; } function fillSpyTable() { // Get saved spy insertion times var player = getPlayerName(); var db = new DOMStorage("local", "BvSImprovedSpyReport." + player); var infiltrating = {}; var inf = db.getItem("infiltrating", "").split(":"); for (var i = 0; i < inf.length; i++) { var match = inf[i].match(/(.*)=(.*)/); if (match) infiltrating[match[1]] = parseInt(match[2]); } var date = new Date(); var age = parseInt(db.getItem("timestamp", "0")); age = Math.floor((date.getTime() - age) / 3600 / 1000); // Fill in <tbody> var tbody = document.getElementById("isrbody"); tbody.textContent = ""; var even = true; for (var i in villageList) { var v = villageList[i]; if (!tierFilter[v.tier]) continue; var data = []; data.push({cls: "number", text: v.attackers}); if (infiltrating[v.name]) { // Village is still being infiltraded according to latest seen village.html spy list var t = infiltrating[v.name] - age; if (t < 0) data.push({cls: "name", html: "<a href='./vlookup.html?village=" + v.name + "'>" + v.name + "</a> (0 h?)"}); else data.push({cls: "name", html: "<a href='./vlookup.html?village=" + v.name + "'>" + v.name + "</a> (" + t + " h)"}); } else data.push({cls: "name", html: "<a href='./vlookup.html?village=" + v.name + "'>" + v.name + "</a>"}); data.push({cls: "peace", text: v.peace ? "+" + v.peace : ""}); data.push({cls: "ryo", text: numberToString(v.ryo)}); data.push({cls: "resource", text: v.bc}); data.push({cls: "resource", text: v.mw}); data.push({cls: "resource", text: v.pm}); data.push({cls: "resource", text: v.sf}); data.push({cls: "resource", text: v.ui}); if (v.target[0] == "(") data.push({cls: "name oot", text: v.target}); else data.push({cls: "name", text: v.target}); if (v.defense[0] == "(") data.push({cls: "name oot", text: v.defense}); else data.push({cls: "name", text: v.defense}); var tr = tableRow(data); tbody.appendChild(tr); tr.setAttribute("class", (even ? "even" : "odd") + " tier" + v.tier); even = !even; } } function improveSpyReport() { // Insert stylesheet addHeaderNode("style", "text/css", "table#improvedspyreport {width: 525px; font-size: 12px; " + "background-color: black; border-spacing: 1px;}\n" + "#improvedspyreport .isrtitle {font-size: 15px;}\n" + "#improvedspyreport thead {background-color: rgb(220, 180, 140); font-weight: bold; " + "text-align: center;}\n" + "#improvedspyreport th {border: 1px outset rgb(220, 180, 140);}\n" + "#improvedspyreport td {padding: 1px;}\n" + ".isrbuttonrow th {cursor: pointer;}\n" + ".isrbuttonrow th.active {border: 1px inset rgb(200, 160, 125) !important; color: yellow;}\n" + "#isrbody a {font-weight: bold;}\n" + "#isrbody .oot {color: grey;}\n" + "#isrbody .even.tier2 {background-color: rgb(234, 216, 195);}\n" + "#isrbody .odd.tier2 {background-color: rgb(222, 202, 168);}\n" + "#isrbody .even.tier1 {background-color: rgb(195, 216, 234);}\n" + "#isrbody .odd.tier1 {background-color: rgb(168, 202, 222);}\n" + "#isrbody .even.tier0 {background-color: rgb(195, 234, 216);}\n" + "#isrbody .odd.tier0 {background-color: rgb(168, 222, 202);}\n" + "#isrbody tr.tier0:hover {background-color: rgb(255, 255, 95);}\n" + "#isrbody tr.tier1:hover {background-color: rgb(255, 255, 95);}\n" + "#isrbody tr.tier2:hover {background-color: rgb(255, 255, 95);}\n" + "#isrbody td.number {text-align: center;}\n" + "#isrbody td.peace {text-align: center;}\n" + "#isrbody td.ryo {text-align: right;}\n" + "#isrbody td.name {overflow: hidden;}\n" + "#isrbody td.resource {width: 24px; text-align: right;}\n" + "#isrtierselect {padding: 0; text-align: center;}\n" + "#isrtierselect li {display: inline; margin: 0 2px 0 2px; padding: 2px 3px 2px 3px; " + "cursor: pointer; font-weight: bold;}\n" + "#isrtierselect .button {border: 2px outset grey;}\n" + "#isrtierselect .activebutton {border: 2px inset grey;}\n" + "#isrtierselect .tier2 {border-color: rgb(222, 202, 168); background-color: rgb(222, 202, 168);}\n" + "#isrtierselect .tier1 {border-color: rgb(168, 202, 222); background-color: rgb(168, 202, 222);}\n" + "#isrtierselect .tier0 {border-color: rgb(168, 222, 202); background-color: rgb(168, 222, 202);}"); // Find spy report <td> node var spyTable = findSpyReportTd(); if (!spyTable) return; var header = parseSpyReport(); if (!header) return; // Go to <table> parent while (!/table/i.test(spyTable.tagName)) spyTable = spyTable.parentNode; var player = getPlayerName(); var db = new DOMStorage("local", "BvSImprovedSpyReport." + player); // Sort by resources mergeSort(villageList, cmpRes); var header = "- " + header + " (" + villageList.length + " villages) -"; var improvedSpyTable = createSpyTable("improvedspyreport", header); spyTable.parentNode.replaceChild(improvedSpyTable, spyTable); // Tier filter tierFilter = []; var t = db.getItem("tiers", "0:1:2").split(/:/); for (var i in t) tierFilter[parseInt(t[i])] = true; var tiersel = document.createElement("ul"); tiersel.id = "isrtierselect"; for (var t = 2; t >= 0; t--) { var li = document.createElement("li"); if (tierFilter[t]) li.setAttribute("class", "activebutton tier" + t); else li.setAttribute("class", "button tier" + t); switch (t) { case 2: li.textContent = "Beacon (Tier 3)"; break; case 1: li.textContent = "NRF (Tier 2)"; break; case 0: li.textContent = "Basic (Tier 1)"; break; } li.id = "isrtier" + t; tiersel.appendChild(li); li.addEventListener("click", function(event) { if (/isrtier(\d+)/.test(event.target.id)) { var tier = parseInt(RegExp.lastParen); tierFilter[tier] = !tierFilter[tier]; if (tierFilter[tier]) event.target.setAttribute("class", "activebutton tier" + tier); else event.target.setAttribute("class", "button tier" + tier); fillSpyTable(); var save = []; for (var t = 0; t < 3; t++) if (tierFilter[t]) save.push(t); db.setItem("tiers", save.join(":")); event.preventDefault(); } }, true); } improvedSpyTable.parentNode.insertBefore(tiersel, improvedSpyTable); var date = new Date(); var age = parseInt(db.getItem("timestamp", "0")); age = Math.floor((date.getTime() - age) / 3600 / 1000); if (age > 0) { var div = document.createElement("div"); improvedSpyTable.parentNode.insertBefore(div, improvedSpyTable); div.setAttribute("style", "font-size: 14px; text-align: center; font-weight: bold;"); div.innerHTML = "Spy insertion times updated " + age + " hours ago. Check Village/Show All " + "Spies to update."; } fillSpyTable(); // Listen to click events on the button row // send element id to sortOption function document.getElementById("isr_button1").parentNode.addEventListener( 'mousedown', function(event) { sortOption(event.target.getAttribute("id")); return; }, true); } function scanSpyList() { // village.html spy list var spies = document.evaluate("//table/tbody/tr/td/div/ul/li", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); var found = false; var infiltrating = []; for (var i = 0; i < spies.snapshotLength; i++) { var spy = spies.snapshotItem(i).innerHTML; // Match "Villagename (xxH to go)" and "Villagename" // where villagename can be one or two words consisting of letters and digits // "Foo Bar (12H to go)" => ["Foo Bar (12H to go)", "Foo Bar", "Bar", " (12H to go)", "12"] // "Foo Bar" => ["Foo Bar", "Foo Bar", "Bar", undefined, undefined] var match = spy.match(/^([\w\d]+(\s[\w\d]+)?)(\s\((\d+)H to go\))?$/) if (match) { found = true; if (match[4]) infiltrating.push(match[1] + "=" + match[4]); } } if (found) { var player = getPlayerName(); var db = new DOMStorage("local", "BvSImprovedSpyReport." + player); var date = new Date(); db.setItem("timestamp", "" + date.getTime()); db.setItem("infiltrating", infiltrating.join(":")); } } // main if (/billy.bvs.villagespyreport\./.test(location.href)) improveSpyReport(); else if (/billy.bvs.village\./.test(location.href)) scanSpyList();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址