// ==UserScript== // @name Torn圣诞小镇掉落物品坐标显示 // @namespace WH // @version 0.2.2 // @description 在地图界面上方显示附近的宝箱、物品、钥匙坐标,兼容手机APP Torn PDA及Alook // @author Woohoo[2687093] // @match https://www.torn.com/* // @grant none // ==/UserScript== (function () { 'use strict'; const ___window___ = window || window.unsafeWindow; if (___window___.WHLOOTPOS||___window___.WHTRANS) return; ___window___.WHLOOTPOS = true; const $ = ___window___.jQuery; if (/christmas_town\.php/.test(window.location.href)) { let $root = document.querySelector('#christmastownroot'); const chestTypeDict = {'1': '金', '2': '银', '3': '铜',}; const chestTypeColorDict = {'1': 'gold', '2': 'silver', '3': 'sandybrown',}; const lootTypeDict = {'chests': '钥匙箱', 'gifts': '礼物', 'combinationChest': '密码箱', 'keys': '钥匙',}; const keyTypeDict = {'b': '铜', 's': '银', 'g': '金',}; let dropHist = localStorage.getItem('wh-loot-store') ? JSON.parse(localStorage.getItem('wh-loot-store')) : {}; const alertSettings = localStorage.getItem('wh-loot-setting') ? JSON.parse(localStorage.getItem('wh-loot-setting')) : {blink: 'y', sound: 'y', chest: 'y'}; let $ct_wrap; let soundLoopFlag = false; const getDOMOb = new MutationObserver(() => { $ct_wrap = $root.querySelector('#ct-wrap'); if ($ct_wrap) { getDOMOb.disconnect(); const insert_html = `
附近物品
物品
箱子

- 长时间不清空会出现奇怪的问题

