FlatMMO UI Tweaks

Adds a modern skills panel, custom themes, virtual levels, and a portrait mode layout.

Versión del día 8/9/2025. Echa un vistazo a la versión más reciente.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         FlatMMO UI Tweaks
// @namespace    com.pizza1337.flatmmo.uitweaks
// @version      1.1.5
// @description  Adds a modern skills panel, custom themes, virtual levels, and a portrait mode layout.
// @author       Pizza1337
// @match        *://flatmmo.com/play.php*
// @grant        none
// @require      https://update.greasyfork.org/scripts/544062/FlatMMOPlus.js
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    class UITweaksPlugin extends FlatMMOPlusPlugin {
        constructor() {
            super("ui-tweaks", {
                about: {
                    name: GM_info.script.name,
                    version: GM_info.script.version,
                    author: GM_info.script.author,
                    description: GM_info.script.description
                },
                config: [
                    {
                        id: "showSkillsPanel",
                        label: "Modern Skills Panel",
                        type: "boolean",
                        default: true
                    },
                    {
                        id: "showVirtualLevels",
                        label: "Show Virtual Levels (100+)",
                        type: "boolean",
                        default: false
                    },
                    {
                        id: "portraitMode",
                        label: "Portrait Mode (F5 Required)",
                        type: "boolean",
                        default: false
                    },
                    {
                        id: "theme",
                        label: "UI Theme",
                        type: "select",
                        options: [
                            { value: "default", label: "Default" },
                            { value: "dark", label: "Dark Mode" },
                            { value: "pumpkin", label: "Pumpkin Spice" },
                            { value: "sea", label: "Deep Sea" },
                            { value: "mystic", label: "Mystic Vale" },
                            { value: "omboko", label: "Omboko" }
                        ],
                        default: "default"
                    }
                ]
            });

            this.skillUI = { elements: {}, total: {} };
            this.isGridCreated = false;
            this.initialized = false;

            // Hardcoded XP table for levels 1-200
            this.xpTable = {
                1: 0, 2: 9, 3: 28, 4: 66, 5: 131, 6: 228, 7: 368, 8: 557, 9: 805, 10: 1123,
                11: 1519, 12: 2006, 13: 2596, 14: 3301, 15: 4136, 16: 5114, 17: 6251, 18: 7565, 19: 9073, 20: 10795,
                21: 12750, 22: 14961, 23: 17450, 24: 20243, 25: 23365, 26: 26846, 27: 30714, 28: 35001, 29: 39742, 30: 44971,
                31: 50728, 32: 57053, 33: 63988, 34: 71580, 35: 79876, 36: 88929, 37: 98793, 38: 109525, 39: 121186, 40: 133842,
                41: 147562, 42: 162417, 43: 178485, 44: 195848, 45: 214591, 46: 234806, 47: 256589, 48: 280041, 49: 305272, 50: 332394,
                51: 361528, 52: 392800, 53: 426345, 54: 462304, 55: 500828, 56: 542072, 57: 586205, 58: 633401, 59: 683845, 60: 737733,
                61: 795271, 62: 856676, 63: 922177, 64: 992014, 65: 1066442, 66: 1145728, 67: 1230154, 68: 1320018, 69: 1415632, 70: 1517325,
                71: 1625444, 72: 1740353, 73: 1862437, 74: 1992099, 75: 2129766, 76: 2275884, 77: 2430923, 78: 2595379, 79: 2769772, 80: 2954650,
                81: 3150588, 82: 3358190, 83: 3578094, 84: 3810967, 85: 4057513, 86: 4318468, 87: 4594610, 88: 4886754, 89: 5195754, 90: 5522512,
                91: 5867972, 92: 6233126, 93: 6619016, 94: 7026737, 95: 7457436, 96: 7912321, 97: 8392658, 98: 8899775, 99: 9435068, 100: 10000000,
                // Virtual levels 101-200
                101: 10596109, 102: 11225007, 103: 11888385, 104: 12588018, 105: 13325769,
                106: 14103591, 107: 14923534, 108: 15787745, 109: 16698480, 110: 17658103,
                111: 18669092, 112: 19734048, 113: 20855695, 114: 22036891, 115: 23280633,
                116: 24590061, 117: 25968468, 118: 27419303, 119: 28946185, 120: 30552904,
                121: 32243432, 122: 34021934, 123: 35892771, 124: 37860514, 125: 39929953,
                126: 42106106, 127: 44394230, 128: 46799832, 129: 49328680, 130: 51986818,
                131: 54780575, 132: 57716580, 133: 60801776, 134: 64043434, 135: 67449169,
                136: 71026953, 137: 74785137, 138: 78732462, 139: 82878082, 140: 87231579,
                141: 91802988, 142: 96602810, 143: 101642042, 144: 106932193, 145: 112485310,
                146: 118314005, 147: 124431474, 148: 130851534, 149: 137588640, 150: 144657922,
                151: 152075215, 152: 159857086, 153: 168020874, 154: 176584719, 155: 185567605,
                156: 194989391, 157: 204870858, 158: 215233744, 159: 226100794, 160: 237495801,
                161: 249443654, 162: 261970390, 163: 275103244, 164: 288870704, 165: 303302568,
                166: 318430001, 167: 334285599, 168: 350903453, 169: 368319218, 170: 386570179,
                171: 405695328, 172: 425735441, 173: 446733159, 174: 468733066, 175: 491781785,
                176: 515928063, 177: 541222870, 178: 567719497, 179: 595473660, 180: 624543611,
                181: 654990250, 182: 686877242, 183: 720271145, 184: 755241535, 185: 791861146,
                186: 830206006, 187: 870355588, 188: 912392962, 189: 956404955, 190: 1002482324,
                191: 1050719924, 192: 1101216895, 193: 1154076853, 194: 1209408086, 195: 1267323769,
                196: 1327942175, 197: 1391386904, 198: 1457787122, 199: 1527277805, 200: 1600000000
            };

            this.injectStyles();
        }

        onLogin() {
            if (this.initialized) return;
            this.initialized = true;

            this.applyTheme(this.getConfig('theme'));

            if (this.getConfig('portraitMode')) {
                const layoutObserver = new MutationObserver((mutations, observer) => {
                    if (document.querySelector('#game table canvas')) {
                        this.moveUiBelowCanvas();
                        observer.disconnect();
                    }
                });
                layoutObserver.observe(document.body, { childList: true, subtree: true });
            }
        }

        onPanelChanged(panelBefore, panelAfter) {
            if (panelAfter === 'skills') {
                this.createSkillsGrid();
            }
        }

        onConfigsChanged() {
            this.applyTheme(this.getConfig('theme'));

            if (this.getConfig('showSkillsPanel')) {
                if (document.querySelector("#ui-panel-skills")?.style.display !== "none") {
                    this.createSkillsGrid();
                }
            } else {
                this.destroySkillsGrid();
            }

            // Update skills display when virtual levels setting changes
            if (this.isGridCreated) {
                Object.keys(this.skillUI.elements).forEach(skillName => this.updateSkillCell(skillName));
            }
        }

        injectStyles() {
             const styles = `
                /* --- General UI Fixes --- */
                .settings-ui td:first-child {
                    padding-left: 10px;
                }
                .tm-settings-row td {
                    padding-top: 5px;
                    padding-bottom: 5px;
                }

                /* --- Virtual Level Display --- */
                .virtual-level-display {
                    color: #9eb883;
                    margin-left: 4px;
                }
                body.theme-dark .virtual-level-display { color: #6fbf73; }
                body.theme-pumpkin .virtual-level-display { color: #f39c12; }
                body.theme-sea .virtual-level-display { color: #01b4e4; }
                body.theme-mystic .virtual-level-display { color: #b2fab4; }
                body.theme-omboko .virtual-level-display { color: #7a9560; }

                /* --- Shared readability rules for dark themes --- */
                body.theme-dark a, body.theme-pumpkin a, body.theme-sea a, body.theme-omboko a, body.theme-mystic a { color: #8ab4f8 !important; }
                body.theme-dark a:hover, body.theme-pumpkin a:hover, body.theme-sea a:hover, body.theme-omboko a:hover, body.theme-mystic a:hover { color: #c3dafa !important; }

                /* --- Dark Mode Theme --- */
                body.theme-dark .ui-panel, body.theme-dark .modal-content,
                body.theme-dark .ach-sub-menu-btn-td, body.theme-dark .donor-shop-entry, body.theme-dark .ui-donor-chat-tags-info,
                body.theme-dark .npc-chat-options-modal-title, body.theme-dark .npc-chat-options-modal-options div {
                    background-color: #2c2f33; color: #f1f1f1; box-shadow: 1px 1px 5px #121212;
                }
                body.theme-dark table.settings-ui tr:nth-child(odd), body.theme-dark .quests-ui tr:nth-child(odd),
                body.theme-dark .monster-log-ui-table tr:nth-child(odd) { background-color: #3c4045; }
                body.theme-dark table.settings-ui tr:nth-child(even), body.theme-dark .quests-ui tr:nth-child(even),
                body.theme-dark .monster-log-ui-table tr:nth-child(even) { background-color: #32353b; }
                body.theme-dark .achievements-ui tr { background-color: #323b33; border-color: #455a46; }
                body.theme-dark .total-level-div { background-color: #23272a !important; }
                body.theme-dark .hint, body.theme-dark .color-grey, body.theme-dark .modal-content h3[style*="color: grey"] { color: #b0b0b0 !important; }
                body.theme-dark .right-click-item-modal-table { background-color: #32353b; }
                body.theme-dark .right-click-item-modal-table span[style*="color:grey"] { color: #b0b0b0 !important; }
                body.theme-dark .equipement-stats-ui-table, body.theme-dark .right-click-item-modal-table-stats,
                body.theme-dark .monster-log-modal-drops, body.theme-dark .npc-chat-message-modal-message,
                body.theme-dark .player-sell-booth-entry td, body.theme-dark .player-lookup-modal-quests,
                body.theme-dark .player-lookup-modal-achievements { background-color: rgba(0, 0, 0, 0.2); }
                body.theme-dark div[style*="color:green"], body.theme-dark .color-green { color: #6fbf73 !important; }
                body.theme-dark .color-red { color: #ff8a80 !important; }
                body.theme-dark .ach-sub-menu-btn-td:hover, body.theme-dark .npc-chat-options-modal-options div:hover { background-color: #4a4e53; }
                body.theme-dark .hover-continue-npc-chat-message-modal:hover { color: #ff8a80 !important; }
                body.theme-dark button { background-color: #4a4e53; color: #f1f1f1; border: 1px solid #6a6e73; }
                body.theme-dark button:hover { background-color: #5a5e63; }
                body.theme-dark button:disabled { background-color: #2a2e33; color: #6a6e73; border-color: #4a4e53; }
                body.theme-dark .player-lookup-modal-table-skills td { background-color: #3c4045 !important; }
                body.theme-dark .player-lookup-modal-quest-entry { background-color: #32353b; }
                body.theme-dark .hunter-shop-modal-table th { background-color: #23272a; }
                body.theme-dark .hunter-shop-modal-table, body.theme-dark .hunter-shop-modal-table tr { background-color: #3c4045 !important; }
                body.theme-dark .trade-offer-section { background-color: #23272a; }
                body.theme-dark .trade-accepted-msg { background-color: #2e4b30; border-color: #6fbf73; }
                body.theme-dark #trade-inventory-items div.item[style*="background-color"] { background-color: #23272a !important; }
                body.theme-dark .worship-timers-table th { background-color: #23272a; }
                body.theme-dark .worship-timers-table tr[style*="#CEFFB5"] { background-color: #2e4b30 !important; } /* Green */
                body.theme-dark .worship-timers-table tr[style*="#f6ffb5ff"] { background-color: #4b4b30 !important; } /* Yellow */
                body.theme-dark .worship-timers-table tr[style*="#FFCAC2"] { background-color: #4b3030 !important; } /* Red */
                body.theme-dark button[style*="#cee3c9"] { background-color: #2e4b30 !important; color: #f1f1f1 !important; } /* Accept Button */
                body.theme-dark button[style*="#e3d3c9"] { background-color: #4b3030 !important; color: #f1f1f1 !important; } /* Decline Button */
                body.theme-dark button[style*="#cee3c9"]:hover { background-color: #3f6943 !important; }
                body.theme-dark button[style*="#e3d3c9"]:hover { background-color: #694343 !important; }

                /* --- Pumpkin Spice Theme --- */
                body.theme-pumpkin .ui-panel, body.theme-pumpkin .modal-content,
                body.theme-pumpkin .ach-sub-menu-btn-td, body.theme-pumpkin .donor-shop-entry, body.theme-pumpkin .ui-donor-chat-tags-info,
                body.theme-pumpkin .npc-chat-options-modal-title, body.theme-pumpkin .npc-chat-options-modal-options div {
                    background-color: #2a2421; color: #f5e4d9; box-shadow: 1px 1px 5px #1a1411; border: 1px solid #4a3421;
                }
                body.theme-pumpkin .ui-panel-title { color: #e67e22; }
                body.theme-pumpkin table.settings-ui tr:nth-child(odd), body.theme-pumpkin .quests-ui tr:nth-child(odd),
                body.theme-pumpkin .monster-log-ui-table tr:nth-child(odd) { background-color: #3b312c; }
                body.theme-pumpkin table.settings-ui tr:nth-child(even), body.theme-pumpkin .quests-ui tr:nth-child(even),
                body.theme-pumpkin .monster-log-ui-table tr:nth-child(even) { background-color: #312925; }
                body.theme-pumpkin .achievements-ui tr { background-color: #3b2e25; border-color: #5a4431; }
                body.theme-pumpkin .skill-cell-bar-fill { background-color: #d35400 !important; }
                body.theme-pumpkin .total-level-div { background-color: #1c1815 !important; }
                body.theme-pumpkin .hint, body.theme-pumpkin .color-grey, body.theme-pumpkin .modal-content h3[style*="color: grey"] { color: #c9bca2 !important; }
                body.theme-pumpkin .right-click-item-modal-table { background-color: #312925; }
                body.theme-pumpkin .right-click-item-modal-table span[style*="color:grey"] { color: #c9bca2 !important; }
                body.theme-pumpkin .equipement-stats-ui-table, body.theme-pumpkin .right-click-item-modal-table-stats,
                body.theme-pumpkin .monster-log-modal-drops, body.theme-pumpkin .npc-chat-message-modal-message,
                body.theme-pumpkin .player-sell-booth-entry td, body.theme-pumpkin .player-lookup-modal-quests,
                body.theme-pumpkin .player-lookup-modal-achievements { background-color: rgba(40, 26, 13, 0.2); }
                body.theme-pumpkin div[style*="color:green"], body.theme-pumpkin .color-green { color: #e67e22 !important; }
                body.theme-pumpkin .color-red { color: #e84c3d !important; }
                body.theme-pumpkin a { color: #f39c12 !important; }
                body.theme-pumpkin .ach-sub-menu-btn-td:hover, body.theme-pumpkin .npc-chat-options-modal-options div:hover { background-color: #4a3c35; }
                body.theme-pumpkin .hover-continue-npc-chat-message-modal:hover { color: #ffab70 !important; }
                body.theme-pumpkin button { background-color: #4a3c35; color: #f5e4d9; border: 1px solid #6a5c55; }
                body.theme-pumpkin button:hover { background-color: #5a4c45; }
                body.theme-pumpkin button:disabled { background-color: #2a2421; color: #6a5c55; border-color: #4a3421; }
                body.theme-pumpkin .player-lookup-modal-table-skills td { background-color: #3b312c !important; }
                body.theme-pumpkin .player-lookup-modal-quest-entry { background-color: #312925; }
                body.theme-pumpkin .hunter-shop-modal-table th { background-color: #1c1815; }
                body.theme-pumpkin .hunter-shop-modal-table, body.theme-pumpkin .hunter-shop-modal-table tr { background-color: #3b312c !important; }
                body.theme-pumpkin .trade-offer-section { background-color: #1c1815; }
                body.theme-pumpkin .trade-accepted-msg { background-color: #5a4431; border-color: #e67e22; }
                body.theme-pumpkin #trade-inventory-items div.item[style*="background-color"] { background-color: #1c1815 !important; }
                body.theme-pumpkin .worship-timers-table th { background-color: #1c1815; }
                body.theme-pumpkin .worship-timers-table tr[style*="#CEFFB5"] { background-color: #4a3c35 !important; } /* Green */
                body.theme-pumpkin .worship-timers-table tr[style*="#f6ffb5ff"] { background-color: #4a3421 !important; } /* Yellow */
                body.theme-pumpkin .worship-timers-table tr[style*="#FFCAC2"] { background-color: #4a2a21 !important; } /* Red */
                body.theme-pumpkin button[style*="#cee3c9"] { background-color: #662500 !important; color: #f5e4d9 !important; } /* Accept Button */
                body.theme-pumpkin button[style*="#e3d3c9"] { background-color: #4a2a21 !important; color: #f5e4d9 !important; } /* Decline Button */
                body.theme-pumpkin button[style*="#cee3c9"]:hover { background-color: #853100 !important; }
                body.theme-pumpkin button[style*="#e3d3c9"]:hover { background-color: #693a2a !important; }


                /* --- Deep Sea Theme --- */
                body.theme-sea .ui-panel, body.theme-sea .modal-content,
                body.theme-sea .ach-sub-menu-btn-td, body.theme-sea .donor-shop-entry, body.theme-sea .ui-donor-chat-tags-info,
                body.theme-sea .npc-chat-options-modal-title, body.theme-sea .npc-chat-options-modal-options div {
                    background-color: #0d253f; color: #e1f5fe; box-shadow: 1px 1px 5px #051525; border: 1px solid #01b4e4;
                }
                body.theme-sea .ui-panel-title { color: #90cea1; }
                body.theme-sea table.settings-ui tr:nth-child(odd), body.theme-sea .quests-ui tr:nth-child(odd),
                body.theme-sea .monster-log-ui-table tr:nth-child(odd) { background-color: #0a1d31; }
                body.theme-sea table.settings-ui tr:nth-child(even), body.theme-sea .quests-ui tr:nth-child(even),
                body.theme-sea .monster-log-ui-table tr:nth-child(even) { background-color: #102a45; }
                body.theme-sea .achievements-ui tr { background-color: #103a45; border-color: #1a5a65; }
                body.theme-sea .skill-cell-bar-fill { background-color: #01b4e4 !important; }
                body.theme-sea .total-level-div { background-color: #071726 !important; }
                body.theme-sea .hint, body.theme-sea .color-grey, body.theme-sea .modal-content h3[style*="color: grey"] { color: #a4c8d1 !important; }
                body.theme-sea .right-click-item-modal-table { background-color: #102a45; }
                body.theme-sea .right-click-item-modal-table span[style*="color:grey"] { color: #a4c8d1 !important; }
                body.theme-sea .equipement-stats-ui-table, body.theme-sea .right-click-item-modal-table-stats,
                body.theme-sea .monster-log-modal-drops, body.theme-sea .npc-chat-message-modal-message,
                body.theme-sea .player-sell-booth-entry td, body.theme-sea .player-lookup-modal-quests,
                body.theme-sea .player-lookup-modal-achievements { background-color: rgba(1, 180, 228, 0.1); }
                body.theme-sea div[style*="color:green"], body.theme-sea .color-green { color: #90cea1 !important; }
                body.theme-sea .color-red { color: #5eb5b9 !important; }
                body.theme-sea a { color: #01b4e4 !important; }
                body.theme-sea .ach-sub-menu-btn-td:hover, body.theme-sea .npc-chat-options-modal-options div:hover { background-color: #133a5f; }
                body.theme-sea .hover-continue-npc-chat-message-modal:hover { color: #66d9ff !important; }
                body.theme-sea button { background-color: #133a5f; color: #e1f5fe; border: 1px solid #235a7f; }
                body.theme-sea button:hover { background-color: #234a6f; }
                body.theme-sea button:disabled { background-color: #0d253f; color: #235a7f; border-color: #133a5f; }
                body.theme-sea .player-lookup-modal-table-skills td { background-color: #0a1d31 !important; }
                body.theme-sea .player-lookup-modal-quest-entry { background-color: #102a45; }
                body.theme-sea .hunter-shop-modal-table th { background-color: #071726; }
                body.theme-sea .hunter-shop-modal-table, body.theme-sea .hunter-shop-modal-table tr { background-color: #0a1d31 !important; }
                body.theme-sea .trade-offer-section { background-color: #071726; }
                body.theme-sea .trade-accepted-msg { background-color: #103a45; border-color: #90cea1; }
                body.theme-sea #trade-inventory-items div.item[style*="background-color"] { background-color: #071726 !important; }
                body.theme-sea .worship-timers-table th { background-color: #071726; }
                body.theme-sea .worship-timers-table tr[style*="#CEFFB5"] { background-color: #104a35 !important; } /* Green */
                body.theme-sea .worship-timers-table tr[style*="#f6ffb5ff"] { background-color: #133a5f !important; } /* Yellow */
                body.theme-sea .worship-timers-table tr[style*="#FFCAC2"] { background-color: #3f1d3f !important; } /* Red */
                body.theme-sea button[style*="#cee3c9"] { background-color: #104a35 !important; color: #e1f5fe !important; } /* Accept Button */
                body.theme-sea button[style*="#e3d3c9"] { background-color: #3f1d3f !important; color: #e1f5fe !important; } /* Decline Button */
                body.theme-sea button[style*="#cee3c9"]:hover { background-color: #1a694f !important; }
                body.theme-sea button[style*="#e3d3c9"]:hover { background-color: #5f2a5f !important; }


                /* --- Mystic Vale Theme (Darker) --- */
                body.theme-mystic .ui-panel, body.theme-mystic .modal-content,
                body.theme-mystic .ach-sub-menu-btn-td, body.theme-mystic .donor-shop-entry, body.theme-mystic .ui-donor-chat-tags-info,
                body.theme-mystic .npc-chat-options-modal-title, body.theme-mystic .npc-chat-options-modal-options div {
                    background-color: #6A6E94; color: #f0f0f0; box-shadow: 1px 1px 5px #4a4c6a;
                }
                body.theme-mystic .ui-panel-title { color: #d1d3e0; }
                body.theme-mystic table.settings-ui tr:nth-child(odd), body.theme-mystic .quests-ui tr:nth-child(odd),
                body.theme-mystic .monster-log-ui-table tr:nth-child(odd) { background-color: #7A7EA1; }
                body.theme-mystic table.settings-ui tr:nth-child(even), body.theme-mystic .quests-ui tr:nth-child(even),
                body.theme-mystic .monster-log-ui-table tr:nth-child(even) { background-color: #868AAD; }
                body.theme-mystic .achievements-ui tr { background-color: #7a8aa1; border-color: #9598b9; }
                body.theme-mystic .skill-cell-bar-fill { background-color: #9598B9 !important; }
                body.theme-mystic .skill-cell { background-color: #5f6284 !important; }
                body.theme-mystic .total-level-div { background-color: #5f6284 !important; color: #f0f0f0 !important; }
                body.theme-mystic .hint, body.theme-mystic .color-grey, body.theme-mystic .modal-content h3[style*="color: grey"] { color: #d1d3e0 !important; }
                body.theme-mystic .right-click-item-modal-table { background-color: #868AAD; }
                body.theme-mystic .right-click-item-modal-table span[style*="color:grey"] { color: #d1d3e0 !important; }
                body.theme-mystic .equipement-stats-ui-table, body.theme-mystic .right-click-item-modal-table-stats,
                body.theme-mystic .monster-log-modal-drops, body.theme-mystic .npc-chat-message-modal-message,
                body.theme-mystic .player-sell-booth-entry td, body.theme-mystic .player-lookup-modal-quests,
                body.theme-mystic .player-lookup-modal-achievements { background-color: rgba(60, 62, 84, 0.2); }
                body.theme-mystic div[style*="color:green"], body.theme-mystic .green-hover:hover, body.theme-mystic .color-green { color: #b2fab4 !important; }
                body.theme-mystic .color-red { color: #f5c0c0 !important; }
                body.theme-mystic .color-blue { color: #a6cfff !important; }
                body.theme-mystic a { color: #d1d3e0 !important; }
                body.theme-mystic .ach-sub-menu-btn-td:hover, body.theme-mystic .npc-chat-options-modal-options div:hover { background-color: #7A7EA1; }
                body.theme-mystic .hover-continue-npc-chat-message-modal:hover { color: #f5c0c0 !important; }
                body.theme-mystic button { background-color: #7A7EA1; color: #f0f0f0; border: 1px solid #9598B9; }
                body.theme-mystic button:hover { background-color: #868AAD; }
                body.theme-mystic button:disabled { background-color: #6A6E94; color: #9598B9; border-color: #5f6284; }
                body.theme-mystic .player-lookup-modal-table-skills td { background-color: #7A7EA1 !important; }
                body.theme-mystic .player-lookup-modal-quest-entry { background-color: #868AAD; }
                body.theme-mystic .hunter-shop-modal-table th { background-color: #5f6284; }
                body.theme-mystic .hunter-shop-modal-table, body.theme-mystic .hunter-shop-modal-table tr { background-color: #7A7EA1 !important; }
                body.theme-mystic .trade-offer-section { background-color: #5f6284; }
                body.theme-mystic .trade-accepted-msg { background-color: #7a8aa1; border-color: #b2fab4; }
                body.theme-mystic #trade-inventory-items div.item[style*="background-color"] { background-color: #5f6284 !important; }
                body.theme-mystic .worship-timers-table th { background-color: #5f6284; }
                body.theme-mystic .worship-timers-table tr[style*="#CEFFB5"] { background-color: #3e5e40 !important; } /* Green */
                body.theme-mystic .worship-timers-table tr[style*="#f6ffb5ff"] { background-color: #5f6284 !important; } /* Yellow */
                body.theme-mystic .worship-timers-table tr[style*="#FFCAC2"] { background-color: #4a4c6a !important; } /* Red */
                body.theme-mystic button[style*="#cee3c9"] { background-color: #3e5e40 !important; color: #f0f0f0 !important; } /* Accept Button */
                body.theme-mystic button[style*="#e3d3c9"] { background-color: #4a4c6a !important; color: #f0f0f0 !important; } /* Decline Button */
                body.theme-mystic button[style*="#cee3c9"]:hover { background-color: #5e8c61 !important; }
                body.theme-mystic button[style*="#e3d3c9"]:hover { background-color: #696d9a !important; }


                /* --- Omboko Theme --- */
                body.theme-omboko .ui-panel, body.theme-omboko .modal-content,
                body.theme-omboko .ach-sub-menu-btn-td, body.theme-omboko .donor-shop-entry, body.theme-omboko .ui-donor-chat-tags-info,
                body.theme-omboko .npc-chat-options-modal-title, body.theme-omboko .npc-chat-options-modal-options div {
                    background-color: #1D2319; color: #d1dcc6; box-shadow: 1px 1px 5px #000; border: 1px solid #4B682E;
                }
                body.theme-omboko .ui-panel-title { color: #9eb883; }
                body.theme-omboko table.settings-ui tr:nth-child(odd), body.theme-omboko .quests-ui tr:nth-child(odd),
                body.theme-omboko .monster-log-ui-table tr:nth-child(odd) { background-color: #25311B; }
                body.theme-omboko table.settings-ui tr:nth-child(even), body.theme-omboko .quests-ui tr:nth-child(even),
                body.theme-omboko .monster-log-ui-table tr:nth-child(even) { background-color: #30411F; }
                body.theme-omboko .achievements-ui tr { background-color: #30411F; border-color: #4B682E; }
                body.theme-omboko .skill-cell-bar-fill { background-color: #4B682E !important; }
                body.theme-omboko .skill-cell { background-color: #30411F !important; }
                body.theme-omboko .total-level-div { background-color: #11150f !important; }
                body.theme-omboko .hint, body.theme-omboko .color-grey, body.theme-omboko .modal-content h3[style*="color: grey"] { color: #8f9984 !important; }
                body.theme-omboko .right-click-item-modal-table { background-color: #30411F; }
                body.theme-omboko .right-click-item-modal-table span[style*="color:grey"] { color: #8f9984 !important; }
                body.theme-omboko .equipement-stats-ui-table, body.theme-omboko .right-click-item-modal-table-stats,
                body.theme-omboko .monster-log-modal-drops, body.theme-omboko .npc-chat-message-modal-message,
                body.theme-omboko .player-sell-booth-entry td, body.theme-omboko .player-lookup-modal-quests,
                body.theme-omboko .player-lookup-modal-achievements { background-color: rgba(75, 104, 46, 0.1); }
                body.theme-omboko div[style*="color:green"], body.theme-omboko .color-green { color: #9eb883 !important; }
                body.theme-omboko .color-red { color: #e57373 !important; }
                body.theme-omboko a { color: #9eb883 !important; }
                body.theme-omboko .ach-sub-menu-btn-td:hover, body.theme-omboko .npc-chat-options-modal-options div:hover { background-color: #3D5426; }
                body.theme-omboko .hover-continue-npc-chat-message-modal:hover { color: #c4d6b1 !important; }
                body.theme-omboko button { background-color: #3D5426; color: #d1dcc6; border: 1px solid #4B682E; }
                body.theme-omboko button:hover { background-color: #4B682E; }
                body.theme-omboko button:disabled { background-color: #1D2319; color: #4B682E; border-color: #25311B; }
                body.theme-omboko .player-lookup-modal-table-skills td { background-color: #25311B !important; }
                body.theme-omboko .player-lookup-modal-quest-entry { background-color: #30411F; }
                body.theme-omboko .hunter-shop-modal-table th { background-color: #11150f; }
                body.theme-omboko .hunter-shop-modal-table, body.theme-omboko .hunter-shop-modal-table tr { background-color: #25311B !important; }
                body.theme-omboko .trade-offer-section { background-color: #11150f; }
                body.theme-omboko .trade-accepted-msg { background-color: #30411F; border-color: #9eb883; }
                body.theme-omboko #trade-inventory-items div.item[style*="background-color"] { background-color: #11150f !important; }
                body.theme-omboko .worship-timers-table th { background-color: #11150f; }
                body.theme-omboko .worship-timers-table tr[style*="#CEFFB5"] { background-color: #3D5426 !important; } /* Green */
                body.theme-omboko .worship-timers-table tr[style*="#f6ffb5ff"] { background-color: #4B682E !important; } /* Yellow */
                body.theme-omboko .worship-timers-table tr[style*="#FFCAC2"] { background-color: #30411F !important; } /* Red */
                body.theme-omboko button[style*="#cee3c9"] { background-color: #3D5426 !important; color: #d1dcc6 !important; } /* Accept Button */
                body.theme-omboko button[style*="#e3d3c9"] { background-color: #30411F !important; color: #d1dcc6 !important; } /* Decline Button */
                body.theme-omboko button[style*="#cee3c9"]:hover { background-color: #4B682E !important; }
                body.theme-omboko button[style*="#e3d3c9"]:hover { background-color: #4B682E !important; }
            `;
            const styleSheet = document.createElement("style");
            styleSheet.type = "text/css";
            styleSheet.innerText = styles;
            document.head.appendChild(styleSheet);
        }

        applyTheme(themeName) {
            document.body.className = document.body.className.replace(/theme-\w+/g, '');
            if (themeName && themeName !== 'default') {
                document.body.classList.add(`theme-${themeName}`);
            }
        }

        getLevelFromXP(xp) {
            let level = 1;
            for (let i = 200; i >= 1; i--) {
                if (xp >= this.xpTable[i]) {
                    level = i;
                    break;
                }
            }
            return level;
        }

        recalculateAndDisplayTotal() {
            if (!this.skillUI.total.textSpan || !this.skillUI.total.tooltip) return;

            let totalLevel = 0;
            let virtualTotalLevel = 0;
            let totalXp = 0;

            for (const skillName in this.skillUI.elements) {
                const element = this.skillUI.elements[skillName];
                totalXp += element.xp || 0;

                // Calculate both normal and virtual levels
                const normalLevel = Math.min(element.level || 0, 100);
                totalLevel += normalLevel;

                if (this.getConfig('showVirtualLevels')) {
                    virtualTotalLevel += element.level || 0;
                }
            }

            if (this.getConfig('showVirtualLevels') && virtualTotalLevel > totalLevel) {
                // Show regular level first, then virtual level in parentheses with different color
                this.skillUI.total.textSpan.innerHTML = `TOTAL ${totalLevel} <span class="virtual-level-display">(${virtualTotalLevel})</span>`;
            } else {
                this.skillUI.total.textSpan.innerText = `TOTAL ${totalLevel}`;
            }

            this.skillUI.total.tooltip.textContent = `${totalXp.toLocaleString('en-US')} XP`;
        }

        updateSkillCell(skillName) {
            const component = this.skillUI.elements[skillName];
            if (!component || !component.originalLevelEl) return;

            const levelText = component.originalLevelEl.innerText;
            const xpTooltipText = component.originalXpEl.innerHTML;
            const xpDataText = xpTooltipText.split('<br>').pop().trim();

            let displayLevel = parseInt(levelText);
            const [currentStr, maxStr] = xpDataText.replace(" XP", "").split("/");
            const currentXP = parseInt(currentStr.replace(/,/g, "")) || 0;

            // Calculate virtual level if enabled
            if (this.getConfig('showVirtualLevels')) {
                displayLevel = this.getLevelFromXP(currentXP);
            }

            component.level = displayLevel;
            component.xp = currentXP;
            component.label.innerText = displayLevel;

            // Handle progress bar and tooltip
            if (displayLevel >= 100 && !this.getConfig('showVirtualLevels')) {
                // At level 100 without virtual levels
                component.barFill.style.width = '100%';
                component.tooltip.textContent = `${currentXP.toLocaleString('en-US')} XP`;
            } else {
                // Calculate progress for current level
                const currentLevelXP = this.xpTable[displayLevel] || 0;
                const nextLevelXP = this.xpTable[displayLevel + 1] || currentXP;

                const xpGainedThisLevel = Math.max(0, currentXP - currentLevelXP);
                const xpNeededForThisLevel = nextLevelXP - currentLevelXP;

                let progress = 0;
                if (xpNeededForThisLevel > 0) {
                    progress = Math.min((xpGainedThisLevel / xpNeededForThisLevel) * 100, 100);
                }

                component.barFill.style.width = `${progress}%`;

                if (displayLevel >= 200) {
                    // Max virtual level
                    component.tooltip.textContent = `${currentXP.toLocaleString('en-US')} XP`;
                } else {
                    component.tooltip.textContent = `${currentXP.toLocaleString('en-US')} / ${nextLevelXP.toLocaleString('en-US')} XP`;
                }
            }

            this.recalculateAndDisplayTotal();
        }

        moveUiBelowCanvas() {
            const table = document.querySelector('#game table');
            if (!table) return;

            const canvas = table.querySelector('canvas');
            const canvasTd = canvas?.closest('td');
            const uiTd = table.querySelector('.td-ui');

            if (canvas && canvasTd && uiTd) {
                const newTbody = document.createElement('tbody');
                const canvasRow = document.createElement('tr');
                const uiRow = document.createElement('tr');

                canvasRow.appendChild(canvasTd);
                uiRow.appendChild(uiTd);
                newTbody.append(canvasRow, uiRow);
                table.innerHTML = '';
                table.appendChild(newTbody);

                uiTd.style.cssText = 'padding: 0; margin: 0; overflow: hidden; position: relative;';
                const uiWrapper = document.createElement('div');
                uiWrapper.style.transformOrigin = 'top left';
                uiWrapper.style.display = 'inline-block';

                while (uiTd.firstChild) {
                    uiWrapper.appendChild(uiTd.firstChild);
                }
                uiTd.appendChild(uiWrapper);

                const applyZoomAndWidthFix = () => {
                    const canvasWidth = canvas.offsetWidth;
                    const ratio = window.devicePixelRatio || 1;
                    const scale = 1 / ratio;

                    uiWrapper.style.transform = `scale(${scale})`;
                    uiWrapper.style.width = `${canvasWidth * ratio}px`;

                    requestAnimationFrame(() => {
                        const realHeight = uiWrapper.getBoundingClientRect().height;
                        uiTd.style.height = `${realHeight}px`;
                    });
                };

                applyZoomAndWidthFix();
                window.addEventListener('resize', applyZoomAndWidthFix);
            }
        }

        createSkillsGrid() {
            if (this.isGridCreated || !this.getConfig('showSkillsPanel')) {
                if (this.isGridCreated && !this.getConfig('showSkillsPanel')) {
                    this.destroySkillsGrid();
                }
                return;
            }

            const skillPanel = document.querySelector("#ui-panel-skills");
            const skillContainer = skillPanel?.querySelector("center");

            if (skillPanel && skillContainer) {
                const skillDivs = Array.from(skillContainer.querySelectorAll("div.skills-ui-new"));
                if (skillDivs.length > 0) {
                    this.isGridCreated = true;

                    const grid = document.createElement("div");
                    grid.style.cssText = "display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin: 20px 0;";
                    grid.id = "tm-skills-grid";

                    skillDivs.slice(0, -1).forEach(skillDiv => {
                        const levelEl = skillDiv.querySelector("span[id$='-level']");
                        if (!levelEl) return;
                        const skillName = levelEl.id.replace('-level', '');
                        const iconEl = skillDiv.querySelector("img");
                        const xpEl = skillDiv.querySelector("span[id$='-xp']");
                        const onclickAttr = skillDiv.getAttribute("onclick");

                        const cell = document.createElement("div");
                        cell.style.cssText = `display: flex; flex-direction: column; align-items: center; justify-content: space-between; background: #222; color: #fff; padding: 10px; border-radius: 8px; cursor: pointer;`;
                        cell.classList.add('skill-cell');
                        if (onclickAttr) cell.onclick = () => eval(onclickAttr);

                        const img = document.createElement("img");
                        img.src = iconEl.src;
                        img.style.cssText = "width: 32px; height: 32px; margin-bottom: 5px;";

                        const label = document.createElement("div");
                        label.style.fontSize = '16pt';
                        label.style.fontWeight = 'bold';

                        const spacer = document.createElement("div");
                        spacer.style.flexGrow = "1";

                        const barContainer = document.createElement("div");
                        barContainer.style.cssText = `width: 100%; height: 6px; background: #444; border-radius: 4px; margin-top: 6px; overflow: hidden;`;
                        const barFill = document.createElement("div");
                        barFill.style.cssText = "height: 100%; background: #00cc66; border-radius: 4px 0 0 4px;";
                        barFill.classList.add('skill-cell-bar-fill');
                        barContainer.appendChild(barFill);

                        const tooltip = document.createElement("div");
                        let tooltipStyle = `position: fixed; padding: 6px 10px; background: #333; color: #fff; border-radius: 6px; font-size: 14px; box-shadow: 0 2px 6px rgba(0,0,0,0.4); pointer-events: none; z-index: 9999; white-space: nowrap; display: none;`;

                        if (this.getConfig('portraitMode')) {
                            const ratio = window.devicePixelRatio || 1;
                            const scale = 1 / ratio;
                            tooltipStyle += `transform: scale(${scale}); transform-origin: left top;`;
                        }
                        tooltip.style.cssText = tooltipStyle;
                        document.body.appendChild(tooltip);

                        cell.addEventListener("mousemove", e => {
                            tooltip.style.left = `${e.pageX + 15}px`;
                            tooltip.style.top = `${e.pageY + 10}px`;
                        });
                        cell.addEventListener("mouseenter", () => { tooltip.style.display = "block"; });
                        cell.addEventListener("mouseleave", () => { tooltip.style.display = "none"; });

                        cell.append(img, label, spacer, barContainer);
                        grid.appendChild(cell);

                        this.skillUI.elements[skillName] = { label, barFill, tooltip, originalLevelEl: levelEl, originalXpEl: xpEl };
                        this.updateSkillCell(skillName);
                    });

                    const totalDiv = document.createElement("div");
                    totalDiv.style.cssText = `display: flex; align-items: center; justify-content: center; text-align: center; margin-top: 10px; padding: 10px; background: #444; color: #fff; font-weight: bold; border-radius: 8px; font-size: 16pt;`;
                    totalDiv.classList.add('total-level-div');
                    totalDiv.id = "tm-total-level-div";

                    const totalIcon = document.createElement("img");
                    totalIcon.src = "images/icons/skills_large.png";
                    totalIcon.style.cssText = "width: 24px; height: 24px; margin-right: 10px;";

                    const totalTextSpan = document.createElement("span");
                    totalDiv.append(totalIcon, totalTextSpan);

                    this.skillUI.total.textSpan = totalTextSpan;

                    const totalTooltip = document.createElement("div");
                    let tooltipStyle = `position: fixed; padding: 6px 10px; background: #333; color: #fff; border-radius: 6px; font-size: 14px; box-shadow: 0 2px 6px rgba(0,0,0,0.4); pointer-events: none; z-index: 9999; white-space: nowrap; display: none;`;
                     if (this.getConfig('portraitMode')) {
                        const ratio = window.devicePixelRatio || 1;
                        const scale = 1 / ratio;
                        tooltipStyle += `transform: scale(${scale}); transform-origin: left top;`;
                    }
                    totalTooltip.style.cssText = tooltipStyle;
                    document.body.appendChild(totalTooltip);
                    this.skillUI.total.tooltip = totalTooltip;

                    totalDiv.addEventListener("mousemove", e => {
                        totalTooltip.style.left = `${e.pageX + 15}px`;
                        totalTooltip.style.top = `${e.pageY + 10}px`;
                    });
                    totalDiv.addEventListener("mouseenter", () => { totalTooltip.style.display = "block"; });
                    totalDiv.addEventListener("mouseleave", () => { totalTooltip.style.display = "none"; });

                    this.recalculateAndDisplayTotal();

                    skillContainer.style.display = "none";
                    skillPanel.append(grid, totalDiv);

                    const skillObserver = new MutationObserver(() => {
                        Object.keys(this.skillUI.elements).forEach(skillName => this.updateSkillCell(skillName));
                    });
                    skillObserver.observe(skillContainer, { childList: true, subtree: true, characterData: true });
                }
            }
        }

        destroySkillsGrid() {
            if (!this.isGridCreated) return;

            const skillPanel = document.querySelector("#ui-panel-skills");
            const skillContainer = skillPanel?.querySelector("center");
            const grid = document.getElementById('tm-skills-grid');
            const totalDiv = document.getElementById('tm-total-level-div');

            if (grid) grid.remove();
            if (totalDiv) totalDiv.remove();
            if (this.skillUI.total.tooltip) this.skillUI.total.tooltip.remove();
            if (skillContainer) skillContainer.style.display = "";

            this.isGridCreated = false;
        }
    }

    const plugin = new UITweaksPlugin();
    FlatMMOPlus.registerPlugin(plugin);

})();