Neopets Fishing Cooldown Timer

Tracks and displays the time since the 'Reel In Your Line' button was last clicked.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Neopets Fishing Cooldown Timer
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  Tracks and displays the time since the 'Reel In Your Line' button was last clicked.
// @author       Logan Bell
// @match        https://www.neopets.com/water/fishing.phtml
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Key for storing the timestamp in Tampermonkey storage
    const LAST_CLICK_KEY = 'lastFishingClickTime';

    // Selectors for key elements
    // The form containing the button
    const FISHING_FORM_SELECTOR = 'form[action="/water/fishing.phtml"]';
    // The submit button itself
    const BUTTON_SELECTOR = FISHING_FORM_SELECTOR + ' input[type="submit"][value="Reel In Your Line"]';

    /**
     * Finds a suitable element (like the skill text) to place the timer after.
     * @returns {HTMLElement | null} The skill element, or null if not found.
     */
    function findSkillElement() {
        // Look for common HTML tags that contain the skill text
        const potentialElements = document.querySelectorAll('p, div, b, span');
        for (const element of potentialElements) {
            if (element.textContent && element.textContent.includes('Your current fishing skill is')) {
                return element;
            }
        }
        return null;
    }

    /**
     * Converts a time difference in milliseconds into a formatted string (D H M).
     * @param {number} ms - The time difference in milliseconds.
     * @returns {string} The formatted time string.
     */
    function formatTimeDifference(ms) {
        if (ms < 0) ms = 0;

        const totalSeconds = Math.floor(ms / 1000);
        const days = Math.floor(totalSeconds / (60 * 60 * 24));
        const hours = Math.floor((totalSeconds % (60 * 60 * 24)) / (60 * 60));
        const minutes = Math.floor((totalSeconds % (60 * 60)) / 60);

        return `${days}d, ${hours}h, ${minutes}m`;
    }

    /**
     * Renders the time since the last click onto the page.
     * @param {HTMLElement} placementAnchor - The element to insert the time display *after*.
     */
    function displayLastClickTime(placementAnchor) {
        const lastClickTime = GM_getValue(LAST_CLICK_KEY, 0);
        const now = Date.now();
        let displayHTML = '';
        let timeDiffMs = now - lastClickTime;

        if (lastClickTime === 0) {
            displayHTML = '<strong>First Time?</strong> Click the button below to start tracking!';
        } else {
            const timeDiffFormatted = formatTimeDifference(timeDiffMs);
            const oneDayMs = 24 * 60 * 60 * 1000;
            const statusClass = timeDiffMs >= oneDayMs ? 'cooldown-ready' : 'cooldown-waiting';

            let remainingText = '';
            if (timeDiffMs < oneDayMs) {
                const remainingMs = oneDayMs - timeDiffMs;
                const remainingFormatted = formatTimeDifference(remainingMs);
                remainingText = `<br>(${remainingFormatted} remaining)`;
            }

            displayHTML = `
                <span class="${statusClass}">
                    Last Fished:
                    <strong>${timeDiffFormatted} ago</strong>
                    ${remainingText}
                </span>
            `;
        }

        // Create or find the display element
        let timerDisplay = document.getElementById('fishingCooldownTimer');
        if (!timerDisplay) {
            timerDisplay = document.createElement('div');
            timerDisplay.id = 'fishingCooldownTimer';
            timerDisplay.style.cssText = 'font-size: 16px; margin: 10px 0; padding: 8px; border: 1px solid #000; background-color: #f7f7f7; text-align: center; color: black; font-weight: bold;';

            // Insert the timer *after* the anchor element
            placementAnchor.parentNode.insertBefore(timerDisplay, placementAnchor.nextSibling);
        }

        timerDisplay.innerHTML = displayHTML;

        // Apply specific color styles
        let style = document.getElementById('fishingCooldownStyles');
        if (!style) {
            style = document.createElement('style');
            style.id = 'fishingCooldownStyles';
            style.textContent = `
                #fishingCooldownTimer .cooldown-ready { color: green; font-weight: bold; }
                #fishingCooldownTimer .cooldown-waiting { color: black; font-weight: bold; }
            `;
            document.head.appendChild(style);
        }

        // Update the time every second
        if (lastClickTime !== 0) {
            setTimeout(() => displayLastClickTime(placementAnchor), 1000);
        }
    }


    // --- Main Execution ---

    const reelInButton = document.querySelector(BUTTON_SELECTOR);
    const fishingForm = document.querySelector(FISHING_FORM_SELECTOR);
    const skillElement = findSkillElement();

    // 1. Determine the insertion point
    let insertionAnchor = null;

    if (skillElement) {
        // If the specific skill element is found, use it for perfect placement.
        insertionAnchor = skillElement;
    } else if (fishingForm) {
        // If the skill element is missing, use the entire <form> as the anchor.
        // The timer will be placed right after the form, which is still right above the button.
        insertionAnchor = fishingForm;
    } else if (reelInButton) {
        // Fallback: If for some reason we only find the button, use its parent container.
        insertionAnchor = reelInButton.parentNode;
    }

    if (insertionAnchor) {
        // 2. Attach the click handler to save the current time (must be on the button)
        if (reelInButton) {
            reelInButton.addEventListener('click', function() {
                GM_setValue(LAST_CLICK_KEY, Date.now());
                console.log('Fishing click timestamp saved.');
            });
        }

        // 3. Display the time since the last click
        displayLastClickTime(insertionAnchor);

    } else {
        console.warn('Kiko Lake Fishing Timer Script: Could not find a suitable placement anchor (Skill Text or Form). Timer will not display.');
    }

})();