Greasy Fork 支持简体中文。

Elethor Spire Simulator

Spire simulator

目前為 2025-03-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Elethor Spire Simulator
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  Spire simulator
// @author       Eugene
// @match        https://elethor.com/*
// @grant        none
// @license      GPL-3.0-or-later
// ==/UserScript==
/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
(function() {
    'use strict';

    const stats = [
        'MIN DAMAGE',
        'MAX DAMAGE',
        'HEALTH',
        'BREACH',
        'BARRIER',
        'CRIT CHANCE',
        'CRIT DAMAGE',
        'BLOCK CHANCE',
        'BLOCK AMOUNT'
    ];
    let simulationLogs = [];
    let monsterWins = 0;
    let userWins = 0;
    let selectedModifiers = {};

    function saveToLocalStorage(key, data) {
        try {
            localStorage.setItem(key, JSON.stringify(data));
        } catch (error) {
            console.error('Error saving to localStorage:', error);
        }
    }

    function loadFromLocalStorage(key) {
        try {
            const data = localStorage.getItem(key);
            return data ? JSON.parse(data) : null;
        } catch (error) {
            console.error('Error loading from localStorage:', error);
            return null;
        }
    }

    function calculateTotalStats() {

        const totals = {};
        for (const key in userStats) {
            totals[key] = parseFloat(userStats[key]) || 0;
        }

        console.log('Starting calculation with base stats:', {...totals});
        console.log('Processing modifiers:', Object.keys(selectedModifiers).length, selectedModifiers);

        const baseModifiers = {};
        const percentageStats = ['crit_chance', 'block_chance'];

        Object.values(selectedModifiers).forEach(modifier => {
            if (modifier.name.startsWith('Base ') && !modifier.name.includes('MINUS')) {
                const words = modifier.name.split(' '); // Split into words
                let modKey = modifier.name.replace('Base ', '').toLowerCase().replace(/ /g, '_');

                // If the second word is "Crit" and the third word is NOT "Chance", set it to crit_chance
                if (words.length >= 2 && words[1] === 'Crit' && words[2] !== 'Chance') {
                    modKey = 'crit_damage';
                }
                const modValue = parseFloat(modifier.value);

                if (!baseModifiers[modKey]) baseModifiers[modKey] = [];

                if (percentageStats.includes(modKey)) {
                    baseModifiers[modKey].push(Math.floor(modValue));
                } else {

                    baseModifiers[modKey].push(Math.floor(modValue));
                }

                console.log(`Adding Base modifier: ${modifier.name} (${modValue}) from ${modifier.tree}:${modifier.anchor}`);
            }
        });

        for (const [key, values] of Object.entries(baseModifiers)) {

            if (percentageStats.includes(key)) {
                const totalValue = values.reduce((sum, val) => sum + val, 0);
                console.log(`Applying summed Base ${key} (percentage stat): ${totalValue}`);

                if (totals[key] !== undefined) {
                    totals[key] += totalValue;
                }
            } else {

                const totalValue = Math.floor(values.reduce((sum, val) => sum + val, 0));
                console.log(`Applying summed Base ${key}: ${totalValue}`);

                if (key === 'damage') {

                    if (totals['min_damage'] !== undefined) {
                        totals['min_damage'] += totalValue;
                    }
                    if (totals['max_damage'] !== undefined) {
                        totals['max_damage'] += totalValue;
                    }
                } else if (key === 'block') {

                    if (totals['block_amount'] !== undefined) {
                        totals['block_amount'] += totalValue;
                    }
                } else {

                    if (totals[key] !== undefined) {
                        totals[key] += totalValue;
                    }
                }
            }
        }

        const baseMinusModifiers = {
            'breach_barrier': [], 
            'barrier_breach': [], 
            'health_damage': [],  
            'damage_health': []   
        };

        Object.values(selectedModifiers).forEach(modifier => {
            if (/^Base\s+/i.test(modifier.name) && /MINUS/i.test(modifier.name)) {
                const modStr = modifier.name.replace(/^Base\s+/i, '');
                const parts = modStr.split(/\s+MINUS\s+/i);
                if (parts.length !== 2) return;

                const stat1 = parts[0].toLowerCase().replace(/ /g, '_');
                const stat2 = parts[1].toLowerCase().replace(/ /g, '_');
                const modValue = parseFloat(modifier.value);

                const typeKey = `${stat1}_${stat2}`;
                if (baseMinusModifiers[typeKey] !== undefined) {
                    baseMinusModifiers[typeKey].push({
                        value: modValue,
                        source: `${modifier.tree}:${modifier.anchor}`
            });
                    console.log(`Collecting MINUS modifier: ${modifier.name} (${modValue}) from ${modifier.tree}:${modifier.anchor}`);
                }
            }
        });

        if (baseMinusModifiers['breach_barrier'].length > 0) {
            const values = baseMinusModifiers['breach_barrier'].map(mod => Math.floor(mod.value));
            const totalValue = values.reduce((sum, val) => sum + val, 0);
            console.log(`Applying BREACH MINUS BARRIER: ${values.join(' + ')} = ${totalValue}`);

            if (totals['breach'] !== undefined) totals['breach'] += totalValue;
            if (totals['barrier'] !== undefined) totals['barrier'] -= totalValue;
        }

        if (baseMinusModifiers['barrier_breach'].length > 0) {
            const values = baseMinusModifiers['barrier_breach'].map(mod => Math.floor(mod.value));
            const totalValue = values.reduce((sum, val) => sum + val, 0);
            console.log(`Applying BARRIER MINUS BREACH: ${values.join(' + ')} = ${totalValue}`);

            if (totals['breach'] !== undefined) totals['breach'] -= totalValue;
            if (totals['barrier'] !== undefined) totals['barrier'] += totalValue;
        }

        if (baseMinusModifiers['health_damage'].length > 0) {
            const values = baseMinusModifiers['health_damage'].map(mod => Math.floor(mod.value));
            const totalValue = values.reduce((sum, val) => sum + val, 0);
            console.log(`Applying HEALTH MINUS DAMAGE: ${values.join(' + ')} = ${totalValue}`);

            if (totals['health'] !== undefined) totals['health'] += totalValue;
            if (totals['min_damage'] !== undefined) totals['min_damage'] -= Math.floor(totalValue / 3);
            if (totals['max_damage'] !== undefined) totals['max_damage'] -= Math.floor(totalValue / 3);
        }

        if (baseMinusModifiers['damage_health'].length > 0) {
            const values = baseMinusModifiers['damage_health'].map(mod => Math.floor(mod.value));
            const totalValue = values.reduce((sum, val) => sum + val, 0);
            console.log(`Applying DAMAGE MINUS HEALTH: ${values.join(' + ')} = ${totalValue}`);

            if (totals['min_damage'] !== undefined) totals['min_damage'] += totalValue;
            if (totals['max_damage'] !== undefined) totals['max_damage'] += totalValue;
            if (totals['health'] !== undefined) totals['health'] -= Math.floor(totalValue * 3);
        }
        for (const key in totals) {
            if (totals[key] < 0) {
                console.log(`Fixing negative stat: ${key} from ${totals[key]} to 0`);
                totals[key] = 0;
            }
        }

        const incSums = {};

        Object.values(selectedModifiers).forEach(modifier => {
            if (/^Increase(d)?\s+/.test(modifier.name)) {
                const statKey = modifier.name.replace(/^Increase(d)?\s+/, '').toLowerCase().replace(/ /g, '_');

                if (!incSums[statKey]) incSums[statKey] = 0;

                const value = parseFloat(modifier.value);
                incSums[statKey] += value;
                console.log(`Adding Increased modifier: ${modifier.name} (${value}) from ${modifier.tree}:${modifier.anchor}`);
            }
        });
        for (const stat in incSums) {

            const totalIncrease = incSums[stat];

            const multiplier = 1 + totalIncrease / 100;
            console.log(`Applying Increased modifier for ${stat}: +${totalIncrease}% (x${multiplier.toFixed(2)})`);

            if (stat === 'damage') {
                if (totals['min_damage'] !== undefined) {

                    totals['min_damage'] = Math.floor(totals['min_damage'] * multiplier);
                }
                if (totals['max_damage'] !== undefined) {
                    totals['max_damage'] = Math.floor(totals['max_damage'] * multiplier);
                }
            } else if (stat === 'block') {
                if (totals['block_amount'] !== undefined) {
                    totals['block_amount'] = Math.floor(totals['block_amount'] * multiplier);
                }
            } else {
                if (totals[stat] !== undefined) {
                    totals[stat] = Math.floor(totals[stat] * multiplier);
                }
            }
        }

        console.log('Final calculated stats:', {...totals});

        const finalTotals = {};
        for (const key in totals) {
            finalTotals[key] = totals[key].toString();
        }
        return finalTotals;
    }

    function logSelectedModifiers() {
        console.log('Currently selected modifiers:');
        Object.entries(selectedModifiers).forEach(([key, mod]) => {
            console.log(`${key}: ${mod.name} (${mod.value})`);
        });
    }

    let monsterStats = {};
    let modifiersData = {};
    let userStats = {
        min_damage: 90,
        max_damage: 110,
        health: 800,
        breach: 120,
        barrier: 120,
        crit_chance: 5,
        crit_damage: 100,
        block_chance: 5,
        block_amount: 10
    };
    let autoMonsterStats = false;
    let currentFloor = 1;

    let simulationResultsContainer = null;

    function calculateDamageMultiplier(breach, barrier) {
        if (breach > barrier) {
            return 4 * (2 / Math.PI) * Math.atan((breach - barrier) / Math.sqrt(breach * barrier)) + 1;
        } else {
            return Math.max(0.25, (2 / Math.PI) * Math.atan((breach - barrier) / Math.sqrt(breach * barrier)) + 1);
        }
    }

    function calculateDamage(attacker, defender) {

        if (attacker === userStats) {
            attacker = calculateTotalStats();
        }

        const baseDamage = (parseFloat(attacker.min_damage) + parseFloat(attacker.max_damage)) / 2;
        const multiplier = calculateDamageMultiplier(parseFloat(attacker.breach), parseFloat(defender.barrier));
        let damage = baseDamage * multiplier;

        const critRoll = Math.random() * 100;
        if (critRoll <= parseFloat(attacker.crit_chance)) {
            damage = damage * (parseFloat(attacker.crit_damage) / 100);
        }
        const blockRoll = Math.random() * 100;
        if (blockRoll <= parseFloat(defender.block_chance)) {
            damage = Math.max(0, damage - parseFloat(defender.block_amount));
        }

        return Math.round(damage);
    }
    const simulationLogOverlay = document.createElement('div');
    simulationLogOverlay.style.cssText = `
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(22, 28, 36, 0.95);
    color: #ecf0f1;
    padding: 40px 20px 20px 20px;
    overflow-y: auto;
    display: none;
    z-index: 10000;
    box-sizing: border-box;
`;
    simulationLogOverlay.id = 'simulation-log-overlay';
    const backButton = document.createElement('button');
    backButton.textContent = 'Back to results';
    backButton.style.cssText = `
    position: absolute;
    top: 10px;
    right: 10px;
    padding: 5px 10px;
    background: #e67e22;
    border: none;
    border-radius: 4px;
    color: white;
    cursor: pointer;
`;
    backButton.addEventListener('click', () => {
        simulationLogOverlay.style.display = 'none';
        simulationResultsContainer.style.display = 'block';
    });
    simulationLogOverlay.appendChild(backButton);

    function simulateCombat() {
        const startTime = performance.now();

        const combatLog = document.createElement('div');
        combatLog.style.cssText = `
        margin-top: 20px;
        padding: 10px;
        background: rgba(44, 62, 80, 0.95);
        border-radius: 4px;
        max-height: 400px;
        overflow-y: auto;
        font-family: monospace;
        line-height: 1.4;
    `;

       const totalUserStats = calculateTotalStats();

       let userHealth = parseFloat(totalUserStats.health);
       let monsterHealth = parseFloat(monsterStats.health);
       let round = 1;
       let monsterDefeated = false;

       function logAttack(attacker, defender, isUser) {
           const baseDamage = (parseFloat(attacker.min_damage) + parseFloat(attacker.max_damage)) / 2;
           const multiplier = calculateDamageMultiplier(parseFloat(attacker.breach), parseFloat(defender.barrier));
           let damage = baseDamage * multiplier;

           const critRoll = Math.random() * 100;
           const isCrit = critRoll <= parseFloat(attacker.crit_chance);
           if (isCrit) {
               damage = damage * (parseFloat(attacker.crit_damage) / 100);
           }
           const blockRoll = Math.random() * 100;
           const isBlocked = blockRoll <= parseFloat(defender.block_chance);
           const originalDamage = damage;
           if (isBlocked) {
               damage = Math.max(0, damage - parseFloat(defender.block_amount));
           }

           const color = isUser ? '#2ecc71' : '#e74c3c';
           const attacker_name = isUser ? 'You' : 'Monster';
           const defender_name = isUser ? 'Monster' : 'You';

           let log = `<div style="color: ${color}; margin-bottom: 10px;">`;
           log += `<div><b>Round ${round} - ${attacker_name}'s Attack</b></div>`;
           log += `<div>Base Damage: ${Math.round(baseDamage)} (${attacker.min_damage}-${attacker.max_damage})</div>`;
           log += `<div>Breach vs Barrier: ${attacker.breach} vs ${defender.barrier} (x${multiplier.toFixed(2)})</div>`;

           if (isCrit) {
               log += `<div>Critical Hit! (${attacker.crit_chance}% chance, +${attacker.crit_damage}% damage)</div>`;
           }
           if (isBlocked) {
               log += `<div>Attack Blocked! (${defender.block_chance}% chance, ${defender.block_amount} blocked)</div>`;
               log += `<div>Damage Reduced: ${Math.round(originalDamage)} → ${Math.round(damage)}</div>`;
           }

           const finalDamage = Math.round(damage);
           const newHealth = isUser ? Math.max(0, monsterHealth - finalDamage) : Math.max(0, userHealth - finalDamage);
           log += `<div><b>Final Damage: ${finalDamage}</b></div>`;
           log += `<div>${defender_name}'s Health: ${isUser ? monsterHealth : userHealth} → ${newHealth}</div>`;
           log += '</div>';

           combatLog.innerHTML += log;
           return finalDamage;
       }

       while (userHealth > 0) {
           const userDamage = logAttack(totalUserStats, monsterStats, true);
           monsterHealth -= userDamage;

           if (monsterHealth <= 0) {
               monsterDefeated = true;
               break;
           }

           const monsterDamage = logAttack(monsterStats, totalUserStats, false);
           userHealth -= monsterDamage;

           round++;
       }

       const winner = monsterDefeated ? 'You win!' : 'Monster wins!';
       combatLog.innerHTML += `<div style="color: #f1c40f; margin-top: 10px; font-weight: bold; font-size: 1.2em;">${winner}</div>`;
       const endTime = performance.now();
       console.log(`Single simulation time: ${(endTime - startTime).toFixed(6)} ms`);

       return combatLog;
   }

    async function fetchModifiers() {
        try {
            const response = await fetch('https://elethor.com/game/neo-spire/modifiers');
            const data = await response.json();
            return data.modifiers || {};
        } catch (error) {
            console.error('Error fetching modifiers:', error);
            return null;
        }
    }

    function createModifiersList(modifiers) {
        selectedModifiers = {};
        selectedModifiers = loadFromLocalStorage('selectedModifiers') || {};

        const container = document.createElement('div');

        container.style.cssText = `
        display: flex;
        gap: 20px;
        padding: 10px;
        font-family: Arial, sans-serif;
        color: #ecf0f1;
        max-height: 80vh;
        overflow-y: auto;
        width: 600px;
    `;

    ['Defense', 'Offense'].forEach(tree => {
        const treeData = modifiers[tree];
        if (!treeData) return;

        const treeSection = document.createElement('div');
        treeSection.style.cssText = `flex: 1; min-width: 0;`;
        treeSection.innerHTML = `<h3 style="color: #3498db; margin: 10px 0;">${tree}</h3>`;

        Object.entries(treeData).forEach(([anchor, options]) => {
            const anchorSection = document.createElement('div');
            anchorSection.style.cssText = `
                margin-bottom: 15px;
                padding: 8px;
                background: rgba(44, 62, 80, 0.5);
                border-radius: 4px;
            `;

            const headerContainer = document.createElement('div');
            headerContainer.style.cssText = `
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 5px;
            `;

            const anchorName = document.createElement('div');
            anchorName.style.cssText = `color: #f1c40f;`;
            anchorName.textContent = anchor;
            headerContainer.appendChild(anchorName);

            const deselectButton = document.createElement('button');
            deselectButton.textContent = 'Deselect';
            deselectButton.style.cssText = `
                padding: 2px 6px;
                background: #e74c3c;
                border: none;
                border-radius: 3px;
                color: white;
                cursor: pointer;
                font-size: 0.8em;
                transition: background 0.2s;
            `;
            deselectButton.addEventListener('mouseenter', () => { deselectButton.style.background = '#c0392b'; });
            deselectButton.addEventListener('mouseleave', () => { deselectButton.style.background = '#e74c3c'; });

            deselectButton.addEventListener('click', () => {

                optionsContainer.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
                    checkbox.checked = false;
                });

                const compositeKey = `${tree}:${anchor}`;
                if (selectedModifiers[compositeKey]) {
                    delete selectedModifiers[compositeKey];
                    console.log(`Deselected modifier at ${compositeKey}`);
                    console.log('Remaining selected modifiers:', {...selectedModifiers});

                    saveToLocalStorage('selectedModifiers', selectedModifiers);
                }
            });
            headerContainer.appendChild(deselectButton);
            anchorSection.appendChild(headerContainer);

            const optionsContainer = document.createElement('div');
            optionsContainer.style.cssText = `
                display: flex;
                flex-wrap: wrap;
                gap: 10px;
                margin-top: 5px;
            `;

            const radioGroupName = `${tree.toLowerCase()}-${anchor.replace(/\s+/g, '-').toLowerCase()}`;

            options.forEach(option => {
                const optionDiv = document.createElement('div');
                optionDiv.style.cssText = `
                    padding: 5px;
                    border-left: 2px solid #3498db;
                    padding-left: 8px;
                    flex: 1;
                    min-width: 150px;
                    max-width: calc(50% - 5px);
                    cursor: pointer;
                    transition: background-color 0.3s;
                    display: flex;
                    align-items: center;
                `;

                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.style.marginRight = '8px';
                checkbox.dataset.tree = tree;
                checkbox.dataset.anchor = anchor;
                checkbox.dataset.optionIndex = options.indexOf(option);

                checkbox.addEventListener('change', (e) => {

                    if (e.target.checked) {
                        optionsContainer.querySelectorAll('input[type="checkbox"]').forEach(cb => {
                            if (cb !== e.target) {
                                cb.checked = false;
                            }
                        });

                        const compositeKey = `${tree}:${anchor}`;
                        selectedModifiers[compositeKey] = { ...option, tree: tree, anchor: anchor };

                        optionsContainer.querySelectorAll('div').forEach(div => {
                            div.style.backgroundColor = '';
                        });
                        optionDiv.style.backgroundColor = '#2980b9';

                        console.log('Selected modifier:', compositeKey, selectedModifiers[compositeKey]);
                    } else {

                        const compositeKey = `${tree}:${anchor}`;
                        if (selectedModifiers[compositeKey]) {
                            delete selectedModifiers[compositeKey];
                            optionDiv.style.backgroundColor = '';
                            console.log(`Deselected modifier at ${compositeKey}`);
                        }
                    }

                    saveToLocalStorage('selectedModifiers', selectedModifiers);

                    console.log('All selected modifiers:', {...selectedModifiers});
                });
                optionDiv.appendChild(checkbox);

                const textContainer = document.createElement('div');
                textContainer.style.cssText = 'flex: 1;';
                textContainer.innerHTML = `
                    <div style="font-weight: bold;">${option.name}</div>
                    <div style="color: #95a5a6; font-size: 0.9em;">Cost: ${option.cost} | Value: ${option.value}</div>
                `;

                optionDiv.appendChild(textContainer);

                optionDiv.addEventListener('click', (e) => {

                    if (e.target !== checkbox) {
                        checkbox.checked = !checkbox.checked;

                        const changeEvent = new Event('change', { bubbles: true });
                        checkbox.dispatchEvent(changeEvent);
                    }
                });

                optionsContainer.appendChild(optionDiv);
            });

            anchorSection.appendChild(optionsContainer);
            treeSection.appendChild(anchorSection);
        });

        container.appendChild(treeSection);
    });

    const savedModifiers = loadFromLocalStorage('selectedModifiers') || {};
    Object.entries(savedModifiers).forEach(([compositeKey, modData]) => {
        const [tree, anchor] = compositeKey.split(':');

        const options = modifiers[tree]?.[anchor] || [];
        const matchingOption = options.find(opt =>
                                            opt.name === modData.name &&
                                            opt.value === modData.value
                                           );

        if (matchingOption) {
            const optionIndex = options.indexOf(matchingOption);

            const checkbox = container.querySelector(
                `input[data-tree="${tree}"][data-anchor="${anchor}"][data-option-index="${optionIndex}"]`
        );

        if (checkbox) {
            checkbox.checked = true;

            const parentDiv = checkbox.closest('div');
            if (parentDiv) {
                parentDiv.style.backgroundColor = '#2980b9';
            }

            selectedModifiers[compositeKey] = {
                ...matchingOption,
                tree: tree,
                anchor: anchor
            };
        }
    }
});

    return container;
}

    async function fetchMonsterStats(floor) {
        try {
            const response = await fetch(`https://elethor.com/game/neo-spire/${floor}`);
            const data = await response.json();
            if (data && data.floor && data.floor.stats) {
                return data.floor.stats;
            } else {
                console.error('Invalid data format from API');
                return null;
            }
        } catch (error) {
            console.error('Error fetching monster stats:', error);
            return null;
        }
    }


    function updateMonsterStatInputs(stats) {
        if (!stats) return;
        document.querySelectorAll('input[data-stat]').forEach(input => {
            if (input.dataset.type === 'monster') {
                const statKey = input.dataset.stat.toLowerCase().replace(/ /g, '_');
                if (stats[statKey] !== undefined) {
                    input.value = stats[statKey];
                    const event = new Event('input', { bubbles: true });
                    input.dispatchEvent(event);
                }
            }
        });
    }
    function updateStatInputs() {
        document.querySelectorAll('input[data-stat]').forEach(input => {
            const statKey = input.dataset.stat.toLowerCase().replace(/ /g, '_');

            if (input.dataset.type === 'monster') {
                if (monsterStats[statKey] !== undefined) {
                    input.value = monsterStats[statKey];
                    const event = new Event('input', { bubbles: true });
                    input.dispatchEvent(event);
                }
            } else if (input.dataset.type === 'user') {  // Update "You" stats
                if (userStats[statKey] !== undefined) {
                    input.value = userStats[statKey];
                    const event = new Event('input', { bubbles: true });
                    input.dispatchEvent(event);
                }
            }
        });
    }


    function updateTotalStats() {
        const totalStats = calculateTotalStats();
        document.querySelectorAll('span[data-stat]').forEach(span => {
            const stat = span.dataset.stat.toLowerCase().replace(/ /g, '_');
            span.textContent = totalStats[stat] || '-';
        });
    }


    function createStatsTable() {
        const tableContainer = document.createElement('div');
        tableContainer.style.cssText = `
            padding: 10px;
            font-family: Arial, sans-serif;
            color: #ecf0f1;
            flex: 1;
        `;
        const table = document.createElement('table');
        table.style.cssText = `
            border-collapse: collapse;
            width: 100%;
        `;

        const autoStatsContainer = document.createElement('div');
        autoStatsContainer.style.cssText = `
            display: flex;
            align-items: center;
            margin-bottom: 10px;
            padding: 8px;
            background: rgba(44, 62, 80, 0.5);
            border-radius: 4px;
        `;
        const autoStatsCheckbox = document.createElement('input');
        autoStatsCheckbox.type = 'checkbox';
        autoStatsCheckbox.id = 'auto-monster-stats';
        autoStatsCheckbox.style.cssText = `margin-right: 8px;`;
        const autoStatsLabel = document.createElement('label');
        autoStatsLabel.htmlFor = 'auto-monster-stats';
        autoStatsLabel.textContent = 'Auto Monster Stats';
        autoStatsLabel.style.cssText = `margin-right: 15px; color: #3498db;`;
        const floorLabel = document.createElement('label');
        floorLabel.htmlFor = 'floor-input';
        floorLabel.textContent = 'Floor:';
        floorLabel.style.cssText = `margin-right: 8px; color: #3498db;`;
        const floorInput = document.createElement('input');
        floorInput.type = 'number';
        floorInput.id = 'floor-input';
        floorInput.min = '1';
        floorInput.value = '1';
        floorInput.style.cssText = `
            width: 60px;
            background: #2c3e50;
            border: 1px solid #34495e;
            color: #ecf0f1;
            padding: 4px;
            border-radius: 3px;
        `;
        const fetchButton = document.createElement('button');
        fetchButton.textContent = 'Fetch';
        fetchButton.style.cssText = `
            margin-left: 8px;
            padding: 4px 8px;
            background: #3498db;
            border: none;
            border-radius: 4px;
            color: white;
            cursor: pointer;
            font-weight: bold;
            transition: background 0.3s;
        `;
        fetchButton.addEventListener('mouseenter', () => { fetchButton.style.background = '#2980b9'; });
        fetchButton.addEventListener('mouseleave', () => { fetchButton.style.background = '#3498db'; });
        autoStatsCheckbox.addEventListener('change', async (e) => {
            autoMonsterStats = e.target.checked;
            document.querySelectorAll('input[data-type="monster"]').forEach(input => {
                input.disabled = autoMonsterStats;
                input.style.opacity = autoMonsterStats ? '0.5' : '1';
            });
            if (autoMonsterStats) {
                currentFloor = parseInt(floorInput.value) || 1;
                const stats = await fetchMonsterStats(currentFloor);
                if (stats) { updateMonsterStatInputs(stats); }
            }
        });
        floorInput.addEventListener('input', (e) => { currentFloor = parseInt(e.target.value) || 1; });
        fetchButton.addEventListener('click', async () => {
            if (autoMonsterStats) {
                currentFloor = parseInt(floorInput.value) || 1;
                const stats = await fetchMonsterStats(currentFloor);
                if (stats) { updateMonsterStatInputs(stats); }
            }
        });
        autoStatsContainer.appendChild(autoStatsCheckbox);
        autoStatsContainer.appendChild(autoStatsLabel);
        autoStatsContainer.appendChild(floorLabel);
        autoStatsContainer.appendChild(floorInput);
        autoStatsContainer.appendChild(fetchButton);
        tableContainer.appendChild(autoStatsContainer);

        const simulationControls = document.createElement('div');
        simulationControls.style.cssText = 'margin-bottom: 10px; display: flex; align-items: center; gap: 10px;';
        const numSimLabel = document.createElement('label');
        numSimLabel.htmlFor = 'num-simulations';
        numSimLabel.textContent = 'Number of simulations:';
        numSimLabel.style.cssText = 'color: #3498db;';
        simulationControls.appendChild(numSimLabel);
        const numSimInput = document.createElement('input');
        numSimInput.type = 'number';
        numSimInput.id = 'num-simulations';
        numSimInput.value = '10';
        numSimInput.style.cssText = `
            width: 60px;
            background: #2c3e50;
            border: 1px solid #34495e;
            color: #ecf0f1;
            padding: 4px;
            border-radius: 3px;
        `;
        simulationControls.appendChild(numSimInput);
        tableContainer.appendChild(simulationControls);

        const header = document.createElement('tr');
        ['STAT', 'MONSTER', 'YOU', 'TOTAL YOU'].forEach(text => {
            const th = document.createElement('th');
            th.textContent = text;
            th.style.cssText = `
                padding: 8px;
                text-align: left;
                border-bottom: 2px solid #34495e;
                color: #3498db;
            `;
            header.appendChild(th);
        });
        table.appendChild(header);

        stats.forEach(stat => {
            const row = document.createElement('tr');
            const nameCell = document.createElement('td');
            nameCell.textContent = stat;
            nameCell.style.cssText = `padding: 8px; border-bottom: 1px solid #34495e;`;
            const monsterCell = document.createElement('td');
            const monsterInput = document.createElement('input');
            monsterInput.type = 'text';
            monsterInput.dataset.stat = stat;
            monsterInput.dataset.type = 'monster';
            monsterInput.addEventListener('input', (e) => {
                const value = e.target.value;
                const statKey = stat.toLowerCase().replace(/ /g, '_');
                monsterStats[statKey] = value;

                saveToLocalStorage('monsterStats', monsterStats); 
                console.log('Monster stats updated:', monsterStats);
            });

            monsterInput.style.cssText = `
                width: 80px;
                background: #2c3e50;
                border: 1px solid #34495e;
                color: #ecf0f1;
                padding: 4px;
                border-radius: 3px;
            `;
            monsterCell.appendChild(monsterInput);
            monsterCell.style.cssText = `padding: 8px; border-bottom: 1px solid #34495e;`;
            const yourCell = document.createElement('td');
            const yourInput = document.createElement('input');
            yourInput.type = 'text';
            yourInput.dataset.stat = stat;
            yourInput.addEventListener('input', (e) => {
                const value = e.target.value;
                const statKey = stat.toLowerCase().replace(/ /g, '_');
                userStats[statKey] = value;

                console.log('User stats updated:', userStats);
            });
            yourInput.style.cssText = `
                width: 80px;
                background: #2c3e50;
                border: 1px solid #34495e;
                color: #ecf0f1;
                padding: 4px;
                border-radius: 3px;
            `;
            yourCell.appendChild(yourInput);
            yourCell.style.cssText = `padding: 8px; border-bottom: 1px solid #34495e;`;
            row.appendChild(nameCell);
            row.appendChild(monsterCell);
            row.appendChild(yourCell);
            const totalCell = document.createElement('td');
            const totalSpan = document.createElement('span');
            totalSpan.dataset.stat = stat;
            totalSpan.textContent = '-';
            totalSpan.style.cssText = `color: #2ecc71; font-weight: bold;`;
            totalCell.appendChild(totalSpan);
            totalCell.style.cssText = `padding: 8px; border-bottom: 1px solid #34495e;`;
            row.appendChild(totalCell);
            row.addEventListener('mouseenter', () => { row.style.backgroundColor = 'rgba(52, 73, 94, 0.5)'; });
            row.addEventListener('mouseleave', () => { row.style.backgroundColor = ''; });
            table.appendChild(row);
        });
        tableContainer.appendChild(table);

        const simulateButton = document.createElement('button');
        simulateButton.textContent = 'Simulate Combat';
        simulateButton.style.cssText = `
            margin-top: 10px;
            padding: 8px 16px;
            background: #2ecc71;
            border: none;
            border-radius: 4px;
            color: white;
            cursor: pointer;
            width: 100%;
            font-weight: bold;
            transition: background 0.3s;
        `;
        simulateButton.addEventListener('mouseenter', () => { simulateButton.style.background = '#27ae60'; });
        simulateButton.addEventListener('mouseleave', () => { simulateButton.style.background = '#2ecc71'; });
        simulateButton.addEventListener('click', () => {

            simulationResultsContainer.innerHTML = '';
            simulationLogs = [];
            monsterWins = 0;
            userWins = 0;
            const totalUserStats = calculateTotalStats();

            const numSims = parseInt(document.getElementById('num-simulations').value) || 1;
            for (let i = 0; i < numSims; i++) {
                const combatLog = simulateCombat();

                combatLog.style.display = 'none';
                combatLog.id = `combat-log-${i}`;
                simulationLogs.push(combatLog);

                const finalLine = combatLog.querySelector('div[style*="color: #f1c40f"]');
                const resultText = finalLine ? finalLine.textContent : 'Unknown';
                if (resultText === 'You win!') {
                    userWins++;
                } else if (resultText === 'Monster wins!') {
                    monsterWins++;
                }
                const simResultDiv = document.createElement('div');
                simResultDiv.style.cssText = 'margin-bottom: 10px; padding: 5px; background: rgba(44, 62, 80, 0.5); border-radius: 4px;';
                simResultDiv.innerHTML = `<strong>Simulation ${i + 1}:</strong> ${resultText}`;
                const logsButton = document.createElement('button');
                logsButton.textContent = 'Logs';
                logsButton.style.cssText = 'margin-left: 10px; padding: 2px 6px;';
                logsButton.addEventListener('click', () => {
                    simulationResultsContainer.style.display = 'none';
                    simulationLogOverlay.innerHTML = '';
                    simulationLogOverlay.appendChild(backButton);
                    simulationLogOverlay.appendChild(simulationLogs[i]);
                    simulationLogs[i].style.display = 'block';
                    simulationLogOverlay.style.display = 'block';
                });
                simResultDiv.appendChild(logsButton);
                simulationResultsContainer.appendChild(simResultDiv);
            }
            const statsDiv = document.createElement('div');
            statsDiv.style.cssText = `
        margin-bottom: 15px;
        padding: 10px;
        background: rgba(44, 62, 80, 0.8);
        border-radius: 4px;
        font-weight: bold;
        display: flex;
        justify-content: space-between;
    `;

            const winrate = (userWins / numSims * 100).toFixed(2);

            statsDiv.innerHTML = `
        <div style="color: #e74c3c;">Monster Wins: ${monsterWins}</div>
        <div style="color: #2ecc71;">Your Wins: ${userWins}</div>
        <div style="color: #f1c40f;">Winrate: ${winrate}%</div>
    `;


            simulationResultsContainer.insertBefore(statsDiv, simulationResultsContainer.firstChild);
        });
        tableContainer.appendChild(simulateButton);

        const confirmButton = document.createElement('button');
        confirmButton.textContent = 'Confirm Modifiers';
        confirmButton.style.cssText = `
            margin-top: 10px;
            margin-bottom: 10px;
            padding: 8px 16px;
            background: #e67e22;
            border: none;
            border-radius: 4px;
            color: white;
            cursor: pointer;
            width: 100%;
            font-weight: bold;
            transition: background 0.3s;
        `;
        confirmButton.addEventListener('mouseenter', () => { confirmButton.style.background = '#d35400'; });
        confirmButton.addEventListener('mouseleave', () => { confirmButton.style.background = '#e67e22'; });
        confirmButton.addEventListener('click', () => {
            logSelectedModifiers();
            updateTotalStats();
        });
        tableContainer.appendChild(confirmButton);

        let isDragging = false, currentX, currentY, initialX, initialY, xOffset = 0, yOffset = 0;
        tableContainer.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);
        function dragStart(e) {
            initialX = e.clientX - xOffset;
            initialY = e.clientY - yOffset;
            if (e.target === tableContainer) { isDragging = true; }
        }
        function drag(e) {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                xOffset = currentX;
                yOffset = currentY;
                setTranslate(currentX, currentY, tableContainer);
            }
        }
        function setTranslate(xPos, yPos, el) {
            el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
        }
        function dragEnd() {
            initialX = currentX;
            initialY = currentY;
            isDragging = false;
        }

        document.body.appendChild(tableContainer);
    }

    window.addEventListener('load', async () => {
        const mainContainer = document.createElement('div');
        const closeButton = document.createElement('button');
        closeButton.textContent = 'X';
        closeButton.style.cssText = `
    position: absolute;
    top: 10px;
    right: 10px;
    padding: 5px 10px;
    background: #e74c3c;
    border: none;
    border-radius: 4px;
    color: white;
    cursor: pointer;
    font-weight: bold;
    z-index: 10001;
`;
        closeButton.addEventListener('click', () => {
            mainContainer.style.display = 'none';
            showOverlayButton.style.display = 'block';
        });

        mainContainer.appendChild(closeButton);
        const showOverlayButton = document.createElement('button');
        showOverlayButton.textContent = 'Open Spire Simulator';
        showOverlayButton.style.cssText = `
    position: fixed;
    top: 10px;
    right: 10px;
    padding: 5px 10px;
    background: #2ecc71;
    border: none;
    border-radius: 4px;
    color: white;
    cursor: pointer;
    font-weight: bold;
    z-index: 9998;
    display: none;
`;
        showOverlayButton.addEventListener('click', () => {
            mainContainer.style.display = 'block';
            showOverlayButton.style.display = 'none';
        });
        document.body.appendChild(showOverlayButton);

        const savedUserStats = {};
        const savedMonsterStats = loadFromLocalStorage('monsterStats') || {};

        userStats = savedUserStats;
        monsterStats = savedMonsterStats;

        mainContainer.style.cssText = `
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(22, 28, 36, 0.95);
    z-index: 9999;
    box-shadow: 0 0 10px rgba(0,0,0,0.5);
    overflow: auto;
`;

        const contentContainer = document.createElement('div');
        contentContainer.style.cssText = `
            display: flex;
            flex-direction: row;
            gap: 20px;
            padding: 20px;
        `;

        simulationResultsContainer = document.createElement('div');
        simulationResultsContainer.id = 'simulation-results';
        simulationResultsContainer.style.cssText = `
            flex: 1;
            background: rgba(22, 28, 36, 0.95);
            color: #ecf0f1;
            padding: 10px;
            border: 1px solid #2c3e50;
            border-radius: 4px;
            max-height: 400px;
            overflow-y: auto;
        `;
        contentContainer.appendChild(simulationResultsContainer);

        createStatsTable();
        document.querySelectorAll('input[data-stat]').forEach(input => {
            const statKey = input.dataset.stat.toLowerCase().replace(/ /g, '_');

            if (input.dataset.type === 'monster' && monsterStats[statKey] !== undefined) {
                input.value = monsterStats[statKey];
                const event = new Event('input', { bubbles: true });
                input.dispatchEvent(event);
            }

            if (input.dataset.type === 'user' && userStats[statKey] !== undefined) {
                input.value = userStats[statKey];
                const event = new Event('input', { bubbles: true });
                input.dispatchEvent(event);
            }
        });

        const statsTable = document.querySelector('div[style*="font-family: Arial"]');
        if (statsTable) {
            contentContainer.appendChild(statsTable);
        }

        const modifiers = await fetchModifiers();
        if (modifiers) {
            modifiersData = modifiers;
            const modifiersList = createModifiersList(modifiers);
            contentContainer.appendChild(modifiersList);
        }

        mainContainer.appendChild(contentContainer);

        document.body.appendChild(simulationLogOverlay);

        document.body.appendChild(mainContainer);

    });
})();