图寻连击计数器

自动记录国家/一级行政区连击次数

当前为 2024-08-31 提交的版本,查看 最新版本

// ==UserScript==
// @name         图寻连击计数器
// @namespace    https://gf.qytechs.cn/users/1179204
// @version      1.0.0
// @description  自动记录国家/一级行政区连击次数
// @author       KaKa
// @match        *://tuxun.fun/*
// @exclude      *://tuxun.fun/replay-pano?*
// @icon         data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDggNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0iIzAwMDAwMCI+PGcgaWQ9IlNWR1JlcG9fYmdDYXJyaWVyIiBzdHJva2Utd2lkdGg9IjAiPjwvZz48ZyBpZD0iU1ZHUmVwb190cmFjZXJDYXJyaWVyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvZz48ZyBpZD0iU1ZHUmVwb19pY29uQ2FycmllciI+PHRpdGxlPjcwIEJhc2ljIGljb25zIGJ5IFhpY29ucy5jbzwvdGl0bGU+PHBhdGggZD0iTTI0LDEuMzJjLTkuOTIsMC0xOCw3LjgtMTgsMTcuMzhBMTYuODMsMTYuODMsMCwwLDAsOS41NywyOS4wOWwxMi44NCwxNi44YTIsMiwwLDAsMCwzLjE4LDBsMTIuODQtMTYuOEExNi44NCwxNi44NCwwLDAsMCw0MiwxOC43QzQyLDkuMTIsMzMuOTIsMS4zMiwyNCwxLjMyWiIgZmlsbD0iI2ZmOTQyNyI+PC9wYXRoPjxwYXRoIGQ9Ik0yNS4zNywxMi4xM2E3LDcsMCwxLDAsNS41LDUuNUE3LDcsMCwwLDAsMjUuMzcsMTIuMTNaIiBmaWxsPSIjZmZmZmZmIj48L3BhdGg+PC9nPjwvc3ZnPg==
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11
// @copyright    KaKa
// @license      BSD
// ==/UserScript==

