Gl tournament

Gl tournament helper (with Save & Load army buttons on tournaments)

// ==UserScript==
// @name         Gl tournament
// @namespace    http://tampermonkey.net/
// @description  Gl tournament helper (with Save & Load army buttons on tournaments)
// @version      1.3
// @author       Julian Delphiki II
// @match        https://www.heroeswm.ru/leader_spec_army.php?idx=1&setkamarmy=*
// @match        https://www.heroeswm.ru/tournaments.php*
// @run-at       document-idle
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const url = location.href;

    if (url.includes('setkamarmy=1')) {
        setTimeout(setArmyFromUrl, 1000);
    }

    if (url.includes('/tournaments.php')) {
        injectStyles();
        addSaveLoadButtons();
    }
})();

function addSaveLoadButtons() {
    const autoImgs = document.querySelectorAll('img[src*="btn_autoalignment_done.png"], img[src*="btn_autoalignment.png"]');
    if (!autoImgs.length) return;

    autoImgs.forEach(img => {
        // Find the table row that contains the army
        const armyRow = img.closest('tr');
        if (armyRow.querySelector('.saveArmyBtn')) return;

        // Create td cell for Save button
        const saveTd = document.createElement('td');
        saveTd.style.width = '35px';
        saveTd.style.paddingTop = '2px';
        saveTd.style.verticalAlign = 'middle';

        // Save Button
        const saveBtn = document.createElement('button');
        saveBtn.className = 'saveArmyBtn';
        saveBtn.title       = 'Save army';
        saveBtn.textContent = '💾';
        saveBtn.addEventListener('click', () => {
            const armyData = extractArmyData();
            if (Object.keys(armyData).length > 0) {
                // Find first unused set number
                let setNumber = null;
                for (let i = 1; i <= 99; i++) {
                    const setKey = `savedArmy_${i}`;
                    const existingArmy = GM_getValue(setKey, null);
                    if (!existingArmy || Object.keys(existingArmy).length === 0) {
                        setNumber = i;
                        break;
                    }
                }

                if (setNumber) {
                    const setKey = `savedArmy_${setNumber}`;
                    GM_setValue(setKey, armyData);
                    alert(`Army saved to set ${setNumber}!`);
                } else {
                    alert('All army set slots (1-99) are full. Please delete some sets first.');
                }
            } else {
                alert('No army data found to save.');
            }
        });
        saveTd.appendChild(saveBtn);

        // Create td cell for Load button
        const loadTd = document.createElement('td');
        loadTd.style.width = '35px';
        loadTd.style.paddingTop = '2px';
        loadTd.style.verticalAlign = 'middle';

        // Load Button
        const loadBtn = document.createElement('button');
        loadBtn.className = 'loadArmyBtn';
        loadBtn.title       = 'Load army';
        loadBtn.textContent = '📥';
        loadBtn.addEventListener('click', () => {
            showArmySetSelector();
        });
        loadTd.appendChild(loadBtn);

        // Insert both cells as the first two cells in the row
        armyRow.insertBefore(loadTd, armyRow.firstChild);
        armyRow.insertBefore(saveTd, armyRow.firstChild);
    });
}

function injectStyles() {
    if (document.getElementById('saveLoadBtnStyles')) return;

    const css = `
    .saveArmyBtn, .loadArmyBtn {
      width: 32px;
      height: 32px;
      border: 1px solid #888;
      background: #fff;
      border-radius: 50%;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      padding: 0;
      cursor: pointer;
      font-size: 18px;
      line-height: 1;
    }
    .saveArmyBtn:hover, .loadArmyBtn:hover {
      background: #f0f0f0;
    }`;

    const style = document.createElement('style');
    style.id = 'saveLoadBtnStyles';
    style.textContent = css;
    document.head.appendChild(style);
}

function showArmySetSelector() {
    // Find all available sets
    const availableSets = [];
    for (let i = 1; i <= 99; i++) {
        const setKey = `savedArmy_${i}`;
        const savedArmy = GM_getValue(setKey, null);
        if (savedArmy && Object.keys(savedArmy).length > 0) {
            availableSets.push({
                number: i,
                army: savedArmy
            });
        }
    }

    if (availableSets.length === 0) {
        alert('No saved armies found.');
        return;
    }

    createArmySetModal(availableSets);
}