历史记录
坐标地图类型发现获取
`; $($ct_wrap).before(insert_html); const $wh_loot_container = $root.querySelector('#wh-loot-container'); const $btn = $wh_loot_container.querySelector('#wh-loot-btn button'); const $clear_btn = $wh_loot_container.querySelector('#wh-hist-clear button'); const $ex = $wh_loot_container.querySelector('#wh-loot-container-ex'); const $tbody = $wh_loot_container.querySelector('tbody'); const $blink = $wh_loot_container.querySelector('#wh-loot-setting-blink'); const $sound = $wh_loot_container.querySelector('#wh-loot-setting-sound'); const $chest = $wh_loot_container.querySelector('#wh-loot-setting-chest'); const $audio = $wh_loot_container.querySelector('audio'); $btn.onclick = e => { e.target.innerText = e.target.innerText === '设置' ? '收起' : '设置'; $($ex).toggleClass('wh-hide'); e.target.blur(); }; $clear_btn.onclick = e => { e.target.blur(); dropHist = {}; $tbody.innerHTML = ''; localStorage.setItem('wh-loot-store', JSON.stringify(dropHist)); }; $blink.onchange = e => { if (e.target.checked) { alertSettings.blink = 'y'; if ($wh_loot_container.querySelector('#wh-loot-item-count').innerText !== '(0)') { $wh_loot_container.querySelector('#wh-loot-container-main').style.animation = 'lootFoundAlert 2s infinite'; } } else { alertSettings.blink = 'n'; $wh_loot_container.querySelector('#wh-loot-container-main').style.animation = ''; } localStorage.setItem('wh-loot-setting', JSON.stringify(alertSettings)); }; $sound.onchange = e => { if (e.target.checked) { alertSettings.sound = 'y'; if ($wh_loot_container.querySelector('#wh-loot-item-count').innerText !== '(0)') { soundLoopFlag = true; } } else { alertSettings.sound = 'n'; soundLoopFlag = false; } localStorage.setItem('wh-loot-setting', JSON.stringify(alertSettings)); }; $chest.onchange = e => { alertSettings.chest = e.target.checked ? 'y' : 'n'; localStorage.setItem('wh-loot-setting', JSON.stringify(alertSettings)); }; const soundIntervalID = window.setInterval(() => { if (soundLoopFlag) $audio.play().then(); }, 1200); ob.observe($root, {childList: true, subtree: true}); } }); const ob = new MutationObserver(() => { ob.disconnect(); // 页面刷新重新获取dom $root = document.querySelector('#christmastownroot'); $ct_wrap = $root.querySelector('#ct-wrap'); if (!$ct_wrap) { ob.observe($root, {childList: true, subtree: true}); return; } const $ct_title = $ct_wrap.querySelector('.status-title'); const $pos = $ct_wrap.querySelector('.map-title span[class^="position___"]') || $ct_wrap.querySelector('.status-title span[class^="position___"]'); if (!$pos) { ob.observe($root, {childList: true, subtree: true}); return; } const $pos_spl = $pos.innerText.trim().split(','); const player_position = {}; player_position.x = parseInt($pos_spl[0]); player_position.y = parseInt($pos_spl[1]); const $wh_loot_container = $root.querySelector('#wh-loot-container'); if (!$wh_loot_container) { console.error('掉落助手未找到DOM容器'); ob.observe($root, {childList: true, subtree: true}); return; } const $blink = $wh_loot_container.querySelector('#wh-loot-setting-blink'); const $sound = $wh_loot_container.querySelector('#wh-loot-setting-sound'); const $chest = $wh_loot_container.querySelector('#wh-loot-setting-chest'); const $tbody = $wh_loot_container.querySelector('tbody'); const nearby_arr = []; const items = $root.querySelectorAll('div.grid-layer div.items-layer div.ct-item'); // 附近的所有物品 items.forEach(el => { const item_props = {x: 0, y: 0, name: '', type: '', url: '',}; item_props.x = parseInt(el.style.left.replaceAll('px', '')) / 30; item_props.y = -parseInt(el.style.top.replaceAll('px', '')) / 30; item_props.url = el.firstElementChild.src; const srcSpl = item_props.url.trim().split('/'); item_props.name = srcSpl[6]; item_props.type = srcSpl[8].slice(0, 1); nearby_arr.push(item_props); }); const $wh_loot_container_items = $wh_loot_container.querySelector('#wh-loot-container-items'); const $wh_loot_container_chests = $wh_loot_container.querySelector('#wh-loot-container-chests'); let item_count = 0, chest_count = 0; $wh_loot_container_items.innerHTML = ''; $wh_loot_container_chests.innerHTML = ''; nearby_arr.forEach(nearby_item => { let path = '='; if (nearby_item.x < player_position.x && nearby_item.y < player_position.y) path = '↙'; else if (nearby_item.x < player_position.x && nearby_item.y === player_position.y) path = '←'; else if (nearby_item.x < player_position.x && nearby_item.y > player_position.y) path = '↖'; else if (nearby_item.x === player_position.x && nearby_item.y > player_position.y) path = '↑'; else if (nearby_item.x > player_position.x && nearby_item.y > player_position.y) path = '↗'; else if (nearby_item.x > player_position.x && nearby_item.y === player_position.y) path = '→'; else if (nearby_item.x > player_position.x && nearby_item.y < player_position.y) path = '↘'; else if (nearby_item.x === player_position.x && nearby_item.y < player_position.y) path = '↓'; let item_name; if (nearby_item.name === 'chests') { chest_count++; item_name = chestTypeDict[nearby_item.type] + lootTypeDict[nearby_item.name]; $wh_loot_container_chests.innerHTML += `${path}[${nearby_item.x},${nearby_item.y}] ${item_name}` } else { item_count++; item_name = (nearby_item.name === 'keys' ? keyTypeDict[nearby_item.type] || '' : '') + lootTypeDict[nearby_item.name] || nearby_item.name; $wh_loot_container_items.innerHTML += `${path}[${nearby_item.x},${nearby_item.y}] ${item_name}` } // 确认地图坐标存在 if ($ct_title) { const hist_key = `[${nearby_item.x},${nearby_item.y}]"${$ct_title.firstChild.nodeValue.trim()}"${item_name}`; const el = dropHist[hist_key]; if (el) { if (path === '=' && (nearby_item.name === 'keys' || nearby_item.name === 'gifts')) { el.isPassed = true; } } else { if (!(nearby_item.name === 'chests' && $chest.checked)) { const now = new Date(); dropHist[hist_key] = { pos: `[${nearby_item.x},${nearby_item.y}]`, map: $ct_title.firstChild.nodeValue.trim(), last: `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} ${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`, name: item_name, id: Object.keys(dropHist).length, }; } } } }); $wh_loot_container.querySelector('#wh-loot-item-count').innerText = `(${item_count})`; if (item_count === 0) { $wh_loot_container_items.innerText = '暂无'; $wh_loot_container.querySelector('#wh-loot-container-main').style.animation = ''; soundLoopFlag = false; } else { if ($blink.checked) $wh_loot_container.querySelector('#wh-loot-container-main').style.animation = 'lootFoundAlert 2s infinite'; if ($sound.checked) soundLoopFlag = true; } $wh_loot_container.querySelector('#wh-loot-chest-count').innerText = `(${chest_count})`; if (chest_count === 0) $wh_loot_container_chests.innerText = '暂无'; const history = Object.keys(dropHist).map(key => dropHist[key]).sort((a, b) => a.id - b.id); let table_html = ''; history.forEach(e => { table_html += `${e.pos}${e.map}${e.name}${e.last}${e.isPassed ? '已取得' : '不确定'}`; }); $tbody.innerHTML = table_html; localStorage.setItem('wh-loot-store', JSON.stringify(dropHist)); ob.observe($root, {childList: true, subtree: true}); }); getDOMOb.observe($root, {childList: true, subtree: true}); } }());