(function() {
    let viewer, map, finalGuess, currentRound, gameState = false, roundPins = {}, roundState, countsDiv, countsValue;
    let streakMode = 'country';

    // 初始化 streakCounts
    let streakCounts = JSON.parse(localStorage.getItem('streakCounts')) || { country: 0, state: 0 };

    const CC_DICT = {
        AX: "FI", AS: "US", AI: "GB", AW: "NL", BM: "GB", BQ: "NL", BV: "NO", IO: "GB", KY: "UK",
        CX: "AU", CC: "AU", CK: "NZ", CW: "NL", FK: "GB", FO: "DK", GF: "FR", PF: "FR", TF: "FR",
        GI: "UK", GL: "DK", GP: "FR", GU: "US", GG: "GB", HM: "AU", HK: "CN", IM: "GB", JE: "GB",
        MO: "CN", MQ: "FR", YT: "FR", MS: "GB", AN: "NL", NC: "FR", NU: "NZ", NF: "AU", MP: "US",
        PS: "IL", PN: "GB", PR: "US", RE: "FR", BL: "FR", SH: "GB", MF: "FR", PM: "FR", SX: "NL",
        GS: "GB", SJ: "NO", TK: "NZ", TC: "GB", UM: "US", VG: "GB", VI: "US", WF: "FR", EH: "MA",
        TW: "CN"
    };

    // 轮询获取必要的 DOM 元素
    let intervalId = setInterval(() => {
        const streetViewContainer = document.getElementById('viewer');
        if (streetViewContainer) {
            getSVContainer();
            getMap();
            if (map && viewer && viewer.location) {
                mapListener();
                clearInterval(intervalId);
            }
        }
    }, 500);

    function getMap() {
        const mapContainer = document.getElementById('map');
        const key = Object.keys(mapContainer).find(k => k.startsWith("__reactFiber$"));
        const props = mapContainer[key];
        map = props.child.memoizedProps.value.map.getMap();
    }

    function getSVContainer() {
        const streetViewContainer = document.getElementById('viewer');
        const key = Object.keys(streetViewContainer).find(k => k.startsWith("__reactFiber"));
        const props = streetViewContainer[key];
        viewer = props.return.child.memoizedProps.children[1].props.googleMapInstance;
        const gameData = props.return.return.return.return.return.memoizedState.next.next.memoizedState.current.gameData;
        if (gameData?.status === 'ongoing') {
            gameState = roundState = true;
            currentRound = gameData.rounds.length;
        }
    }

    function mapListener() {
        setMapObserver();
        setSVObserver();
        if (!roundPins[currentRound]) {
            getRoundPin();
            updatePanel();
        }
        const mapContainer = document.querySelector('.maplibregl-canvas');
        const observer = new MutationObserver(mutationsList => {
            for (let mutation of mutationsList) {
                if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
                    handleSizeChange(mapContainer);
                }
            }
        });
        observer.observe(mapContainer, { attributes: true, attributeFilter: ['style'] });
    }

    function setMapObserver() {
        map.on('click', e => {
            if (gameState && roundState) finalGuess = e.lngLat;
        });
    }

    function setSVObserver() {
        viewer.addListener('position_changed', () => {
            if (!roundPins[currentRound] && gameState) {
                getRoundPin();
            }
        });
    }

    async function getRoundPin() {
        const { lat, lng } = viewer.location.latLng;
        roundPins[currentRound] = await queryOSM(lat(), lng(), 'en');
    }

    function handleSizeChange(target) {
        const { width } = target.getBoundingClientRect();
        const widthRatio = (width / window.innerWidth) * 100;
        if (widthRatio >= 90) {
            streakCheck();
            roundState = false;
        } else {
            roundState = true;
            updatePanel();
        }
    }

    async function queryOSM(lat, lng, language) {
        const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=jsonv2&accept-language=${language}`;
        const response = await fetch(url);
        if (response.ok) {
            const data = await response.json();
            return data.address || null;
        }
        return null;
    }

    async function streakCheck() {
        if (!roundState || !finalGuess) return;

        const guess = await queryOSM(finalGuess.lat, finalGuess.lng, 'en');
        if (guess?.country === 'India' && guess.state === 'Arunachal Pradesh') {
            guess.country = 'China';
            guess.state = 'Tibet';
        }

        const isStreak = streakMode === 'country'
            ? matchCountryCode(guess) === matchCountryCode(roundPins[currentRound])
            : streakMode === 'state'
            ? matchState(guess) === matchState(roundPins[currentRound])
            : false;

        updateBar(isStreak, guess || { country: 'Undefined' }, roundPins[currentRound], streakMode);
        currentRound += 1;
    }

    function correctAddress(item) {
        return ['Taiwan', 'HongKong', 'Macau'].includes(item) ? 'China' : item;
    }

    function updateBar(status, guess, pin, mode) {
        const infoBar = document.querySelector('.controls___yY74y');
        if (!infoBar) return;

        const streakText = infoBar.querySelector('p')
        streakText.style.cssText = 'font-size: 24px; color: #fff; font-family: Baloo Bhaina;';
        infoBar.appendChild(streakText);

        if (status) {
            streakCounts[mode] += 1;
            streakText.textContent = `恭喜你选中 ${correctAddress(guess.country)}, 连击次数: ${streakCounts[mode]}`;
        } else {
            const endCount = streakCounts[mode];
            streakCounts[mode] = 0;
            streakText.textContent = `答案是 ${correctAddress(guess.country)}, 你选了 ${correctAddress(pin.country)}, 连击次数: ${streakCounts[mode]}, 本轮达成连击: ${endCount}`;
        }

        localStorage.setItem('streakCounts', JSON.stringify(streakCounts));
    }

    function updatePanel() {
        const panelContainer = document.querySelector('.roundWrapper___eTnOj');
        if (!panelContainer) return;

        if (!countsDiv) {
            countsDiv = document.createElement('div');
            countsDiv.className = 'roundInfoBox___ikizG';

            const countsTitle = document.createElement('div');
            countsTitle.className = 'roundInfoTitle___VOdv2';
            countsTitle.textContent = streakMode === 'country' ? '国家连击' : '一级行政区连击';

            countsValue = document.createElement('div');
            countsValue.className = 'roundInfoValue___zV6GS';
            countsDiv.append(countsTitle, countsValue);

            const divider = document.createElement('div');
            divider.className = 'ant-divider css-i874aq ant-divider-vertical';
            divider.setAttribute('role', 'separator');

            panelContainer.append(divider, countsDiv);
        }

        countsValue.textContent = streakCounts[streakMode];
    }

    function matchCountryCode(t) {
        const code = t?.country_code?.toUpperCase();
        return CC_DICT[code] || code || 'Undefined';
    }

    function matchState(t) {
        return t?.state || t?.province || t?.county || 'Undefined';
    }

    document.addEventListener("keydown", (e) => {
        if (e.key.toLowerCase() === 'p') {
            e.stopImmediatePropagation();
            streakMode = streakMode === 'state' ? 'country' : 'state';
            Swal.fire({
                title: '切换成功',
                text: `${streakMode} 连击计数器已就绪!`,
                icon: 'success',
                timer: 1000,
                showConfirmButton: false,
            });
        }
    });
})();

QingJ © 2025

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