function createArmySetModal(availableSets) {
    // Create modal overlay
    const overlay = document.createElement('div');
    overlay.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.7);
        z-index: 10000;
        display: flex;
        align-items: center;
        justify-content: center;
    `;

    // Create modal content
    const modal = document.createElement('div');
    modal.style.cssText = `
        background: white;
        border-radius: 8px;
        padding: 20px;
        max-width: 600px;
        max-height: 80vh;
        overflow-y: auto;
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
        position: relative;
    `;

    // Create header
    const header = document.createElement('div');
    header.style.cssText = `
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 20px;
        border-bottom: 2px solid #eee;
        padding-bottom: 10px;
    `;

    const title = document.createElement('h3');
    title.textContent = 'Select Army Set to Load';
    title.style.cssText = 'margin: 0; color: #333; font-size: 18px;';

    const closeBtn = document.createElement('button');
    closeBtn.textContent = '✕';
    closeBtn.style.cssText = `
        background: none;
        border: none;
        font-size: 20px;
        cursor: pointer;
        color: #666;
        padding: 5px;
        border-radius: 3px;
    `;
    closeBtn.onmouseover = () => closeBtn.style.background = '#f0f0f0';
    closeBtn.onmouseout = () => closeBtn.style.background = 'none';
    closeBtn.onclick = () => document.body.removeChild(overlay);

    header.appendChild(title);
    header.appendChild(closeBtn);
    modal.appendChild(header);

    // Create sets container
    const setsContainer = document.createElement('div');
    setsContainer.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px;';

    // Create set cards
    availableSets.forEach(set => {
        const setCard = document.createElement('div');
        setCard.style.cssText = `
            border: 2px solid #ddd;
            border-radius: 6px;
            padding: 15px;
            cursor: pointer;
            transition: all 0.2s ease;
            background: #f9f9f9;
        `;

        setCard.onmouseover = () => {
            setCard.style.borderColor = '#007bff';
            setCard.style.background = '#f0f8ff';
            setCard.style.transform = 'translateY(-2px)';
            setCard.style.boxShadow = '0 4px 12px rgba(0, 123, 255, 0.2)';
        };
        setCard.onmouseout = () => {
            setCard.style.borderColor = '#ddd';
            setCard.style.background = '#f9f9f9';
            setCard.style.transform = 'translateY(0)';
            setCard.style.boxShadow = 'none';
        };

        // Set header
        const setHeader = document.createElement('div');
        setHeader.style.cssText = `
            font-weight: bold;
            font-size: 16px;
            color: #333;
            margin-bottom: 10px;
            text-align: center;
            background: #007bff;
            color: white;
            padding: 8px;
            border-radius: 4px;
            margin: -15px -15px 10px -15px;
            position: relative;
        `;
        setHeader.textContent = `Army Set ${set.number}`;

        // Delete button
        const deleteBtn = document.createElement('button');
        deleteBtn.textContent = '✕';
        deleteBtn.style.cssText = `
            position: absolute;
            top: 5px;
            right: 8px;
            background: #dc3545;
            color: white;
            border: none;
            border-radius: 50%;
            width: 22px;
            height: 22px;
            font-size: 12px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s ease;
            box-shadow: 0 1px 3px rgba(0,0,0,0.3);
        `;

        deleteBtn.onmouseover = () => {
            deleteBtn.style.background = '#c82333';
            deleteBtn.style.transform = 'scale(1.1)';
        };
        deleteBtn.onmouseout = () => {
            deleteBtn.style.background = '#dc3545';
            deleteBtn.style.transform = 'scale(1)';
        };

        deleteBtn.onclick = (e) => {
            e.stopPropagation(); // Prevent triggering the card click
            if (confirm(`Delete Army Set ${set.number}?`)) {
                const setKey = `savedArmy_${set.number}`;
                GM_setValue(setKey, null); // Remove from storage
                setCard.remove(); // Remove from modal

                // If no more sets, close modal and show message
                if (setsContainer.children.length === 0) {
                    document.body.removeChild(overlay);
                    alert('All army sets have been deleted.');
                }
            }
        };

        setHeader.appendChild(deleteBtn);

        // Army composition
        const armyList = document.createElement('div');
        armyList.style.cssText = `
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
            justify-content: center;
            align-items: center;
        `;

        Object.entries(set.army).forEach(([creature, count]) => {
            const creatureContainer = document.createElement('div');
            creatureContainer.style.cssText = `
                position: relative;
                display: inline-block;
            `;

            const creatureImg = document.createElement('img');
            creatureImg.src = `https://dcdn.heroeswm.ru/i/portraits/${creature}.png`;
            creatureImg.style.cssText = `
                width: 40px;
                height: 40px;
                border: 1px solid #ccc;
                border-radius: 3px;
                display: block;
            `;

            const countOverlay = document.createElement('div');
            countOverlay.textContent = count;
            countOverlay.style.cssText = `
                position: absolute;
                top: -3px;
                right: -3px;
                background: #ff6b35;
                color: white;
                font-size: 11px;
                font-weight: bold;
                padding: 2px 4px;
                border-radius: 8px;
                min-width: 16px;
                text-align: center;
                box-shadow: 0 1px 3px rgba(0,0,0,0.3);
                line-height: 1;
            `;

            creatureContainer.appendChild(creatureImg);
            creatureContainer.appendChild(countOverlay);
            armyList.appendChild(creatureContainer);
        });

        setCard.appendChild(setHeader);
        setCard.appendChild(armyList);

        // Click handler
        setCard.onclick = () => {
            // Generate URL with army parameters
            let url = 'https://www.heroeswm.ru/leader_spec_army.php?idx=1&setkamarmy=1';
            for (const [creature, count] of Object.entries(set.army)) {
                url += `&${creature}=${count}`;
            }

            // Navigate to the URL which will trigger setArmyFromUrl
            window.location.href = url;
        };

        setsContainer.appendChild(setCard);
    });

    modal.appendChild(setsContainer);
    overlay.appendChild(modal);
    document.body.appendChild(overlay);

    // Close on overlay click
    overlay.onclick = (e) => {
        if (e.target === overlay) {
            document.body.removeChild(overlay);
        }
    };
}

