您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a button to player profiles to save them to a custom list and a new menu to access the list.
// ==UserScript== // @name Torn City Add to Your List // @namespace https://www.torn.com/ // @version 1.3.1 // @description Adds a button to player profiles to save them to a custom list and a new menu to access the list. // @author MossaJehad // @match https://www.torn.com/* // @icon https://upload.wikimedia.org/wikipedia/commons/f/ff/Emojione_1F4CB.svg // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function () { const LIST_KEY = 'customPlayerList'; function getList() { const list = GM_getValue(LIST_KEY, []); return list; } function saveList(list) { GM_setValue(LIST_KEY, list); } function removePlayer(playerId) { const list = getList(); const updatedList = list.filter(player => player.id !== playerId); saveList(updatedList); return updatedList; } const playerIdMatch = window.location.href.match(/XID=(\d+)/); let isDark = document.body.classList.contains('dark-mode') ? 1 : 0; let svgtheme = isDark ? 'url(#linear-gradient-dark-mode)' : 'url(#linear-gradient)'; const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.attributeName === "class") { if (document.body.classList.contains('dark-mode')) { isDark = 1; } else { isDark = 0; } updateSidebarButton(); } }); }); function addProfileButton() { const actionsContainer = document.querySelector('.buttons-wrap .buttons-list'); if (!actionsContainer || document.querySelector('.add-to-your-list')) return; const addButton = document.createElement('a'); addButton.className = 'profile-button add-to-your-list active'; addButton.href = '#'; addButton.style.textAlign = 'center'; const playerName = document.querySelector('h4#skip-to-content').innerText.slice(0, -10); const leftDigit = document.querySelector('.box-value .digit-r .digit.left'); const middleDigit = document.querySelector('.box-value .digit-m .digit'); const rightDigit = document.querySelector('.box-value .digit-l .digit.left'); const leftValue = leftDigit ? leftDigit.textContent.trim() : ''; const middleValue = middleDigit ? middleDigit.textContent.trim() : ''; const rightValue = rightDigit ? rightDigit.textContent.trim() : ''; const playerLevel = `${leftValue}${middleValue}${rightValue}`; if (!playerIdMatch) return; const playerId = playerIdMatch[1]; const list = getList(); const isInList = list.some(player => player.id === playerId); addButton.innerHTML = isInList ? `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 0" id="star" style="width: 25px;height: 25px;scale: 1.33;margin: 8px 0 0 0;" fill=${svgtheme}> <path d="M9.362,9.158c0,0-3.16,0.35-5.268,0.584c-0.19,0.023-0.358,0.15-0.421,0.343s0,0.394,0.14,0.521 c1.566,1.429,3.919,3.569,3.919,3.569c-0.002,0-0.646,3.113-1.074,5.19c-0.036,0.188,0.032,0.387,0.196,0.506 c0.163,0.119,0.373,0.121,0.538,0.028c1.844-1.048,4.606-2.624,4.606-2.624s2.763,1.576,4.604,2.625 c0.168,0.092,0.378,0.09,0.541-0.029c0.164-0.119,0.232-0.318,0.195-0.505c-0.428-2.078-1.071-5.191-1.071-5.191 s2.353-2.14,3.919-3.566c0.14-0.131,0.202-0.332,0.14-0.524s-0.23-0.319-0.42-0.341c-2.108-0.236-5.269-0.586-5.269-0.586 s-1.31-2.898-2.183-4.83c-0.082-0.173-0.254-0.294-0.456-0.294s-0.375,0.122-0.453,0.294C10.671,6.26,9.362,9.158,9.362,9.158z"/> </svg>` : `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 0" id="star" style="width: 25px;height: 25px;scale: 1.22;margin: 8px 0 0 0;" fill=${svgtheme}> <path d="M16.855,20.966c-0.224,0-0.443-0.05-0.646-0.146c-0.035-0.014-0.069-0.031-0.104-0.051l-4.107-2.343L7.891,20.77 c-0.035,0.02-0.07,0.037-0.106,0.053C7.297,21.051,6.7,20.997,6.264,20.68c-0.469-0.34-0.701-0.933-0.586-1.509l0.957-4.642 c-0.374-0.34-0.962-0.875-1.602-1.457l-1.895-1.725c-0.027-0.025-0.055-0.053-0.078-0.082c-0.375-0.396-0.509-0.97-0.34-1.492 C2.893,9.249,3.34,8.861,3.88,8.764C3.914,8.756,3.947,8.75,3.982,8.746l4.701-0.521l1.946-4.31 c0.017-0.038,0.036-0.075,0.06-0.11c0.262-0.473,0.764-0.771,1.309-0.771c0.543,0,1.044,0.298,1.309,0.77 c0.021,0.036,0.041,0.073,0.06,0.112l1.948,4.312l4.701,0.521c0.034,0.003,0.068,0.009,0.104,0.017 c0.539,0.1,0.986,0.486,1.158,1.012c0.17,0.521,0.035,1.098-0.34,1.494c-0.024,0.026-0.051,0.054-0.078,0.078l-3.498,3.184 l0.957,4.632c0.113,0.587-0.118,1.178-0.59,1.519C17.477,20.867,17.173,20.966,16.855,20.966z M8.706,14.402 c-0.039,0.182-0.466,2.246-0.845,4.082l3.643-2.077c0.307-0.175,0.684-0.175,0.99,0l3.643,2.075l-0.849-4.104 c-0.071-0.346,0.045-0.705,0.308-0.942l3.1-2.822l-4.168-0.461c-0.351-0.039-0.654-0.26-0.801-0.584l-1.728-3.821l-1.726,3.821 c-0.146,0.322-0.45,0.543-0.801,0.584l-4.168,0.461l3.1,2.822C8.676,13.682,8.788,14.053,8.706,14.402z"/> </svg>`; addButton.addEventListener('click', (e) => { e.preventDefault(); const list = getList(); const playerEntry = { 'id': playerId, 'name': playerName, 'level': playerLevel }; if (!list.some(player => player.id === playerId)) { list.push(playerEntry); saveList(list); updatePlayerListState(); updateSidebarCount(); } else { removePlayer(playerId); saveList(list); updatePlayerListState(); updateSidebarCount(); } }); actionsContainer.appendChild(addButton); } function replaceMainContentWithList() { const mainContentWrapper = document.querySelector('#mainContainer'); const mainItemsContainer = document.querySelector('.content-wrapper[role="main"]'); if (!mainContentWrapper || !mainItemsContainer) return; mainItemsContainer.remove(); const listContainer = document.createElement('div'); listContainer.className = 'content'; listContainer.innerHTML = ` <style> .content { width: 100%; } .your-list-container { width: 100%; margin: 0 auto; padding: 20px; } .your-list-container h3 { margin: 0 0 20px 0; font-size: 22px; text-align: center; } .remove-all-btn { display: flex; margin: 0 auto 20px auto; color: #fff; border: none; cursor: pointer; justify-content: space-around; align-content: center; align-items: center; flex-direction: row; flex-wrap: nowrap; } .player-table { width: 100%; } .player-table td{ text-align: center; } .remove-btn { background: none; border: none; padding: 4px; color: #ff4d4d; font-size: 20px; cursor: pointer; transition: color 0.3s ease; } .remove-btn:hover { color: #d93636; } .player-tr { background: #aaa; } .player-tr:hover { background: #bbb; } </style> <div class="your-list-container"> <h3>Your Player List</h3> <div class="mission-tooltip"> <button id="rall" class="remove-all-btn ladda-button cancel torn-btn btn-dark-bg" data-style="slide-right"> <span class="ladda-label">Remove All</span> <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 60 60" style="margin-left: 10px;"> <path fill="#FFF" d="M36 26v11c0 1.66-1.34 3-3 3h-10c-1.66 0-3-1.34-3-3V26h16zm-2 0v11c0 .55-.45 1-1 1h-10c-.55 0-1-.45-1-1V26h12zm-9-5c0-.55.45-1 1-1h4c.55 0 1 .45 1 1s-.45 1-1 1h-4c-.55 0-1-.45-1-1zm0 7c0-.55.45-1 1-1s1 .45 1 1v6c0 .55-.45 1-1 1s-1-.45-1-1v-6zm4 0c0-.55.45-1 1-1s1 .45 1 1v6c0 .55-.45 1-1 1s-1-.45-1-1v-6zm-6-6h-4c-.55 0-1 .45-1 1s.45 1 1 1h18c.55 0 1-.45 1-1s-.45-1-1-1h-4v-1c0-1.66-1.34-3-3-3h-4c-1.66 0-3 1.34-3 3v1z"></path> </svg> </button> </div> <table class="player-table title-black top-round" style="border-radius: 7px 7px 0 0;"> <thead class="users-list-title top-round m-top10"> <tr> <th>Name</th> <th>Level</th> <th>ID</th> <th style="width: 33px;">Del</th> </tr> </thead> <tbody> ${getList() .map( player => ` <tr class="player-tr"> <td class="player-name"> <a href="/profiles.php?XID=${player.id}" target="_blank" id="player-${player.id}" style=" font-family: visitor1; color: white; text-decoration: none; font-size: 16px; text-shadow: 0px 0px 3px #000000;"> ${player.name} </a> </td> <td class="player-level" style="color: white;">${player.level}</td> <td class="player-id" style="color: white;">${player.id}</td> <td> <button class="remove-btn delete" data-player-id="${player.id}">×</button> </td> </tr> ` ) .join('')} </tbody> </table> </div> `; const lastElement = mainContentWrapper.lastElementChild; mainContentWrapper.insertBefore(listContainer, lastElement); const removeButtons = listContainer.querySelectorAll('.remove-btn'); removeButtons.forEach(button => { button.addEventListener('click', function () { const playerId = this.getAttribute('data-player-id'); const updatedList = removePlayer(playerId); if (updatedList.length === 0) { listContainer.style.width = "100%"; listContainer.innerHTML = '<div style="width: 100%; margin: 0 auto; padding: 20px;"><h3 style="margin: 0 0 20px 0; font-size: 22px; text-align: center;">Your list is empty</h3></div>'; } else { replaceMainContentWithList(); } updateSidebarCount(); }); }); const removeAllButton = listContainer.querySelector('#rall'); if (removeAllButton) { removeAllButton.addEventListener('click', function () { saveList([]); listContainer.style.width = "100%"; listContainer.innerHTML = '<div style="width: 100%; margin: 0 auto; padding: 20px;"><h3 style="margin: 0 0 20px 0; font-size: 22px; text-align: center;">Your list is empty</h3></div>'; updateSidebarCount(); }); } } observer.observe(document.body, { attributes: true }); function addSidebarButton() { const enemiesListElement = document.querySelector('#nav-enemies_list'); if (!enemiesListElement || document.querySelector('.your-list-button')) return; const listButton = document.createElement('div'); listButton.className = 'area-desktop___bpqAS your-list-button'; listButton.innerHTML = ` <div class="area-row___iBD8N" id="yourList"> <a href="#" class="desktopLink___SG2RU"> <span class="svgIconWrap___AMIqR"> ${isDark ? '<span class="defaultIcon___iiNis mobile___paLva">' : '<span class="defaultIcon___iiNis desktop___LfsR8">' } <svg xmlns="http://www.w3.org/2000/svg" stroke="transparent" stroke-width="0" width="20" height="16" viewBox="0 1 20 16"> <g> <path d="M13.88,13.06c-2.29-.53-4.43-1-3.39-2.94C13.63,4.18,11.32,1,8,1S2.36,4.3,5.51,10.12c1.07,2-1.15,2.43-3.39,2.94C.13,13.52,0,14.49,0,16.17V17H16v-.83C16,14.49,15.87,13.52,13.88,13.06Z"></path> </g> </svg> </span> </span> <span class="linkName___FoKha">Your List</span> </a> <div role="button" tabindex="0" class="info___cuq1T"> <span class="amount___p8QZX">0</span> <span class="arrow___tKP13 disabled___nQBDP"></span> </div> </div> `; listButton.querySelector('#yourList').addEventListener('click', (e) => { e.preventDefault(); replaceMainContentWithList(); }); enemiesListElement.appendChild(listButton); updateSidebarCount(); } function updateSidebarButton() { const listButton = document.querySelector('.your-list-button'); if (listButton) { const currentCount = listButton.querySelector('.amount___p8QZX')?.textContent || '0'; listButton.innerHTML = ` <div class="area-row___iBD8N" id="yourList"> <a href="#" class="desktopLink___SG2RU"> <span class="svgIconWrap___AMIqR"> ${isDark ? '<span class="defaultIcon___iiNis mobile___paLva">' : '<span class="defaultIcon___iiNis desktop___LfsR8">' } <svg xmlns="http://www.w3.org/2000/svg" stroke="transparent" stroke-width="0" width="20" height="16" viewBox="0 1 20 16"> <g> <path d="M13.88,13.06c-2.29-.53-4.43-1-3.39-2.94C13.63,4.18,11.32,1,8,1S2.36,4.3,5.51,10.12c1.07,2-1.15,2.43-3.39,2.94C.13,13.52,0,14.49,0,16.17V17H16v-.83C16,14.49,15.87,13.52,13.88,13.06Z"></path> </g> </svg> </span> </span> <span class="linkName___FoKha">Your List</span> </a> <div role="button" tabindex="0" class="info___cuq1T"> <span class="amount___p8QZX">${currentCount}</span> <span class="arrow___tKP13 disabled___nQBDP"></span> </div> </div> `; } } function updateSidebarCount() { const list = getList(); const listButtonAmount = document.querySelector('.your-list-button .amount___p8QZX'); if (listButtonAmount) { listButtonAmount.textContent = list.length; } } function updatePlayerListState() { const list = getList(); const playerId = playerIdMatch[1]; const addButton = document.querySelector('.add-to-your-list'); if(addButton) { addButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 0" id="star" fill=${svgtheme} style="width: 25px;height: 25px;scale: 1.33;margin: 8px 0 0 0;"> <path d="M9.362,9.158c0,0-3.16,0.35-5.268,0.584c-0.19,0.023-0.358,0.15-0.421,0.343s0,0.394,0.14,0.521 c1.566,1.429,3.919,3.569,3.919,3.569c-0.002,0-0.646,3.113-1.074,5.19c-0.036,0.188,0.032,0.387,0.196,0.506 c0.163,0.119,0.373,0.121,0.538,0.028c1.844-1.048,4.606-2.624,4.606-2.624s2.763,1.576,4.604,2.625 c0.168,0.092,0.378,0.09,0.541-0.029c0.164-0.119,0.232-0.318,0.195-0.505c-0.428-2.078-1.071-5.191-1.071-5.191 s2.353-2.14,3.919-3.566c0.14-0.131,0.202-0.332,0.14-0.524s-0.23-0.319-0.42-0.341c-2.108-0.236-5.269-0.586-5.269-0.586 s-1.31-2.898-2.183-4.83c-0.082-0.173-0.254-0.294-0.456-0.294s-0.375,0.122-0.453,0.294C10.671,6.26,9.362,9.158,9.362,9.158z"/> </svg>` } } window.addEventListener('load', () => { addProfileButton(); addSidebarButton(); updateSidebarCount(); }); })();