Tribal Wars Auto Resource Builder with reduction

Automatically builds resource buildings with level balancing

目前為 2024-10-26 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Tribal Wars Auto Resource Builder with reduction
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Automatically builds resource buildings with level balancing
// @author       You
// @match        https://*.die-staemme.de/game.php?village=*&screen=main*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const CHECK_INTERVAL = 5 * 61 * 1000; // 5 minutes in milliseconds
    const BUILDING_PRIORITY = ['wood', 'stone', 'iron'];
    const LEVEL_DIFFERENCE_THRESHOLD = 3; // Maximum allowed level difference
    const WAIT_FOR_HIGHER_PRIORITY = 10 * 60; // Wait up to 10 minutes for higher priority building
    const DEBUG = true;

    function debugLog(message, data = null) {
        if (!DEBUG) return;
        const timestamp = new Date().toLocaleTimeString();
        if (data) {
            console.log(`[${timestamp}] ${message}`, data);
        } else {
            console.log(`[${timestamp}] ${message}`);
        }
    }

    function getBuildingLevel(buildingName) {
        debugLog(`Getting level for ${buildingName}`);
        try {
            // Find the row containing the building
            const row = document.querySelector(`#main_buildrow_${buildingName}`);
            if (!row) {
                debugLog(`No row found for ${buildingName}`);
                return null;
            }

            // Find the build button
            const buildButton = row.querySelector(`a.btn-build[id*="_${buildingName}_"]`);
            if (!buildButton) {
                debugLog(`No build button found for ${buildingName}`);
                return null;
            }

            // Get the next level from data attribute and subtract 1
            const nextLevel = parseInt(buildButton.getAttribute('data-level-next'));
            if (isNaN(nextLevel)) {
                debugLog(`Could not parse next level for ${buildingName}`);
                return null;
            }

            const currentLevel = nextLevel - 1;
            debugLog(`${buildingName} current level:`, currentLevel);
            return currentLevel;
        } catch (error) {
            debugLog(`Error getting level for ${buildingName}:`, error);
            return null;
        }
    }


    function getResourceLevels() {
        debugLog('Getting current resource levels...');
        const levels = {};
        let highestLevel = 0;

        for (const resource of BUILDING_PRIORITY) {
            levels[resource] = getBuildingLevel(resource);
            if (levels[resource] !== null) {
                highestLevel = Math.max(highestLevel, levels[resource]);
            }
        }

        debugLog('Resource levels summary:', { levels, highestLevel });
        return { levels, highestLevel };
    }

    function getRemainingBuildTime(buildingName) {
        try {
            const row = document.querySelector(`#main_buildrow_${buildingName}`);
            if (!row) {
                debugLog(`No row found for ${buildingName} when checking time`);
                return Infinity;
            }

            // Check if there's a running timer
            const timerCell = row.querySelector('td:nth-child(5)');
            if (!timerCell) {
                debugLog(`No timer cell found for ${buildingName}`);
                return Infinity;
            }

            const timeText = timerCell.textContent.trim();
            if (!timeText) return 0;

            const [hours, minutes, seconds] = timeText.split(':').map(Number);
            const totalSeconds = hours * 3600 + minutes * 60 + seconds;
            debugLog(`${buildingName} remaining build time: ${timeText} (${totalSeconds} seconds)`);
            return totalSeconds;
        } catch (error) {
            debugLog(`Error getting remaining time for ${buildingName}:`, error);
            return Infinity;
        }
    }

    function canBuildResource(buildingName) {
        try {
            const row = document.querySelector(`#main_buildrow_${buildingName}`);
            if (!row) {
                debugLog(`No row found for ${buildingName} when checking buildability`);
                return false;
            }

            // Find the -20% button specifically
            const buildButton = row.querySelector(`#main_buildlink_${buildingName}_cheap`);
            if (!buildButton) {
                debugLog(`No cheap build button found for ${buildingName}`);
                return false;
            }

            // Check if button has the disabled class
            const isDisabled = buildButton.classList.contains('btn-bcr-disabled');

            // Check if there's a valid build link
            const hasValidHref = buildButton.getAttribute('href') &&
                  buildButton.getAttribute('href').includes('cheap') &&
                  buildButton.getAttribute('href') !== '#';

            // Check if the button is inside a cell with class 'build_options'
            const inBuildOptions = buildButton.closest('.build_options') !== null;

            // A button is buildable if it has a valid href, is in the build options cell, and is not disabled
            const canBuild = hasValidHref && inBuildOptions && !isDisabled;

            debugLog(`Checking if ${buildingName} can be built with -20%:`, {
                buttonFound: true,
                hasValidHref: hasValidHref,
                inBuildOptions: inBuildOptions,
                isDisabled: isDisabled,
                canBuild: canBuild,
                href: buildButton.getAttribute('href'),
                buttonText: buildButton.textContent.trim(),
                buttonClasses: buildButton.className,
                parentCell: buildButton.closest('td')?.className || 'no parent cell'
            });

            return canBuild;
        } catch (error) {
            debugLog(`Error checking if ${buildingName} can be built:`, error);
            return false;
        }
    }

    function willBeAvailableSoon(buildingName) {
        const remainingTime = getRemainingBuildTime(buildingName);
        const willBeSoon = remainingTime > 0 && remainingTime <= WAIT_FOR_HIGHER_PRIORITY;
        debugLog(`Checking if ${buildingName} will be available soon:`, {
            remainingTime,
            threshold: WAIT_FOR_HIGHER_PRIORITY,
            willBeSoon
        });
        return willBeSoon;
    }

    function applyBuildTimeReduction() {
        try {
            // Find all build time reduction buttons
            const reductionButtons = document.querySelectorAll('a.order_feature.btn.btn-btr');
            if (!reductionButtons || reductionButtons.length === 0) {
                debugLog('No build time reduction buttons found');
                return false;
            }

            // Get the last button (most recently added building)
            const lastButton = reductionButtons[reductionButtons.length - 1];

            // Click the button
            lastButton.click();
            debugLog('Clicked build time reduction button');
            return true;
        } catch (error) {
            debugLog('Error applying build time reduction:', error);
            return false;
        }
    }

    function buildResource(buildingName) {
        debugLog(`Attempting to build ${buildingName} with -20% discount`);

        try {
            const row = document.querySelector(`#main_buildrow_${buildingName}`);
            if (!row) {
                debugLog(`No row found for ${buildingName} when trying to build`);
                return false;
            }

            const buildButton = row.querySelector(`#main_buildlink_${buildingName}_cheap`);
            if (!buildButton) {
                debugLog(`No cheap build button found for ${buildingName}`);
                return false;
            }

            const buildUrl = buildButton.getAttribute('href');
            if (!buildUrl || !buildUrl.includes('cheap')) {
                debugLog(`No valid cheap build href found for ${buildingName}`);
                return false;
            }

            debugLog(`Clicking -20% build button for ${buildingName} with URL: ${buildUrl}`);
            window.location.href = buildUrl;

            // Wait a short moment for the page to update then apply reduction
            setTimeout(applyBuildTimeReduction, 1000);
            return true;
        } catch (error) {
            debugLog(`Error building ${buildingName}:`, error);
            return false;
        }
    }

    function isConstructionInProgress() {
        // Check for buildorder element
        const buildorder = document.querySelector('#buildorder_4');
        const isBuilding = buildorder !== null;

        debugLog('Checking for ongoing construction:', {
            buildorderFound: isBuilding,
            elementId: isBuilding ? buildorder.id : 'not found'
        });

        return isBuilding;
    }

    function reduceLongBuilds() {
        try {
            // Get all actual build rows (excluding headers and progress bars)
            const buildRows = document.querySelectorAll('#buildqueue tr.buildorder_wood, #buildqueue tr.buildorder_stone, #buildqueue tr.buildorder_iron, #buildqueue tr.buildorder_farm, #buildqueue tr.buildorder_market');

            debugLog('Found build rows:', buildRows.length);

            for (const row of buildRows) {
                // Get the duration cell - looking specifically at the td with nowrap class
                const durationCell = row.querySelector('td.nowrap.lit-item');
                if (!durationCell) {
                    debugLog('No duration cell found for row:', row.className);
                    continue;
                }

                // Get the span containing the time
                const timeSpan = durationCell.querySelector('span');
                if (!timeSpan) {
                    debugLog('No time span found in duration cell');
                    continue;
                }

                // Get the duration text
                const durationText = timeSpan.textContent.trim();
                if (!durationText) {
                    debugLog('Empty duration text');
                    continue;
                }

                // Parse the time
                const [hours, minutes, seconds] = durationText.split(':').map(Number);
                const totalHours = hours + minutes/60 + seconds/3600;

                // Get building info for logging
                const buildingCell = row.querySelector('td.lit-item');
                const buildingName = buildingCell ? buildingCell.textContent.trim().split('\n')[0] : 'Unknown';

                debugLog('Checking build duration:', {
                    building: buildingName,
                    duration: durationText,
                    totalHours: totalHours,
                    rowClass: row.className
                });

                // If duration is over 2 hours
                if (totalHours > 2) {
                    // Find the reduction button in this row
                    const reductionButton = row.querySelector('a.order_feature.btn.btn-btr:not(.btn-instant)');
                    if (reductionButton) {
                        debugLog('Found long build, clicking reduction button:', {
                            building: buildingName,
                            duration: durationText,
                            buttonText: reductionButton.textContent.trim()
                        });

                        reductionButton.click();
                        return true;
                    }
                }
            }

            debugLog('No builds over 2 hours found needing reduction');
            return false;
        } catch (error) {
            debugLog('Error in reduceLongBuilds:', error);
            return false;
        }
    }

    function checkAndBuild() {
        debugLog('Starting building check cycle...');
        reduceLongBuilds();

        try {
            // First check if there's ongoing construction
            if (isConstructionInProgress()) {
                debugLog('Construction already in progress, skipping build check');
                return;
            }

            const { levels, highestLevel } = getResourceLevels();
            let buildableResources = [];

            // Rest of the function remains the same...
            for (const resource of BUILDING_PRIORITY) {
                if (canBuildResource(resource)) {
                    const resourceInfo = {
                        name: resource,
                        level: levels[resource],
                        levelDifference: highestLevel - levels[resource],
                        priority: BUILDING_PRIORITY.indexOf(resource)
                    };
                    buildableResources.push(resourceInfo);
                    debugLog(`${resource} is buildable:`, resourceInfo);
                }
            }

            if (buildableResources.length === 0) {
                debugLog('No resources can be built at this time');
                return;
            }

            debugLog('Buildable resources before sorting:', buildableResources);

            // Sort resources by level difference and priority
            buildableResources.sort((a, b) => {
                const aIsBehind = a.levelDifference >= LEVEL_DIFFERENCE_THRESHOLD;
                const bIsBehind = b.levelDifference >= LEVEL_DIFFERENCE_THRESHOLD;

                if (aIsBehind && !bIsBehind) return -1;
                if (!aIsBehind && bIsBehind) return 1;
                return a.priority - b.priority;
            });

            const selectedBuilding = buildableResources[0];
            debugLog('Selected building for construction:', selectedBuilding);

            // Check if any higher priority building will be available soon
            const shouldWait = BUILDING_PRIORITY.some((resource, index) => {
                if (index < BUILDING_PRIORITY.indexOf(selectedBuilding.name)) {
                    return willBeAvailableSoon(resource);
                }
                return false;
            });

            if (shouldWait) {
                debugLog('Waiting for higher priority building to become available');
                return;
            }

            // Build the selected resource
            debugLog('Proceeding with building construction:', selectedBuilding);
            if (buildResource(selectedBuilding.name)) {
                debugLog('Building command sent successfully');
            }

        } catch (error) {
            debugLog('Error in checkAndBuild:', error);
        }
    }

    // Initial check
    debugLog('Script initialized, performing initial check...');
    checkAndBuild();

    // Set up periodic page reload
    debugLog(`Setting up periodic page reload every ${CHECK_INTERVAL/1000} seconds`);
    setInterval(() => {
        debugLog('Triggering page reload for next check');
        window.location.reload();
    }, CHECK_INTERVAL);

    debugLog('Script setup completed successfully');
})();

QingJ © 2025

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