function extractArmyData() {
    const armyData = {};

    // Find all creature elements in the army display
    const creatureElements = document.querySelectorAll('.cre_creature');

    creatureElements.forEach(creatureEl => {
        // Find the creature image with portrait
        const creatureImg = creatureEl.querySelector('img[src*="/portraits/"]');
        if (!creatureImg) return;

        // Extract creature name from the image src
        const src = creatureImg.src;
        const match = src.match(/\/portraits\/(.+?)\.png/);
        if (!match) return;

        const creatureName = match[1];

        // Find the count element
        const countEl = creatureEl.querySelector('.cre_amount');
        if (!countEl) return;

        const count = parseInt(countEl.textContent.trim());
        if (isNaN(count)) return;

        armyData[creatureName] = count;
    });

    return armyData;
}

function setArmyFromUrl(){
    army_try_to_reset();

    var params = document.location.href.split('&');
    console.log(params);
    var noChuvi = 0;
    for(var i=1;i<=35;i++){
        if( params[i] ){
            var chelCnt = params[i].split('=');
            console.log(chelCnt);
            chelCnt[0] = chelCnt[0].replace('30','33');
            if( $('div.creature_slider_portrait img[src*="/' + chelCnt[0] + '.png"]').length ){
                var idChuviList = $('div.creature_slider_portrait img[src*="/' + chelCnt[0] + '.png"]').prev().attr('id').replace('obj_fon','');
                obj_army[i-noChuvi]['link'] = idChuviList;
                obj_army[i-noChuvi]['count'] = chelCnt[1];
            } else {
                noChuvi++;
            }
        } else {
            noChuvi++;
        }
    }
    console.log(obj_army);
    show_details();
    if(noChuvi>0){
        //alert('Не найдено юнитов: '+noChuvi+'');
    }
}