Boss Identifier Replacer

Replace two-letter identifiers with unique single characters in the boss map and panel without altering data-title attributes. Mark blocks with specified styles based on boss names.

// ==UserScript==
// @name         Boss Identifier Replacer
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Replace two-letter identifiers with unique single characters in the boss map and panel without altering data-title attributes. Mark blocks with specified styles based on boss names.
// @author       Lucky11
// @match        https://www.dfprofiler.com/bossmap
// @match        https://*.dfprofiler.com/profile/view/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Enable or disable features
    const enableIdentifierReplacement = true; // Set to false to disable identifier replacement
    const enableBossStyling = true; // Set to false to disable custom boss block styling
    const useFullTitleForStyling = true; // Set to true to use EXACT Boss block name as it is shown on BossMap for example : "4 x Bandits" check example below!
    //if it is set to false then it will extract only boss name not the count "2 x Bandits" and "4 x Bandits" would be seen as "Bandits" and the same style would apply!

//     // Editable list of bosses and their corresponding styles using hex codes
//     const bossStyles = {
//         "4 x Bandits": "1px dashed yellow", // 1px = width, dashed = border style (can be set to solid or dotted as well), yellow = color (HEX code can be used as well)
//         "8 x Bandits": "1px dashed red", // "Flaming Titan": "2px solid #27BEF5" // #008000 = green #0000FF = blue #FFA500 = orange
//         "4 x Six-Armed Bandit": "1px dashed yellow", //
//         "BossNameExample2": "none",
//         "BossNameExample3": "none"
//     };

    //use this style if useFullTitleForStyling is set to false
    const bossStyles = {
        "Bandits": "1px dashed red",//if const useFullTitleForStyling = true; then would be "4 x Bandits" instead of "Bandits"
        'Six-Armed Bandit':'1px dashed yellow',
        //"Flaming": "2px solid #27BEF5",// If only a part of Boss name is used like "Flaming" then it will change Style for all "Flaming" Bosses like "Flaming Spider", "Flaming Titan" etc...
     };

    // Function to generate unique single-character replacements
    const generateUniqueChars = (startChar) => {
        const usedChars = new Set();
        const replacements = {};
        let charCode = startChar.charCodeAt(0);

        return (key) => {
            if (!replacements[key]) {
                while (usedChars.has(String.fromCharCode(charCode))) {
                    charCode++;
                }
                const newChar = String.fromCharCode(charCode);
                usedChars.add(newChar);
                replacements[key] = newChar;
                charCode++;
            }
            return replacements[key];
        };
    };

    const getLowercaseChar = generateUniqueChars('a'); // For class="preboss"
    const getUniqueChar = generateUniqueChars('A'); // For class="oldboss"

    // Function to replace identifiers in the boss panel and map
    const replaceIdentifiers = () => {
        if (enableIdentifierReplacement) {
            const bossTableRows = document.querySelectorAll('#cur_bosses tbody tr');
            const identifiers = new Set();

            // Collect identifiers from the boss panel
            bossTableRows.forEach(row => {
                const slot = row.cells[0].textContent.trim();
                if (slot.length === 2) {
                    identifiers.add(slot);
                }
            });

            // Replace identifiers in the boss panel
            bossTableRows.forEach(row => {
                const slot = row.cells[0];
                if (identifiers.has(slot.textContent.trim())) {
                    const newChar = getUniqueChar(slot.textContent.trim());
                    slot.textContent = newChar;
                }
            });

            // Replace identifiers on the map
            const mapCells = document.querySelectorAll('#boss-table .coord');
            mapCells.forEach(cell => {
                const spans = cell.querySelectorAll('span');
                spans.forEach(span => {
                    const currentText = span.textContent.trim();

                    // Check if the current text matches any identifier
                    if (identifiers.has(currentText)) {
                        if (cell.querySelector('.preboss')) {
                            // Keep class="preboss" and use lowercase letters
                            const newChar = getLowercaseChar(currentText);
                            span.textContent = newChar;
                        } else if (cell.querySelector('.oldboss')) {
                            // Keep class="oldboss" and use unique characters
                            const newChar = getUniqueChar(currentText);
                            span.textContent = newChar;
                        } else {
                            // Default replacement for other cases
                            const newChar = getUniqueChar(currentText);
                            span.textContent = newChar; // Only change the visible text
                        }
                    }
                });
            });
        }
    };

    // Function to apply boss styling
    const applyBossStyling = () => {
        if (!enableBossStyling) return; // Exit if styling is disabled

        const mapCells = document.querySelectorAll('#boss-table .coord');
        mapCells.forEach(cell => {
            // Use full data-title or just the boss name based on user preference
            const bossName = useFullTitleForStyling ? cell.dataset.title : cell.dataset.title.split(" x ")[1] || cell.dataset.title; // Extract boss name from data-title
            const trimmedBossName = bossName.trim(); // Trim any extra spaces

            // Check for exact matches in the bossStyles object
            let appliedStyle = "";
            for (const [key, style] of Object.entries(bossStyles)) {
                if (useFullTitleForStyling) {
                    // Check for exact match when using full title
                    if (trimmedBossName === key) {
                        appliedStyle = style; // Set the style to apply
                        break; // Exit the loop once a match is found
                    }
                } else {
                    // Check for partial matches when not using full title
                    if (trimmedBossName.includes(key)) {
                        appliedStyle = style; // Set the style to apply
                        break; // Exit the loop once a match is found
                    }
                }
            }

            cell.style.border = appliedStyle; // Apply the corresponding style
            // Remove 'qrf' class if the cell has the oldboss class and matches the boss name
            if (cell.querySelector('.oldboss') && trimmedBossName === "Quick Reaction Force") {
                cell.classList.remove('qrf'); // Remove the 'qrf' class
            }
            // Remove red border if the cell has the oldboss class
            if (cell.querySelector('.oldboss')) {
                cell.style.border = ""; // Clear the border
            }
        });
    };


    // Function to remove 'qrf' class from specific boss cells
    // const removeQRFClass = () => {
    //     const mapCells = document.querySelectorAll('#boss-table .coord');
    //     mapCells.forEach(cell => {
    //         const bossName = cell.dataset.title.split(" x ")[1] || cell.dataset.title; // Extract boss name from data-title
    //         const trimmedBossName = bossName.trim(); // Trim any extra spaces
    //         // Remove 'qrf' class if the cell has the oldboss class and matches the boss name
    //         if (cell.querySelector('.oldboss') && trimmedBossName === "Quick Reaction Force") {
    //             cell.classList.remove('qrf'); // Remove the 'qrf' class
    //         }
    //     });
    // };

    // Function to handle both identifier replacement and styling
    const handleBossUpdates = () => {
        replaceIdentifiers(); // Call to replace identifiers
        applyBossStyling(); // Call to apply boss styling
        // removeQRFClass(); // Call to remove 'qrf' to remove green marking if QRF ended
    };

    // Initial replacement and styling on page load
    handleBossUpdates();

    // Mutation observer to handle dynamic updates
    const observer = new MutationObserver(handleBossUpdates);
    observer.observe(document.body, { childList: true, subtree: true });
})();