您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Unified skills table w/ radio buttons, maxed indicators, sorting, hidden inputs preserved, and action buttons on **BvS Zombjas Skills** page.
// ==UserScript== // @name BvS Zombjas Unified Skills Table (2025) // @namespace bvs // @version 2.2.0 // @description Unified skills table w/ radio buttons, maxed indicators, sorting, hidden inputs preserved, and action buttons on **BvS Zombjas Skills** page. // @author itsnyxtho // @icon https://github.com/itsnyxtho/bvs/blob/main/other/images/anime_cubed-icon.png?raw=true // @include http*://*animecubed*/billy/bvs/zombjaskills.html // @grant none // @license MIT // @disclaimer These scripts are provided 'as is' and without warranty of any kind. Use of these scripts is at your own risk and discretion. // ==/UserScript== (function () { const style = document.createElement("style"); style.textContent = ` table[width="400"] { width: 550px !important; } td[width="400"] { width: unset !important; } .bvs-skill-table { background-color: #000; color: #fff; border-collapse: collapse; width: 100%; font-family: 'Fira Code', monospace; font-size: 10px; margin-top: 10px; } .bvs-skill-table th, .bvs-skill-table td { border: 1px solid #444; padding: 6px; vertical-align: middle; } .bvs-skill-table th { background-color: #111; cursor: pointer; user-select: none; white-space: nowrap; } .bvs-skill-table td.name { text-align: right; font-weight: 500; white-space: nowrap; } .bvs-skill-table td.level { text-align: center; } .bvs-skill-table td.desc { text-align: left; } .bvs-skill-table td.cost { text-align: right; } .bvs-skill-table td.select { text-align: center; } .bvs-skill-lvl-1 { color: LimeGreen; } .bvs-skill-lvl-2 { color: DarkTurquoise; } .bvs-skill-lvl-3 { color: DeepSkyBlue; } .bvs-skill-lvl-4 { color: MediumOrchid; } .bvs-skill-lvl-5 { color: MediumVioletRed; } .bvs-upgradable { background-color: rgba(255,255,255,0.05); } .bvs-disabled { opacity: 0.5; } .bvs-sort-asc::after { content: " ▲"; } .bvs-sort-desc::after { content: " ▼"; } form[name="bskills"] > button { height: 28px; padding: 4px 12px; margin-top: 6px; color: white; font-family: 'Fira Code', monospace; font-weight: 500; font-size: 10px; cursor: pointer; display: flex; align-items: center; justify-content: center; justify-items: center; } #bvs-buy-skill-btn { float: right; background-color: #a00; border: 1px solid #900; } #bvs-zrewards-btn { background-color: #075; border: 1px solid #064; margin: 6px auto 0 auto; } #bvs-return-btn { float: left; background-color: #05a; border: 1px solid #049; } `; document.head.appendChild(style); const firaCodeFont = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/distr/fira_code.css">`; document.head.innerHTML += firaCodeFont; const td = [...document.querySelectorAll("font")].find((f) => f?.textContent?.trim() === "Your Skills:")?.closest("td"); const form = document.querySelector('form[name="bskills"]'); if (!td || !form) return; const skillLevels = {}; td.innerHTML.replace(/<b><i>([^<]+)<\/i>: <font[^>]+>(\d+)<\/font><\/b>/g, (_, name, level) => { skillLevels[name] = parseInt(level); return ""; }); const skillListFont = [...td.querySelectorAll("font")].find((f) => f?.textContent?.includes("Skill List:")); const descriptions = {}; let node = skillListFont?.nextSibling; while (node) { if (node.nodeType === 1 && node.tagName === "B") { const name = node?.textContent?.trim(); const desc = node?.nextSibling?.nodeType === 3 ? node?.nextSibling?.textContent?.replace(/^:\s*/, "").trim() : ""; if (desc && name) descriptions[name] = desc; } const next = node?.nextSibling; node?.remove(); node = next; } // === Build Skill Rows from Buy Skills const skillRows = []; const radios = [...form.querySelectorAll('input[type="radio"]')]; const hiddenInputs = [...form.querySelectorAll('input[type="hidden"]')]; const radioParent = radios[0]?.parentElement; const radioLines = radioParent?.innerHTML.split(/<br\s*\/?>|\n/i).filter((x) => x.includes('type="radio"')); radioParent.innerHTML = ""; radioLines.forEach((line) => { const match = line.match(/<input[^>]+>(?:\s*)<b>([^<]+):<\/b> Current Level (\d+) \(Cost to Upgrade: ([^<]+)\)/); if (!match) return; const [, name, levelStr, costText] = match; const level = parseInt(levelStr); const wrapper = document.createElement("div"); wrapper.innerHTML = line; const input = wrapper.querySelector("input"); const isMax = level > 4; const cost = isMax ? "Maxed!" : costText; const selectContent = isMax ? "✔✔" : ""; skillRows.push({ name, level, cost, input, selectContent, desc: descriptions[name] || "", class: input.disabled ? "bvs-disabled" : !isMax ? "bvs-upgradable" : "", }); }); const allNames = new Set([...Object.keys(skillLevels), ...Object.keys(descriptions)]); for (const name of allNames) { const isMax = skillLevels[name] > 4; const cost = isMax ? "Maxed!" : "-"; const className = isMax ? "bvs-skill-lvl-5" : ""; const selectContent = isMax ? "✔✔" : ""; if (!skillRows.some((r) => r.name === name)) { skillRows.push({ name, level: skillLevels[name] || 0, cost: cost || "", input: null, selectContent: selectContent || "", desc: descriptions[name] || "", class: className || "", }); } } // === Create Table const table = document.createElement("table"); table.className = "bvs-skill-table"; const thead = document.createElement("thead"); thead.innerHTML = ` <tr> <th data-sort="name">Skill</th> <th data-sort="level">Level</th> <th>Description</th> <th>Upgrade Cost</th> <th>Select</th> </tr> `; const tbody = document.createElement("tbody"); table.append(thead, tbody); const renderRows = (rows) => { tbody.innerHTML = ""; rows.forEach(({ name, level, cost, desc, input, selectContent, class: rowClass }) => { const tr = document.createElement("tr"); tr.className = rowClass; tr.innerHTML = ` <td class="name">${name}</td> <td class="level bvs-skill-lvl-${Math.min(level, 5)}">${level}</td> <td class="desc">${desc}</td> <td class="cost">${cost}</td> <td class="select">${selectContent}</td> `; if (input) { tr.querySelector(".select").appendChild(input); tr.addEventListener("click", () => input.click()); } tbody.appendChild(tr); }); }; renderRows(skillRows); const returnBtn = document.createElement("button"); returnBtn.id = "bvs-return-btn"; returnBtn.textContent = "<|| Zombjas"; returnBtn.type = "button"; returnBtn.addEventListener("click", () => { form.action = "/billy/bvs/zombjas.html"; form.submit(); }); const zRewardsBtn = document.createElement("button"); zRewardsBtn.id = "bvs-zrewards-btn"; zRewardsBtn.textContent = "Z Rewards"; zRewardsBtn.type = "button"; zRewardsBtn.addEventListener("click", () => { form.action = "/billy/bvs/zombjarewards.html"; form.submit(); }); const buyBtn = document.createElement("button"); buyBtn.id = "bvs-buy-skill-btn"; buyBtn.textContent = "Buy Skill ||>"; buyBtn.type = "submit"; // === Clear form and rebuild with hidden fields + new layout form.innerHTML = ""; hiddenInputs.forEach((input) => form.appendChild(input)); form.appendChild(table); form.appendChild(buyBtn); form.appendChild(returnBtn); form.appendChild(zRewardsBtn); // Delete "ref" form const refForm = document.querySelector('form[name="ref"]'); if (refForm) { refForm.remove(); } // Sort let currentSort = { key: "name", dir: "asc" }; const sortTable = (key) => { const dir = currentSort.key === key && currentSort.dir === "asc" ? "desc" : "asc"; currentSort = { key, dir }; const rows = [...tbody.querySelectorAll("tr")]; rows.sort((a, b) => { const aText = a.querySelector(`td.${key}`)?.textContent.trim().toLowerCase() || ""; const bText = b.querySelector(`td.${key}`)?.textContent.trim().toLowerCase() || ""; return key === "level" ? (dir === "asc" ? aText - bText : bText - aText) : dir === "asc" ? aText.localeCompare(bText) : bText.localeCompare(aText); }); rows.forEach((r) => tbody.appendChild(r)); thead.querySelectorAll("th").forEach((th) => { th.classList.remove("bvs-sort-asc", "bvs-sort-desc"); if (th.dataset.sort === key) th.classList.add(`bvs-sort-${dir}`); }); }; thead.querySelectorAll("th[data-sort]").forEach((th) => { th.addEventListener("click", () => sortTable(th.dataset.sort)); }); td.style.display = "none"; })();