Enhanced Bank Sorting for flatmmmo.com

Add better sorting features to bank storage

// ==UserScript==
// @name         Enhanced Bank Sorting for flatmmmo.com
// @namespace    http://tampermonkey.net/
// @version      2.6.2
// @description  Add better sorting features to bank storage
// @author       Carlos
// @match        https://flatmmo.com/play.php
// @match        https://flatmmo.com/play.php*
// @grant        none
// ==/UserScript==
 
(function() {
    'use strict';
 
    console.log('Enhanced Bank Sorting script loaded!');
 
    let original_bank_items = [];
    let current_sort_method = 'original';
 
    // Wait for the page to load and bank to be available
    function init() {
        if (typeof bank_items === 'undefined' || typeof refresh_bank !== 'function') {
            setTimeout(init, 500);
            return;
        }
 
        // Store original order when first loaded
        if (original_bank_items.length === 0) {
            original_bank_items = [...bank_items];
        }
 
        addSortingControls();
    }
 
    function addSortingControls() {
        const storageWrapper = document.querySelector('.storage-wrapper');
        if (!storageWrapper) {
            setTimeout(addSortingControls, 500);
            return;
        }
 
        // Check if controls already exist
        if (document.getElementById('sorting-controls')) {
            return;
        }
 
        // Create sorting controls container
        const sortingControls = document.createElement('div');
        sortingControls.id = 'sorting-controls';
        sortingControls.style.cssText = `
            margin: 10px 0;
            padding: 15px;
            background: rgba(255,255,255,0.1);
            border: 1px solid #555;
            border-radius: 5px;
            display: flex;
            gap: 15px;
            flex-wrap: wrap;
            align-items: center;
        `;
 
        // Sort label
        const sortLabel = document.createElement('span');
        sortLabel.textContent = 'Sort by: ';
        sortLabel.style.cssText = `
            color: white;
            font-weight: bold;
            font-size: 14pt;
        `;
 
        // Sort buttons container
        const buttonContainer = document.createElement('div');
        buttonContainer.style.cssText = `
            display: flex;
            gap: 8px;
            flex-wrap: wrap;
        `;
 
        // Sort options with their functions
        const sortOptions = [
            { id: 'original', text: 'Original', method: sortByOriginal },
            { id: 'name-asc', text: 'Name A-Z', method: () => sortByName(false) },
            { id: 'name-desc', text: 'Name Z-A', method: () => sortByName(true) },
            { id: 'amount-high', text: 'Amount ↓', method: () => sortByAmount(true) },
            { id: 'amount-low', text: 'Amount ↑', method: () => sortByAmount(false) },
            { id: 'type', text: 'Type', method: sortByType }
        ];
 
        // Create buttons
        sortOptions.forEach(option => {
            const button = document.createElement('button');
            button.id = `sort-${option.id}`;
            button.textContent = option.text;
            button.style.cssText = `
                padding: 8px 12px;
                border: 1px solid #666;
                border-radius: 3px;
                background-color: #333;
                color: white;
                cursor: pointer;
                font-size: 11pt;
                font-family: Courier;
                transition: all 0.2s;
            `;
 
            button.addEventListener('mouseenter', function() {
                if (current_sort_method !== option.id) {
                    this.style.backgroundColor = '#555';
                }
            });
 
            button.addEventListener('mouseleave', function() {
                if (current_sort_method !== option.id) {
                    this.style.backgroundColor = '#333';
                }
            });
 
            button.addEventListener('click', function() {
                // Prevent re-sorting if the same method is selected
                if (current_sort_method === option.id) {
                    return;
                }
 
                // Reset all buttons to default style
                document.querySelectorAll('[id^="sort-"]').forEach(btn => {
                    btn.style.backgroundColor = '#333';
                    btn.style.borderColor = '#666';
                });
 
                // Highlight the selected button
                this.style.backgroundColor = '#555';
                this.style.borderColor = '#888';
 
                current_sort_method = option.id;
                resetBankItems(); // Reset items and apply new sort method
                option.method();
            });
 
            buttonContainer.appendChild(button);
        });
 
        // Set initial active button
        setTimeout(() => {
            document.getElementById('sort-original').style.backgroundColor = '#555';
            document.getElementById('sort-original').style.borderColor = '#888';
        }, 100);
 
        // Assemble controls
        sortingControls.appendChild(sortLabel);
        sortingControls.appendChild(buttonContainer);
 
        // Insert after the search input
        const searchInput = storageWrapper.querySelector('input[type="text"]');
        if (searchInput && searchInput.parentNode) {
            searchInput.parentNode.insertBefore(sortingControls, searchInput.nextSibling);
        } else {
            storageWrapper.insertBefore(sortingControls, storageWrapper.firstChild);
        }
    }
 
    // Function to reset bank items to original state and ensure no conflicts
    function resetBankItems() {
        bank_items.splice(0, bank_items.length, ...original_bank_items);
    }
 
    // Flag to prevent refresh_bank loop during sorting
    let isSorting = false;

    // Sorting Functions
    function sortByOriginal() {
        isSorting = true;
        resetBankItems();
        originalRefreshBank();
        isSorting = false;
    }
 
    function sortByName(reverse = false) {
        isSorting = true;
        resetBankItems();
        bank_items.sort((a, b) => {
            const nameA = a.name.toLowerCase();
            const nameB = b.name.toLowerCase();
            return reverse ? nameB.localeCompare(nameA) : nameA.localeCompare(nameB);
        });
        originalRefreshBank();
        isSorting = false;
    }
 
    function sortByAmount(highToLow = true) {
        isSorting = true;
        resetBankItems();
        bank_items.sort((a, b) => {
            return highToLow ? b.value - a.value : a.value - b.value;
        });
        originalRefreshBank();
        isSorting = false;
    }
 
    function sortByType() {
        isSorting = true;
        resetBankItems();
        
        // Enhanced item type categories - alphabetical order for pure type sorting
        const itemCategories = {
            arrows: ['arrow', 'heads'],
            armor: ['helmet', 'body', 'legs', 'boots', 'gloves', 'mask', 'hat', 'top', 'skirt'],
            bars: ['_bar', 'promethium', 'gold', 'silver', 'iron', 'bronze', 'copper'],
            containers: ['bucket', 'vial'],
            food_cooked: ['cooked_', 'cake', 'bread'],
            food_raw: ['raw_', 'milk_bucket', 'wheat', 'sugarcane', 'banana'],
            gems: ['diamond', 'ruby', 'emerald', 'sapphire', 'crystal', 'gemstone'],
            jewelry: ['necklace', 'ring', 'sigil'],
            leaves: ['leaf'],
            materials: ['fur', 'hide', 'skin', 'silk', 'coal', 'string', 'feathers', 'nails', 'glass', 'bones', 'bonemeal', 'matches', 'algae'],
            misc: [],
            mushrooms: ['mushroom', 'shroom', 'fireshroom', 'moldshroom', 'seashroom', 'rockshroom', 'green_mushroom', 'blue_mushroom', 'spirit_mushroom', 'red_mushroom'],
            orbs: ['orb'],
            potions: ['potion'],
            seeds: ['seeds', 'haunted_tree_seeds', 'crystal_leaf_seeds', 'mangrove_tree_seeds', 'maple_tree_seeds', 'willow_tree_seeds', 'gold_leaf_seeds', 'oak_tree_seeds', 'lime_leaf_seeds', 'tree_seeds', 'fireshroom_seeds', 'moldshroom_seeds', 'green_leaf_seeds', 'seashroom_seeds', 'rockshroom_seeds', 'stardust_seeds', 'blue_mushroom_seeds', 'green_mushroom_seeds', 'dotted_green_leaf_seeds', 'spirit_mushroom_seeds', 'red_mushroom_seeds', 'hp_mushroom_seeds', 'wheat_seeds'],
            tools: ['pickaxe', 'axe', 'shovel', 'fishing_rod', 'bonecrusher'],
            trees: ['logs', 'plank'],
            weapons: ['sword', 'bow', 'staff', 'knife', 'mace', 'club', 'harpoon']
        };
 
        function getItemType(itemName) {
            const name = itemName.toLowerCase();
            
            // Check each category
            for (const [type, keywords] of Object.entries(itemCategories)) {
                if (type === 'misc') continue;
                
                if (keywords.some(keyword => name.includes(keyword))) {
                    return type;
                }
            }
            return 'misc';
        }
 
        bank_items.sort((a, b) => {
            const typeA = getItemType(a.name);
            const typeB = getItemType(b.name);
            
            // Sort by type name alphabetically first
            if (typeA !== typeB) {
                return typeA.localeCompare(typeB);
            }
            
            // Within same type, sort by item name alphabetically
            return a.name.localeCompare(b.name);
        });
        originalRefreshBank();
        isSorting = false;
    }
 
    // Override the original refresh_bank function
    const originalRefreshBank = window.refresh_bank;
    window.refresh_bank = function() {
        // Skip the override if we're in the middle of sorting
        if (isSorting) {
            originalRefreshBank.apply(this, arguments);
            return;
        }

        // Store current items before refresh if they haven't been stored yet
        if (bank_items.length > 0 && original_bank_items.length === 0) {
            original_bank_items = [...bank_items];
        }

        // Call the original refresh_bank function
        originalRefreshBank.apply(this, arguments);

        // Update original_bank_items after refresh to include new items
        setTimeout(() => {
            if (current_sort_method === 'original') {
                original_bank_items = [...bank_items];
            } else {
                // Store the current unsorted state as new original
                original_bank_items = [...bank_items];
                
                // Reapply current sorting method
                switch(current_sort_method) {
                    case 'name-asc':
                        sortByName(false);
                        break;
                    case 'name-desc':
                        sortByName(true);
                        break;
                    case 'amount-high':
                        sortByAmount(true);
                        break;
                    case 'amount-low':
                        sortByAmount(false);
                        break;
                    case 'type':
                        sortByType();
                        break;
                }
            }
        }, 50);
    };
 
    // Start initialization
    init();
 
    // Also run when bank is opened
    const originalIsBank = window.is_bank_open;
    if (originalIsBank) {
        setInterval(() => {
            if (is_bank_open() && !document.getElementById('sorting-controls')) {
                addSortingControls();
            }
        }, 1000);
    }
 
})();