EQ SETS

A quick way of changing inventories in TW and filtering notifications

// ==UserScript==
// @name         EQ SETS
// @version      2.7
// @description  A quick way of changing inventories in TW and filtering notifications
// @author       Donald Kaczyński
// @website
// @include      *.the-west.*/game.php*
// @grant        none
// @namespace https://greasyfork.org/users/755649
// ==/UserScript==

(function() {
    'use strict';
    setTimeout(function() {

    function initializeEquipmentSets() {
        // Główna funkcja zarządzająca zestawami ekwipunku
        function MenedzerZestawowWyposazenia() {
            // Klucze localStorage
            const STORAGE_KEY_VISIBLE = 'equipmentSets_isVisible';
            const STORAGE_KEY_COLUMNS = 'equipmentSets_numColumns';
            const STORAGE_KEY_HIDDEN_NAMES = 'equipmentSets_hiddenNames'; // Zmieniono z ID na nazwy
            const STORAGE_KEY_TILE_SCALE = 'equipmentSets_tileScale';
            const STORAGE_KEY_PANEL_LOCATION = 'equipmentSets_panelLocation';

            this.equipmentList = {};
            this.tilesContainer = null;
            this.settingsWindow = null; // Referencja do okna ustawień
            this.allEquipmentSets = []; // Przechowuje pełną listę zestawów po pobraniu
            this.DEFAULT_COLUMNS = 2; // Domyślna liczba kolumn

            // Nowe zmienne stanu
            this.isPanelVisible = false; // Domyślnie panel ukryty przy starcie
            this.numColumns = this.DEFAULT_COLUMNS;
            this.hiddenSetNames = new Set(); // Przechowuje NAZWY ukrytych zestawów
            this.tileScale = parseFloat(localStorage.getItem(STORAGE_KEY_TILE_SCALE)) || 1;
            this.MIN_TILE_SCALE = 1;
            this.MAX_TILE_SCALE = 2;
            this.panelLocation = localStorage.getItem(STORAGE_KEY_PANEL_LOCATION) || 'notibar'; // 'notibar' lub 'fixed'

            // Inicjalizacja całego modułu
            this.initialize = function() {
                // Wczytaj stan z localStorage
                this.isPanelVisible = localStorage.getItem(STORAGE_KEY_VISIBLE) === 'true';
                this.numColumns = parseInt(localStorage.getItem(STORAGE_KEY_COLUMNS), 10) || this.DEFAULT_COLUMNS;
                const hiddenNamesString = localStorage.getItem(STORAGE_KEY_HIDDEN_NAMES);
                if (hiddenNamesString) {
                    try {
                        const hiddenNamesArray = JSON.parse(hiddenNamesString);
                        if (Array.isArray(hiddenNamesArray)) {
                            this.hiddenSetNames = new Set(hiddenNamesArray);
                        }
                    } catch (e) {
                        console.error("[Zestawy wyposażenia] Error parsing hidden names from localStorage:", e);
                        localStorage.removeItem(STORAGE_KEY_HIDDEN_NAMES); // Usuń błędne dane
                    }
                }

                this.createMenuButton();
                // Użyj wczytanej zmiennej stanu
                if (this.isPanelVisible) {
                    setTimeout(() => {
                         if (typeof Ajax !== 'undefined' && typeof EquipManager !== 'undefined' && typeof Wear !== 'undefined' && typeof ItemManager !== 'undefined') {
                             // Sprawdźmy, czy kontener nie został już utworzony przez otwarcie ustawień
                             if (!this.tilesContainer || !this.tilesContainer.parent().length) {
                                this.buildAndShowTiles();
                             }
                         } else {
                             console.error("[Zestawy wyposażenia]: Game objects not ready for initial tile display.");
                         }
                    }, 1000);
                }
            };

            // Tworzenie przycisku w menu
            this.createMenuButton = function() {
                const buttonIcon = this.createButtonIcon();
                const bottomCap = this.createBottomCap();
                const containerDiv = this.createContainerDiv(buttonIcon, bottomCap);

                $("#ui_menubar").append(containerDiv);
            };

            // Tworzenie ikony przycisku
            this.createButtonIcon = function() {
                return $('<div></div>')
                    .attr({
                        'title': 'USTAWIENIA ZESTAWÓW',
                        'class': 'menulink'
                    })
                    .css({
                        'background': 'url("https://i.imgur.com/5LJ8hCP.png")',
                        'background-position': '0px 0px'
                    })
                    .on('mouseleave', function() {
                        $(this).css("background-position", "0px 0px");
                    })
                    .on('mouseenter', function() {
                        $(this).css("background-position", "25px 0px");
                    })
                    .click(() => this.openSettingsWindow());
            };

            // Tworzenie dolnej części przycisku
            this.createBottomCap = function() {
                return $('<div></div>').attr({
                    'class': 'menucontainer_bottom'
                });
            };

            // Tworzenie kontenera dla przycisku
            this.createContainerDiv = function(icon, cap) {
                return $('<div></div>')
                    .attr({
                        'class': 'ui_menucontainer',
                        'id': 'zestawy_init_button'
                    })
                    .append(icon)
                    .append(cap);
            };

            // Funkcja otwierająca (lub pokazująca) okno ustawień
            this.openSettingsWindow = function() {
                const createOrUpdateWindow = () => {
                    // Sprawdź, czy okno już istnieje i jest w DOM
                    if (this.settingsWindow && this.settingsWindow.parent().length) {
                        this.settingsWindow.show();
                        this.settingsWindow.css('z-index', parseInt(this.settingsWindow.css('z-index', 10501111) || 105011111) + 1);
                        // Odśwież zawartość okna ustawień (listę zestawów)
                        const optionsContainer = this.settingsWindow.find('#equipment-settings-options');
                        this.populateSettingsWindow(optionsContainer);
                        return; // Zakończ, jeśli okno już istnieje
                    }

                    // Jeśli okno nie istnieje, utwórz je
                    this.settingsWindow = $('<div></div>')
                        .attr('id', 'equipment-settings-window')
                        .css({
                            'position': 'fixed',
                            'top': '100px',
                            'left': '50%',
                            'transform': 'translateX(-50%)',
                            'width': '360px', // Slightly wider for bolder look
                            'border': '3px solid #5c3c1a', // Thicker, darker wood border
                            'background': 'url("https://westpl.innogamescdn.com/images/inventory/bag.png?1") repeat scroll 0 0 #c8a87a', // Texture on a darker tan background
                            'background-color': '#c8a87a', // Fallback
                            'border-radius': '4px', // Slightly less rounded
                            'padding': '15px',
                            'z-index': '10501111',
                            'box-shadow': '0 5px 15px rgba(0,0,0,0.5), inset 0 0 8px rgba(0,0,0,0.3)', // Stronger shadow, darker inset
                            'color': '#3d1e0a' // Darker brown text for contrast
                        });

                    const title = $('<div>USTAWIENIA ZESTAWÓW</div>').css({
                        'font-family': '"Times New Roman", Times, serif', // More classic/western font
                        'font-size': '20px', // Larger title
                        'font-weight': 'bold',
                        'margin': '-15px -15px 15px -15px', // Extend to edges
                        'padding': '10px 15px', // Padding for title bg
                        'text-align': 'center',
                        'border-bottom': '2px solid #5c3c1a', // Matching thick border
                        'background-color': 'rgba(0, 0, 0, 0.2)', // Dark overlay for title bg
                        'color': '#ffffff',
                        'text-shadow': '1px 1px 3px #000' // Stronger text shadow
                    });

                    const closeButton = $('<button>X</button>').css({
                        'position': 'absolute',
                        'top': '5px',
                        'right': '8px', // Adjust position
                        'cursor': 'pointer',
                        'border': '1px solid #5c3c1a', // Dark wood border
                        'background': '#a47b4f', // Darker button color
                        'color': '#f0e0c0', // Light text on button
                        'font-weight': 'bold',
                        'border-radius': '3px',
                        'box-shadow': 'inset 0 0 3px rgba(255,255,255,0.2)' // Subtle inset highlight
                    }).click(() => {
                        this.settingsWindow.hide();
                    });

                    const optionsContainer = $('<div></div>').attr('id', 'equipment-settings-options');
                    this.settingsWindow.append(closeButton).append(title).append(optionsContainer);
                    this.populateSettingsWindow(optionsContainer); // Wypełnij opcjami (użyje this.allEquipmentSets)

                    this.settingsWindow.appendTo('body');
                };

                // Najpierw pobierz aktualne dane, a potem utwórz/zaktualizuj okno
                this.fetchAndUpdateSets(() => {
                    createOrUpdateWindow();
                });
            };

            // Funkcja wypełniająca okno ustawień opcjami
            this.populateSettingsWindow = function(container) {
                console.log("[Zestawy wyposażenia] Wypełnianie ustawień danymi:", JSON.parse(JSON.stringify(this.allEquipmentSets))); // Log danych używanych do ustawień

                container.empty();

                // --- Opcja widoczności panelu ---
                const visibleLabel = $('<label></label>').css({ 'display': 'block', 'margin-bottom': '10px' });
                const visibleCheckbox = $('<input type="checkbox">')
                    .attr('id', 'setting-tiles-visible')
                    .css({ 'margin-right': '5px' });

                // Użyj zmiennej stanu
                visibleCheckbox.prop('checked', this.isPanelVisible);

                visibleCheckbox.on('change', (e) => {
                    const checked = $(e.target).is(':checked');
                    // Zaktualizuj zmienną stanu
                    this.isPanelVisible = checked;
                    // Zapisz do localStorage
                    localStorage.setItem(STORAGE_KEY_VISIBLE, checked);

                    if (checked) {
                        if (!this.tilesContainer || !this.tilesContainer.parent().length) {
                            this.buildAndShowTiles();
                        } else {
                            this.tilesContainer.show();
                        }
                    } else {
                        if (this.tilesContainer) {
                            this.tilesContainer.hide();
                        }
                    }
                });

                visibleLabel.append(visibleCheckbox).append('Pokaż panel zestawów');
                container.append(visibleLabel);

                // --- Opcja liczby kolumn ---
                const columnsLabel = $('<label></label>').css({ 'display': 'block', 'margin-bottom': '15px' });
                const columnsInput = $('<input type="number">')
                    .attr({
                        'id': 'setting-tiles-columns',
                        'min': '1',
                        'step': '1'
                     })
                     .css({ 'width': '50px', 'margin-left': '5px', 'border': '1px solid #5c3c1a', 'background-color': '#f0e0c0', 'color': '#3d1e0a' }); // Style input: light bg, dark border/text

                // Użyj zmiennej stanu
                columnsInput.val(this.numColumns);

                columnsInput.on('input', (e) => {
                    let newColumns = parseInt($(e.target).val(), 10);
                    if (isNaN(newColumns) || newColumns < 1) {
                        newColumns = this.DEFAULT_COLUMNS;
                        $(e.target).val(newColumns);
                    }
                    // Zaktualizuj zmienną stanu
                    this.numColumns = newColumns;
                    // Zapisz do localStorage
                    localStorage.setItem(STORAGE_KEY_COLUMNS, newColumns);

                    if (this.tilesContainer && this.tilesContainer.is(':visible')) {
                        this.tilesContainer.css('grid-template-columns', `repeat(${newColumns}, 1fr)`);
                    }
                });

                columnsLabel.append('Ilość kolumn:').append(columnsInput);
                container.append(columnsLabel);

                // --- Opcja skalowania kafelków ---
                const scaleLabel = $('<label></label>').css({ 'display': 'block', 'margin-bottom': '15px' });
                const scaleInput = $('<input type="range">')
                    .attr({
                        'id': 'setting-tiles-scale',
                        'min': this.MIN_TILE_SCALE,
                        'max': this.MAX_TILE_SCALE,
                        'step': 0.05
                    })
                    .css({ 'width': '100px', 'margin-left': '5px' })
                    .val(this.tileScale);

                const scaleValue = $('<span></span>').text(this.tileScale);

                scaleInput.on('input', (e) => {
                    let newScale = parseFloat($(e.target).val());
                    if (isNaN(newScale) || newScale < this.MIN_TILE_SCALE) {
                        newScale = this.MIN_TILE_SCALE;
                    }
                    this.tileScale = newScale;
                    localStorage.setItem(STORAGE_KEY_TILE_SCALE, newScale);
                    scaleValue.text(newScale.toFixed(2));
                    if (this.tilesContainer && this.tilesContainer.is(':visible')) {
                        this.tilesContainer.css({
                            'transform': `scale(${newScale})`,
                            'transform-origin': this.panelLocation === 'notibar' ? 'top left' : 'top right'
                        });
                    }
                });

                scaleLabel.append('Skalowanie kafelków:').append(scaleInput).append(scaleValue);
                container.append(scaleLabel);

                // --- Opcja lokalizacji panelu ---
                const locationLabel = $('<label></label>').css({ 'display': 'block', 'margin-bottom': '15px' });
                const locationSelect = $('<select></select>')
                    .attr('id', 'setting-panel-location')
                    .css({ 'margin-left': '5px', 'border': '1px solid #5c3c1a', 'background-color': '#f0e0c0', 'color': '#3d1e0a' })
                    .append('<option value="notibar">Pod powiadomieniami</option>')
                    .append('<option value="fixed">Prawa strona ekranu</option>');

                locationSelect.val(this.panelLocation);

                locationSelect.on('change', (e) => {
                    const newLocation = $(e.target).val();
                    this.panelLocation = newLocation;
                    localStorage.setItem(STORAGE_KEY_PANEL_LOCATION, newLocation);
                    // Przebuduj panel, by zmienić pozycję
                    if (this.tilesContainer) {
                        this.tilesContainer.remove();
                        this.tilesContainer = null;
                    }
                    this.rebuildTilesFromData();
                });

                locationLabel.append('Lokalizacja panelu:').append(locationSelect);
                container.append(locationLabel);

                // --- Separator ---
                container.append($('<hr>').css({ 'margin': '15px 0', 'border-top': '1px dashed #5c3c1a' }));

                // --- Opcja widoczności poszczególnych zestawów ---
                const setsVisibilityLabel = $('<div>Widoczne zestawy:</div>').css({
                     'font-weight': 'bold',
                     'margin-bottom': '10px'
                });
                container.append(setsVisibilityLabel);

                const setsContainer = $('<div></div>').css({
                     'max-height': '200px',
                     'overflow-y': 'auto',
                     'border': '1px solid #5c3c1a', // Dark wood border
                     'padding': '10px',
                     'background-color': 'rgba(255, 255, 255, 0.1)', // Slightly lighter area inside window
                     'border-radius': '3px',
                     'margin-top': '5px' // Space below title
                });

                // Sprawdź, czy mamy już załadowaną listę zestawów
                if (this.allEquipmentSets && this.allEquipmentSets.length > 0) {
                    this.allEquipmentSets.forEach((equipSet, index) => {
                        const setName = equipSet.name || `Zestaw Bez Nazwy ${index}`;
                        // Sprawdź, czy NAZWA jest w secie ukrytych
                        const isSetHidden = this.hiddenSetNames.has(setName);

                        const setLabel = $('<label></label>').css({ 'display': 'block', 'margin-bottom': '3px' });
                        const setCheckbox = $('<input type="checkbox">')
                            .attr('id', `setting-set-visible-${index}`)
                            .prop('checked', !isSetHidden)
                            .css({ 'margin-right': '5px' });

                        setCheckbox.on('change', (e) => {
                            const checked = $(e.target).is(':checked');
                            // Zaktualizuj set ukrytych NAZW
                            if (checked) {
                                this.hiddenSetNames.delete(setName);
                            } else {
                                this.hiddenSetNames.add(setName);
                            }
                            // Zapisz do localStorage (konwertując Set na Array)
                            localStorage.setItem(STORAGE_KEY_HIDDEN_NAMES, JSON.stringify(Array.from(this.hiddenSetNames)));

                            // Jeśli panel główny jest widoczny, przebuduj go
                            if (this.isPanelVisible && this.tilesContainer && this.tilesContainer.is(':visible')) {
                                this.rebuildTilesFromData();
                            }
                        });

                        setLabel.append(setCheckbox).append(setName);
                        setsContainer.append(setLabel);
                    });
                } else {
                    setsContainer.text('Lista zestawów zostanie załadowana po pierwszym otwarciu panelu lub odświeżeniu.');
                }

                container.append(setsContainer);
            };

            // Nowa funkcja do pobierania danych i aktualizacji listy zestawów
            this.fetchAndUpdateSets = function(callback) {
                Ajax.remoteCallMode('inventory', 'show_equip', {}, (data) => {
                    // console.log("[Zestawy wyposażenia] Dane otrzymane z Ajax:", JSON.parse(JSON.stringify(data))); // Usunięto log

                    EquipManager.list = Array.isArray(data.data) ? data.data : Object.values(data.data);
                    // Zapisz pełną listę zestawów do późniejszego użytku w ustawieniach
                    this.allEquipmentSets = EquipManager.list;

                    EquipManager.max = data.max;
                    EquipManager.premiumMax = data.premium_max;
                    EquipManager.hasPremium = data.hasPremium;

                    // Wywołaj callback po pobraniu i przetworzeniu danych
                    if (typeof callback === 'function') {
                        callback(data); // Przekazujemy dane, może się przydać
                    }
                });
            };

            // Funkcja do budowania/przebudowywania kafelków na podstawie aktualnych danych
            this.rebuildTilesFromData = function() {
                // console.log("[Zestawy wyposażenia] Przebudowa kafelków z danymi:", JSON.parse(JSON.stringify(this.allEquipmentSets))); // Usunięto log

                 // Użyj zmiennej stanu dla liczby kolumn
                const columns = this.numColumns;

                if (!this.tilesContainer) {
                    // --- Dodaj styl dla noszonego zestawu ---
                    const wornSetStyle = `
                        /* Animation for the worn set - REMOVED */
                        /* @keyframes pulseGold { ... } */

                        .worn-set-tile {
                            border: 2px solid #FFBF00 !important; /* Gold border */
                            background-color: #e0c8a0 !important; /* Keep the cream/sand background */
                            box-shadow: inset 0 0 4px rgba(0,0,0,0.2), 0 0 8px 2px #FFBF00 !important; /* Static gold glow */
                            /* animation: pulseGold 1.5s infinite ease-in-out; - REMOVED */
                        }
                        /* Style for the settings checkboxes and labels */
                        #equipment-settings-options label {
                            color: #3d1e0a; /* Dark brown text */
                            display: block;
                            margin-bottom: 8px;
                        }
                        #equipment-settings-options input[type="checkbox"] {
                            margin-right: 8px;
                            vertical-align: middle;
                             /* Basic styling is fine, rely on browser default for theme */
                        }
                        #equipment-settings-options input[type="number"] {
                            padding: 2px 4px;
                            border-radius: 3px;
                             /* Styles already applied inline */
                        }
                        #equipment-settings-options hr {
                            border-top: 1px dashed #5c3c1a; /* Dashed separator */
                            margin: 15px 0;
                        }
                        #equipment-settings-options > div:last-of-type { /* Sets container */
                            /* Styles already applied inline */
                        }
                    `;
                    $('<style>').prop('type', 'text/css').html(wornSetStyle).appendTo('head');
                    // --- Koniec dodawania stylu ---

                    this.tilesContainer = $('<div></div>')
                        .attr('id', 'equipment-tiles-container');

                    if (this.panelLocation === 'notibar') {
                        this.tilesContainer.css({
                            'position': 'relative',
                            'left': '-10px',
                            'margin-top': '10px',
                            'border': 'none',
                            'padding': '12px',
                            'display': 'grid',
                            'grid-template-columns': `repeat(${columns}, 1fr)`,
                            'gap': '8px',
                            'z-index': '1',
                            'max-width': '600px',
                            'max-height': '80vh',
                            'overflow-y': 'auto',
                            'box-shadow': '0 4px 12px rgba(0, 0, 0, 0.0)',
                            'right': '',
                            'top': ''
                        }).appendTo('#ui_notibar');
                    } else {
                        this.tilesContainer.css({
                            'position': 'fixed',
                            'top': '130px',
                            'right': '40px',
                            'margin-top': '',
                            'border': 'none',
                            'padding': '12px', // More padding
                            'display': 'grid',
                            'grid-template-columns': `repeat(${columns}, 1fr)`,
                            'gap': '8px', // Increased gap
                            'z-index': '1',
                            'max-width': '600px',
                            'max-height': '80vh',
                            'overflow-y': 'auto',
                            'box-shadow': '0 4px 12px rgba(0, 0, 0, 0.0)', // Darker, more defined shadow
                        }).appendTo('body');
                    }
                } else {
                     this.tilesContainer.css('grid-template-columns', `repeat(${columns}, 1fr)`);
                     this.tilesContainer.empty();
                }

                this.equipmentList = {}; // Wyczyść listę ID przed przebudową

                // Iteruj po ZAPISANEJ pełnej liście zestawów
                this.allEquipmentSets.forEach((equipSet, index) => {
                     // Sprawdź, czy zestaw nie jest aktualnie noszony
                     const match = this.checkEquipmentMatch(equipSet, ['body', 'neck', 'head', 'right_arm', 'left_arm', 'animal', 'pants', 'belt', 'yield', 'foot']);
                     // Sprawdź, czy zestaw jest ustawiony jako widoczny wg zmiennej stanu (NAZWY)
                     const setName = equipSet.name || `Zestaw Bez Nazwy ${index}`; // Użyj nazwy lub zastępczej
                     const setId = equipSet.equip_manager_id; // Potrzebne do przełączania
                     // Sprawdź, czy NAZWA NIE jest w secie ukrytych
                     const isSetVisible = !this.hiddenSetNames.has(setName);

                     // Loguj czynniki decyzyjne dla każdego zestawu - Usunięto log
                     // console.log(`[Zestawy wyposażenia] Processing set: '${setName}'. Match count: ${match}. IsVisible: ${isSetVisible} (Hidden: ${this.hiddenSetNames.has(setName)}). Should display: ${isSetVisible}`);

                     // Utwórz kafelek tylko jeśli jest widoczny (niezależnie czy noszony)
                     if (isSetVisible) {
                         const tileElement = this.createEquipmentTile(equipSet, index); // Przekazuj oryginalny index z allEquipmentSets

                         // Jeśli zestaw jest aktualnie noszony, dodaj mu klasę
                         if (match === 10) {
                             tileElement.addClass('worn-set-tile');
                         }

                         this.tilesContainer.append(tileElement);
                         // Zapisz ID zestawu (wymagane przez switchEquip) używając indexu
                         this.equipmentList[index] = setId;
                     }
                 });

                // Pokaż lub ukryj kontener na podstawie zmiennej stanu isPanelVisible
                if (this.isPanelVisible) {
                     this.tilesContainer.show();
                } else {
                     this.tilesContainer.hide();
                }

                this.tilesContainer.css({
                    'transform': `scale(${this.tileScale})`,
                    'transform-origin': this.panelLocation === 'notibar' ? 'top left' : 'top right'
                });
            };

            // Nowa funkcja do budowania i pokazywania kafelków (teraz tylko wrapper)
            this.buildAndShowTiles = function() {
                // Sprawdź, czy panel powinien być widoczny wg zmiennej stanu
                if (!this.isPanelVisible) {
                    if (this.tilesContainer) {
                        this.tilesContainer.hide();
                    }
                    return;
                }

                // Pobierz dane i przebuduj kafelki
                this.fetchAndUpdateSets(() => {
                    this.rebuildTilesFromData();
                     // Po pobraniu danych, jeśli okno ustawień jest otwarte, odśwież je
                    // To odświeżenie jest teraz robione w openSettingsWindow i przycisku Refresh
                    /*if (this.settingsWindow && this.settingsWindow.is(':visible')) {
                         const optionsContainer = this.settingsWindow.find('#equipment-settings-options');
                         this.populateSettingsWindow(optionsContainer); // Może powodować podwójne odświeżenie
                    }*/
                });
            };

            // Sprawdzanie dopasowania elementów ekwipunku
            this.checkEquipmentMatch = function(equipSet, types) {
                let matchCount = 0;

                types.forEach(type => {
                    const currentItem = Wear.get(type);
                    const setItem = equipSet[type];

                    if ((currentItem === null && setItem === null) ||
                        (currentItem !== null && currentItem.obj.item_id === setItem)) {
                        matchCount++;
                    }
                });

                return matchCount;
            };

            // Nowa funkcja do tworzenia pojedynczego kafelka (uproszczona obsługa podglądu)
            this.createEquipmentTile = function(equipSet, index) {
                const previewHTMLString = this.createEquipmentPreview(equipSet, index);

                // --- Funkcja do dekodowania HTML ---
                function decodeHtmlEntities(html) {
                    var txt = document.createElement("textarea");
                    txt.innerHTML = html;
                    return txt.value;
                }
                // --- Koniec funkcji do dekodowania ---

                const decodedPreviewHTML = decodeHtmlEntities(previewHTMLString);

                // Parsowanie zdekodowanego HTML i modyfikacja obrazków w pamięci
                const previewFragment = $(decodedPreviewHTML);
                const secondColumnTypes = ['head', 'body', 'pants'];
                const thirdColumnTypes = ['left_arm', 'belt', 'foot'];

                previewFragment.find('img').each(function() {
                    $(this).css('position', 'absolute');
                    const parentStyle = $(this).parent().attr('style') || '';
                    if (parentStyle.includes('left: 15px') || parentStyle.includes('left: 35px')) {
                        this.style.setProperty('width', '20px', 'important');
                        this.style.setProperty('height', '20px', 'important');
                    } else {
                        this.style.setProperty('width', '15px', 'important');
                        this.style.setProperty('height', '15px', 'important');
                    }
                });

                const tile = $('<div></div>')
                    .addClass('equipment-tile')
                    .attr('title', equipSet.name)
                    .css({
                        'border': '3px solid #5c3c1a',
                        'padding': '0px',
                        'text-align': 'center',
                        'cursor': 'pointer',
                        'background': 'url("https://westpl.innogamescdn.com/images/inventory/bag.png?1") repeat scroll 0 0 #e0c8a0',
                        'background-color': '#e0c8a0',
                        'border-radius': '3px',
                        'transition': 'background-color 0.2s ease, transform 0.1s ease, border-color 0.2s ease',
                        'box-shadow': 'inset 0 0 0px rgba(0,0,0,0.3)',
                        'width': '55px',
                        'min-width': '55px',
                        'height': '60px',
                        'min-height': '60px'
                    })
                    .html(`
                        <div class="equipment-preview-placeholder" style="margin-bottom: 0px;"></div>
                    `)
                    .on('mouseenter', function() {
                        // Zastosuj efekt hover tylko jeśli kafelek NIE jest noszonym zestawem
                        if (!$(this).hasClass('worn-set-tile')) {
                            $(this).css({
                                'background-color': '#f5deb3', // Lighter wheat/sand on hover
                                'transform': ''
                            });
                        }
                    })
                    .on('mouseleave', function() {
                        // Przywróć tło i transformację tylko jeśli kafelek NIE jest noszonym zestawem
                        if (!$(this).hasClass('worn-set-tile')) {
                            $(this).css({
                                'background-color': '#e0c8a0', // Original darker sand
                                'transform': ''
                            });
                        }
                    })
                    .click(() => {
                        this.switchEquipmentByIndex(index);
                    });

                // Dołączenie zmodyfikowanego fragmentu podglądu do placeholdera
                tile.find('.equipment-preview-placeholder').append(previewFragment);

                return tile;
            };

            // Tworzenie podglądu zestawu (dodano przezroczyste tło)
            this.createEquipmentPreview = function(equipSet, index) {
                // Szerokość 79px, bez marginesu auto, przezroczyste tło
                const previewDiv = `<div id="equipshow_${equipSet.equip_manager_id}" style="height: 60px; width: 55px; position: relative; margin: 0; overflow: hidden; background-color: transparent;">`;

                // Koordynaty (top, left) pozostają bez zmian (0, 27, 54)
                const parts = [
                    // Kolumna 1 (left=0)
                    ['neck',        0,   0],
                    ['right_arm',  15,   0],
                    ['animal',     30,   0],
                    ['yield',      45,   0],
                    // Kolumna 2 (left=15)
                    ['head',        0,  15],
                    ['body',       20,  15],
                    ['pants',      40,  15],
                    // Kolumna 3 (left=35)
                    ['left_arm',    0,  35],
                    ['belt',       20,  35],
                    ['foot',       40,  35],
                ];

                let previewContent = parts.reduce((preview, [type, top, left]) => {
                     let partHtml = '';
                     if (typeof EquipManager.titlepart === 'function') {
                         try {
                             partHtml = EquipManager.titlepart(index, type, top, left);
                             // Loguj tylko dla konkretnego slotu (np. 'head') i zestawu (np. 'win'), aby nie zalać konsoli
                             if (type === 'head' && equipSet.name === 'win') {
                                 console.log(`[Zestawy wyposażenia] HTML dla głowy (set 'win', index ${index}) z EquipManager.titlepart:`, partHtml);
                             }
                         } catch (error) {
                             console.error(`Error calling EquipManager.titlepart for type ${type}:`, error);
                         }
                     } else {
                         console.error("EquipManager.titlepart is not a function!");
                     }
                     return preview + (partHtml || '');
                 }, '');

                return previewDiv + previewContent + '</div>';
            };

            // Zmiana ekwipunku (oryginalna funkcja potrzebuje ID, dodajemy funkcję pomocniczą przez index)
            this.switchEquipmentByIndex = function(selectedIndex) {
                 if (this.equipmentList.hasOwnProperty(selectedIndex)) {
                     const equipId = this.equipmentList[selectedIndex];
                     EquipManager.switchEquip(equipId);

                     // Po udanej zmianie, odśwież dane i przebuduj kafelki
                     // Użyj setTimeout, aby dać serwerowi chwilę na przetworzenie zmiany
                     setTimeout(() => {
                         console.log("[Zestawy wyposażenia] Refreshing data after set switch...");
                         this.fetchAndUpdateSets(() => {
                             this.rebuildTilesFromData();
                         });
                     }, 500); // Małe opóźnienie (0.5 sekundy)

                 } else {
                     new UserMessage("Error: Could not find equipment set ID for the selected index.", UserMessage.TYPE_ERROR).show();
                 }
             };
        }

        // Utworzenie i inicjalizacja menedżera zestawów
        const menedzerZestawow = new MenedzerZestawowWyposazenia();
        menedzerZestawow.initialize();
    }

    // Uruchomienie skryptu
    initializeEquipmentSets();

    // --- Usuwanie powiadomień o pracy co 15 sekund ---
    function removeJobNotifications() {
        document.querySelectorAll('.ui_ongoing .tw2gui_window_buttons_close[title="Ukryj wszystkie powiadomienia o pracy"]').forEach(btn => {
            const notification = btn.closest('.ui_ongoing');
            if (notification) notification.remove();
        });
    }

    setInterval(removeJobNotifications, 15000);
}, 2000);
})();