JIGS (Jigglymoose's Intelligent Gear Simulator)

Automates running multiple simulations on the MWI Combat Simulator with a dynamic, grouped UI and cost-analysis.

当前为 2025-09-29 提交的版本,查看 最新版本

// ==UserScript==
// @name         JIGS (Jigglymoose's Intelligent Gear Simulator)
// @namespace    http://tampermonkey.net/
// @version      22.15
// @description  Automates running multiple simulations on the MWI Combat Simulator with a dynamic, grouped UI and cost-analysis.
// @author       Gemini & Jigglymoose
// @license      MIT
// @match        https://shykai.github.io/MWICombatSimulatorTest/dist/
// @match        https://shykai.github.io/MWICombatSimulator/dist/
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    console.log("JIGS (Jigglymoose's Intelligent Gear Simulator) v22.15 Loaded");

    // --- CONFIGURATION ---
    const MARKET_API_URL = 'https://www.milkywayidle.com/game_data/marketplace.json';
    const ABILITY_XP_LEVELS = [0, 0, 33, 76, 132, 202, 286, 386, 503, 637, 791, 964, 1159, 1377, 1620, 1891, 2192, 2525, 2893, 3300, 3750, 4247, 4795, 5400, 6068, 6805, 7618, 8517, 9508, 10604, 11814, 13151, 14629, 16262, 18068, 20064, 22271, 24712, 27411, 30396, 33697, 37346, 41381, 45842, 50773, 56222, 62243, 68895, 76242, 84355, 93311, 103195, 114100, 126127, 139390, 154009, 170118, 187863, 207403, 228914, 252584, 278623, 307256, 338731, 373318, 411311, 453030, 498824, 549074, 604193, 664632, 730881, 803472, 882985, 970050, 1065351, 1169633, 1283701, 1408433, 1544780, 1693774, 1856536, 2034279, 2228321, 2440088, 2671127, 2923113, 3197861, 3497335, 3823663, 4179145, 4566274, 4987741, 5446463, 5945587, 6488521, 7078945, 7720834, 8418485, 9176537, 10000000, 11404976, 12904567, 14514400, 16242080, 18095702, 20083886, 22215808, 24501230, 26950540, 29574787, 32385721, 35395838, 38618420, 42067584, 45758332, 49706603, 53929328, 58444489, 63271179, 68429670, 73941479, 79829440, 86117783, 92832214, 100000000, 114406130, 130118394, 147319656, 166147618, 186752428, 209297771, 233962072, 260939787, 290442814, 322702028, 357968938, 396517495, 438646053, 484679494, 534971538, 589907252, 649905763, 715423218, 786955977, 865044093, 950275074, 1043287971, 1144777804, 1255500373, 1376277458, 1508002470, 1651646566, 1808265285, 1979005730, 2165114358, 2367945418, 2588970089, 2829786381, 3092129857, 3377885250, 3689099031, 4027993033, 4396979184, 4798675471, 5235923207, 5711805728, 6229668624, 6793141628, 7406162301, 8073001662, 8798291902, 9587056372, 10444742007, 11377254401, 12390995728, 13492905745, 14690506120, 15991948361, 17406065609, 18942428633, 20611406335, 22424231139, 24393069640, 26531098945, 28852589138, 31372992363, 34109039054, 37078841860, 40302007875, 43799759843, 47595067021, 51712786465, 56179815564, 61025256696, 66280594953, 71979889960, 78159982881, 84860719814, 92125192822, 100000000000];
    const SPELL_BOOK_XP = { "Poke": 50, "Impale": 500, "Puncture": 500, "Penetrating Strike": 500, "Scratch": 50, "Cleave": 500, "Maim": 500, "Crippling Slash": 500, "Smack": 50, "Sweep": 500, "Stunning Blow": 500, "Fracturing Impact": 500, "Shield Bash": 500, "Quick Shot": 50, "Aqua Arrow": 500, "Flame Arrow": 500, "Rain Of Arrows": 500, "Silencing Shot": 500, "Steady Shot": 500, "Pestilent Shot": 500, "Penetrating Shot": 500, "Water Strike": 50, "Ice Spear": 500, "Frost Surge": 500, "Mana Spring": 500, "Entangle": 50, "Toxic Pollen": 500, "Nature's Veil": 500, "Life Drain": 500, "Fireball": 50, "Flame Blast": 500, "Firestorm": 500, "Smoke Burst": 500, "Minor Heal": 50, "Heal": 500, "Quick Aid": 500, "Rejuvenate": 500, "Taunt": 500, "Provoke": 500, "Toughness": 500, "Elusiveness": 500, "Precision": 500, "Berserk": 500, "Elemental Affinity": 500, "Frenzy": 500, "Spike Shell": 500, "Retribution": 500, "Vampirism": 500, "Revive": 500, "Insanity": 500, "Invincible": 500, "Speed Aura": 500, "Guardian Aura": 500, "Fierce Aura": 500, "Critical Aura": 500, "Mystic Aura": 500, "Spell Book: Poke": 50, "Spell Book: Scratch": 50, "Spell Book: Smack": 50, "Spell Book: Quick Shot": 50, "Spell Book: Aqua Arrow": 500, "Spell Book: Flame Arrow": 500, "Spell Book: Water Strike": 50, "Spell Book: Entangle": 50, "Spell Book: Fireball": 50, "Spell Book: Minor Heal": 50 };
    const BASIC_MATERIALS_MAP = {"/items/poke":"Poke","/items/impale":"Impale","/items/puncture":"Puncture","/items/penetrating_strike":"Penetrating Strike","/items/scratch":"Scratch","/items/cleave":"Cleave","/items/maim":"Maim","/items/crippling_slash":"Crippling Slash","/items/smack":"Smack","/items/sweep":"Sweep","/items/stunning_blow":"Stunning Blow","/items/fracturing_impact":"Fracturing Impact","/items/shield_bash":"Shield Bash","/items/quick_shot":"Quick Shot","/items/aqua_arrow":"Aqua Arrow","/items/flame_arrow":"Flame Arrow","/items/rain_of_arrows":"Rain Of Arrows","/items/silencing_shot":"Silencing Shot","/items/steady_shot":"Steady Shot","/items/pestilent_shot":"Pestilent Shot","/items/penetrating_shot":"Penetrating Shot","/items/water_strike":"Water Strike","/items/ice_spear":"Ice Spear","/items/frost_surge":"Frost Surge","/items/mana_spring":"Mana Spring","/items/entangle":"Entangle","/items/toxic_pollen":"Toxic Pollen","/items/natures_veil":"Nature's Veil","/items/life_drain":"Life Drain","/items/fireball":"Fireball","/items/flame_blast":"Flame Blast","/items/firestorm":"Firestorm","/items/smoke_burst":"Smoke Burst","/items/minor_heal":"Minor Heal","/items/heal":"Heal","/items/quick_aid":"Quick Aid","/items/rejuvenate":"Rejuvenate","/items/taunt":"Taunt","/items/provoke":"Provoke","/items/toughness":"Toughness","/items/elusiveness":"Elusiveness","/items/precision":"Precision","/items/berserk":"Berserk","/items/elemental_affinity":"Elemental Affinity","/items/frenzy":"Frenzy","/items/spike_shell":"Spike Shell","/items/retribution":"Retribution","/items/vampirism":"Vampirism","/items/revive":"Revive","/items/insanity":"Insanity","/items/invincible":"Invincible","/items/speed_aura":"Speed Aura","/items/guardian_aura":"Guardian Aura","/items/fierce_aura":"Fierce Aura","/items/critical_aura":"Critical Aura","/items/mystic_aura":"Mystic Aura"};
    const SIMULATOR_TO_MARKET_MAP = { "Acrobatic Hood (R)": "Acrobatic Hood Refined", "Anchorbound Plate Body (R)": "Anchorbound Plate Body Refined", "Anchorbound Plate Legs (R)": "Anchorbound Plate Legs Refined", "Bishops Codex (R)": "Bishops Codex Refined", "Blazing Trident (R)": "Blazing Trident Refined", "Blooming Trident (R)": "Blooming Trident Refined", "Chaotic Flail (R)": "Chaotic Flail Refined", "Corsair Helmet (R)": "Corsair Helmet Refined", "Cursed Bow (R)": "Cursed Bow Refined", "Dodocamel Gauntlets (R)": "Dodocamel Gauntlets Refined", "Furious Spear (R)": "Furious Spear Refined", "Griffin Bulwark (R)": "Griffin Bulwark Refined", "Knights Aegis (R)": "Knights Aegis Refined", "Kraken Chaps (R)": "Kraken Chaps Refined", "Kraken Tunic (R)": "Kraken Tunic Refined", "Maelstrom Plate Body (R)": "Maelstrom Plate Body Refined", "Maelstrom Plate Legs (R)": "Maelstrom Plate Legs Refined", "Magicians Hat (R)": "Magicians Hat Refined", "Marksman Bracers (R)": "Marksman Bracers Refined", "Regal Sword (R)": "Regal Sword Refined", "Rippling Trident (R)": "Rippling Trident Refined", "Royal Fire Robe Bottoms (R)": "Royal Fire Robe Bottoms Refined", "Royal Fire Robe Top (R)": "Royal Fire Robe Top Refined", "Royal Nature Robe Bottoms (R)": "Royal Nature Robe Bottoms Refined", "Royal Nature Robe Top (R)": "Royal Nature Robe Top Refined", "Royal Water Robe Bottoms (R)": "Royal Water Robe Bottoms Refined", "Royal Water Robe Top (R)": "Royal Water Robe Top Refined", "Sundering Crossbow (R)": "Sundering Crossbow Refined" };
    const HOUSE_RECIPES = {"Dairy Barn":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/milk":150,"/items/cheese_brush":9,"/items/verdant_brush":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/milk":3000,"/items/verdant_milk":6000,"/items/cheese_brush":12,"/items/verdant_brush":12,"/items/azure_brush":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/verdant_milk":12000,"/items/azure_milk":12000,"/items/azure_brush":15,"/items/burble_brush":15,"/items/milking_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/azure_milk":24000,"/items/burble_milk":24000,"/items/burble_brush":18,"/items/crimson_brush":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/burble_milk":48000,"/items/crimson_milk":48000,"/items/crimson_brush":21,"/items/rainbow_brush":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/crimson_milk":96000,"/items/rainbow_milk":96000,"/items/rainbow_brush":24,"/items/holy_brush":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/holy_milk":240000,"/items/holy_brush":60,"/items/super_milking_tea":2000}}},"Log Shed":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/log":1500,"/items/birch_log":3000,"/items/cheese_hatchet":9,"/items/verdant_hatchet":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/birch_log":6000,"/items/cedar_log":6000,"/items/cheese_hatchet":12,"/items/verdant_hatchet":12,"/items/azure_hatchet":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/cedar_log":12000,"/items/purpleheart_log":12000,"/items/azure_hatchet":15,"/items/burble_hatchet":15,"/items/woodcutting_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/purpleheart_log":24000,"/items/ginkgo_log":24000,"/items/burble_hatchet":18,"/items/crimson_hatchet":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/ginkgo_log":48000,"/items/redwood_log":48000,"/items/crimson_hatchet":21,"/items/rainbow_hatchet":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/redwood_log":96000,"/items/arcane_log":96000,"/items/rainbow_hatchet":24,"/items/holy_hatchet":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/holy_log":240000,"/items/holy_hatchet":60,"/items/super_woodcutting_tea":2000}}},"Forge":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/cheese":750,"/items/cheese_hammer":9,"/items/verdant_hammer":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/cheese":1500,"/items/verdant_cheese":1500,"/items/cheese_hammer":12,"/items/verdant_hammer":12,"/items/azure_hammer":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/verdant_cheese":3000,"/items/azure_cheese":3000,"/items/azure_hammer":15,"/items/burble_hammer":15,"/items/cheesesmithing_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/azure_cheese":6000,"/items/burble_cheese":6000,"/items/burble_hammer":18,"/items/crimson_hammer":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/burble_cheese":12000,"/items/crimson_cheese":12000,"/items/crimson_hammer":21,"/items/rainbow_hammer":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/crimson_cheese":24000,"/items/rainbow_cheese":24000,"/items/rainbow_hammer":24,"/items/holy_hammer":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/holy_cheese":60000,"/items/holy_hammer":60,"/items/super_cheesesmithing_tea":2000}}},"Sewing Parlor":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/rough_leather":180,"/items/cotton_fabric":360,"/items/linen_fabric":360}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/rough_leather":360,"/items/reptile_leather":1440,"/items/linen_fabric":1440,"/items/verdant_needle":12,"/items/azure_needle":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/reptile_leather":1440,"/items/gobo_leather":1440,"/items/linen_fabric":1440,"/items/bamboo_fabric":1440,"/items/azure_needle":15,"/items/burble_needle":15,"/items/tailoring_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/gobo_leather":5760,"/items/bamboo_fabric":5760,"/items/burble_needle":18,"/items/crimson_needle":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/gobo_leather":5760,"/items/bamboo_fabric":5760,"/items/silk_fabric":5760,"/items/crimson_needle":21,"/items/rainbow_needle":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/beast_leather":11520,"/items/umbral_leather":11520,"/items/silk_fabric":11520,"/items/radiant_fabric":11520,"/items/rainbow_needle":24,"/items/holy_needle":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/umbral_leather":28800,"/items/radiant_fabric":28800,"/items/holy_needle":60,"/items/super_tailoring_tea":2000}}},"Workshop":{"2":{"gold":2000000,"materials":{"/items/lumber":900,"/items/birch_lumber":900,"/items/cheese_chisel":9,"/items/verdant_chisel":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":1800,"/items/cedar_lumber":1800,"/items/cheese_chisel":12,"/items/verdant_chisel":12,"/items/azure_chisel":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":3600,"/items/purpleheart_lumber":3600,"/items/azure_chisel":15,"/items/burble_chisel":15,"/items/crafting_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":7200,"/items/ginkgo_lumber":7200,"/items/burble_chisel":18,"/items/crimson_chisel":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":14400,"/items/redwood_lumber":14400,"/items/crimson_chisel":21,"/items/rainbow_chisel":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":28800,"/items/arcane_lumber":28800,"/items/rainbow_chisel":24,"/items/holy_chisel":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":72000,"/items/holy_chisel":60,"/items/super_crafting_tea":2000}}},"Observatory":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/swamp_essence":1500,"/items/aqua_essence":1500,"/items/cheese_enhancer":9,"/items/verdant_enhancer":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/swamp_essence":3000,"/items/aqua_essence":3000,"/items/jungle_essence":3000,"/items/cheese_enhancer":12,"/items/verdant_enhancer":12,"/items/azure_enhancer":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/jungle_essence":6000,"/items/gobo_essence":6000,"/items/azure_enhancer":15,"/items/burble_enhancer":15,"/items/enhancing_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/gobo_essence":12000,"/items/eyessence":12000,"/items/burble_enhancer":18,"/items/crimson_enhancer":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/eyessence":24000,"/items/sorcerer_essence":24000,"/items/crimson_enhancer":21,"/items/rainbow_enhancer":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/sorcerer_essence":48000,"/items/bear_essence":48000,"/items/rainbow_enhancer":24,"/items/holy_enhancer":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/bear_essence":120000,"/items/holy_enhancer":60,"/items/super_enhancing_tea":2000}}},"Laboratory":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/milking_essence":100,"/items/foraging_essence":200,"/items/woodcutting_essence":200,"/items/cheese_alembic":9,"/items/verdant_alembic":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/cheesesmithing_essence":500,"/items/cooking_essence":500,"/items/brewing_essence":500,"/items/tailoring_essence":12,"/items/azure_alembic":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/cooking_essence":1000,"/items/alchemy_essence":1000,"/items/enhancing_essence":1000,"/items/azure_alembic":15,"/items/burble_alembic":15,"/items/alchemy_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/alchemy_essence":1600,"/items/milking_essence":1600,"/items/woodcutting_essence":1600,"/items/burble_alembic":18,"/items/crimson_alembic":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/milking_essence":3200,"/items/cheesesmithing_essence":3200,"/items/tailoring_essence":3200,"/items/crimson_alembic":21,"/items/rainbow_alembic":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/cheesesmithing_essence":8000,"/items/cooking_essence":8000,"/items/brewing_essence":8000,"/items/rainbow_alembic":24,"/items/holy_alembic":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/alchemy_essence":12000,"/items/enhancing_essence":12000,"/items/holy_alembic":60,"/items/super_alchemy_tea":2000}}},"Brewery":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/green_tea_leaf":600,"/items/arabica_coffee_bean":600,"/items/cheese_pot":9,"/items/verdant_pot":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/green_tea_leaf":2400,"/items/black_tea_leaf":2400,"/items/robusta_coffee_bean":2400,"/items/cheese_pot":12,"/items/verdant_pot":12,"/items/azure_pot":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/black_tea_leaf":2400,"/items/burble_tea_leaf":2400,"/items/robusta_coffee_bean":2400,"/items/liberica_coffee_bean":2400,"/items/azure_pot":15,"/items/burble_pot":15,"/items/brewing_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/burble_tea_leaf":4800,"/items/mooalong_tea_leaf":4800,"/items/liberica_coffee_bean":4800,"/items/excelsa_coffee_bean":4800,"/items/burble_pot":18,"/items/crimson_pot":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/mooalong_tea_leaf":9600,"/items/red_tea_leaf":9600,"/items/excelsa_coffee_bean":9600,"/items/fieriosa_coffee_bean":9600,"/items/crimson_pot":21,"/items/rainbow_pot":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/red_tea_leaf":19200,"/items/emp_tea_leaf":19200,"/items/fieriosa_coffee_bean":19200,"/items/spacia_coffee_bean":19200,"/items/rainbow_pot":24,"/items/holy_pot":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/emp_tea_leaf":48000,"/items/spacia_coffee_bean":48000,"/items/holy_pot":60,"/items/super_brewing_tea":2000}}},"Kitchen":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/sugar":8000,"/items/egg":1000,"/items/wheat":1000,"/items/apple":500,"/items/cheese_spatula":9,"/items/verdant_spatula":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/sugar":20000,"/items/egg":2000,"/items/blueberry":2000,"/items/wheat":1000,"/items/apple":1000,"/items/orange":1000,"/items/cheese_spatula":12,"/items/verdant_spatula":12,"/items/azure_spatula":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/sugar":48000,"/items/blueberry":4000,"/items/blackberry":4000,"/items/strawberry":4000,"/items/orange":2000,"/items/plum":2000,"/items/azure_spatula":15,"/items/burble_spatula":15,"/items/cooking_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/sugar":100000,"/items/blackberry":8000,"/items/strawberry":8000,"/items/mooberry":8000,"/items/plum":4000,"/items/peach":4000,"/items/burble_spatula":18,"/items/crimson_spatula":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/sugar":200000,"/items/strawberry":16000,"/items/mooberry":16000,"/items/marsberry":16000,"/items/peach":8000,"/items/dragon_fruit":8000,"/items/crimson_spatula":21,"/items/rainbow_spatula":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/sugar":360000,"/items/mooberry":32000,"/items/marsberry":32000,"/items/spaceberry":32000,"/items/dragon_fruit":16000,"/items/star_fruit":16000,"/items/rainbow_spatula":24,"/items/holy_spatula":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/sugar":640000,"/items/spaceberry":80000,"/items/star_fruit":40000,"/items/holy_spatula":60,"/items/super_cooking_tea":2000}}},"Garden":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/egg":150,"/items/wheat":1000,"/items/cotton":1500,"/items/flax":1500,"/items/cheese_shears":9,"/items/verdant_shears":9}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/egg":300,"/items/blueberry":2000,"/items/apple":1000,"/items/cotton":6000,"/items/flax":6000,"/items/verdant_shears":12,"/items/azure_shears":12}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/blueberry":4000,"/items/blackberry":4000,"/items/flax":6000,"/items/bamboo_branch":6000,"/items/azure_shears":15,"/items/burble_shears":15,"/items/foraging_tea":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/blackberry":8000,"/items/strawberry":8000,"/items/bamboo_branch":24000,"/items/burble_shears":18,"/items/crimson_shears":18}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/strawberry":16000,"/items/mooberry":16000,"/items/bamboo_branch":24000,"/items/cocoon":24000,"/items/crimson_shears":21,"/items/rainbow_shears":21}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/mooberry":32000,"/items/marsberry":32000,"/items/cocoon":48000,"/items/radiant_fiber":48000,"/items/rainbow_shears":24,"/items/holy_shears":24}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/marsberry":80000,"/items/radiant_fiber":120000,"/items/holy_shears":60,"/items/super_foraging_tea":2000}}},"Armory":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/cheese_plate_body":8,"/items/cheese_plate_legs":8,"/items/cheese_gauntlets":8,"/items/cheese_boots":8}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/cheese_plate_body":16,"/items/verdant_plate_body":16,"/items/cheese_plate_legs":16,"/items/verdant_plate_legs":16,"/items/cheese_gauntlets":16,"/items/verdant_gauntlets":16,"/items/cheese_boots":16,"/items/verdant_boots":16}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/verdant_plate_body":20,"/items/azure_plate_body":20,"/items/verdant_plate_legs":20,"/items/azure_plate_legs":20,"/items/verdant_gauntlets":20,"/items/azure_gauntlets":20,"/items/verdant_boots":20,"/items/azure_boots":20,"/items/defense_coffee":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/azure_plate_body":24,"/items/burble_plate_body":24,"/items/azure_plate_legs":24,"/items/burble_plate_legs":24,"/items/azure_gauntlets":24,"/items/burble_gauntlets":24,"/items/azure_boots":24,"/items/burble_boots":24}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/burble_plate_body":28,"/items/crimson_plate_body":28,"/items/burble_plate_legs":28,"/items/crimson_plate_legs":28,"/items/burble_gauntlets":28,"/items/crimson_gauntlets":28,"/items/burble_boots":28,"/items/crimson_boots":28}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/crimson_plate_body":32,"/items/rainbow_plate_body":32,"/items/crimson_plate_legs":32,"/items/rainbow_plate_legs":32,"/items/crimson_gauntlets":32,"/items/rainbow_gauntlets":32,"/items/crimson_boots":32,"/items/rainbow_boots":32}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/holy_plate_body":80,"/items/holy_plate_legs":80,"/items/holy_gauntlets":80,"/items/holy_boots":80,"/items/super_defense_coffee":2000}}},"Gym":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/cheese_mace":16,"/items/cheese_sword":8}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/cheese_mace":32,"/items/verdant_mace":32,"/items/cheese_sword":16,"/items/verdant_sword":16}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/verdant_mace":40,"/items/azure_mace":40,"/items/verdant_sword":20,"/items/azure_sword":20,"/items/power_coffee":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/azure_mace":48,"/items/burble_mace":48,"/items/azure_sword":24,"/items/burble_sword":24}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/burble_mace":56,"/items/crimson_mace":56,"/items/burble_sword":28,"/items/crimson_sword":28}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/crimson_mace":64,"/items/rainbow_mace":64,"/items/crimson_sword":32,"/items/rainbow_sword":32}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/holy_mace":160,"/items/holy_sword":80,"/items/super_power_coffee":2000}}},"Archery Range":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/wooden_crossbow":6,"/items/wooden_bow":4,"/items/rough_tunic":4,"/items/rough_chaps":4,"/items/rough_bracers":4,"/items/rough_boots":4}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/birch_crossbow":9,"/items/birch_bow":6,"/items/reptile_tunic":8,"/items/reptile_chaps":8,"/items/reptile_bracers":8,"/items/reptile_boots":8}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/cedar_crossbow":12,"/items/cedar_bow":8,"/items/reptile_tunic":16,"/items/gobo_tunic":16,"/items/reptile_chaps":16,"/items/gobo_chaps":16,"/items/reptile_bracers":16,"/items/gobo_bracers":16,"/items/reptile_boots":16,"/items/gobo_boots":16,"/items/ranged_coffee":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/purpleheart_crossbow":15,"/items/purpleheart_bow":10,"/items/gobo_tunic":32,"/items/gobo_chaps":32,"/items/gobo_bracers":32,"/items/gobo_boots":32}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/ginkgo_crossbow":18,"/items/ginkgo_bow":12,"/items/gobo_tunic":32,"/items/beast_tunic":32,"/items/gobo_chaps":32,"/items/beast_chaps":32,"/items/gobo_bracers":32,"/items/beast_bracers":32,"/items/gobo_boots":32,"/items/beast_boots":32}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/redwood_crossbow":21,"/items/redwood_bow":14,"/items/beast_tunic":48,"/items/beast_chaps":48,"/items/beast_bracers":48,"/items/beast_boots":48}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/arcane_crossbow":60,"/items/arcane_bow":40,"/items/umbral_tunic":40,"/items/umbral_chaps":40,"/items/umbral_bracers":40,"/items/umbral_boots":40,"/items/super_ranged_coffee":2000}}},"Mystical Study":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/wooden_water_staff":4,"/items/wooden_nature_staff":4,"/items/wooden_fire_staff":4,"/items/cotton_hat":4,"/items/cotton_robe_top":4,"/items/cotton_robe_bottoms":4,"/items/cotton_gloves":4,"/items/cotton_boots":4}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/birch_water_staff":6,"/items/birch_nature_staff":6,"/items/birch_fire_staff":6,"/items/linen_hat":8,"/items/linen_robe_top":8,"/items/linen_robe_bottoms":8,"/items/linen_gloves":8,"/items/linen_boots":8}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/cedar_water_staff":10,"/items/purpleheart_water_staff":10,"/items/cedar_nature_staff":10,"/items/purpleheart_nature_staff":10,"/items/cedar_fire_staff":10,"/items/purpleheart_fire_staff":10,"/items/bamboo_hat":16,"/items/bamboo_robe_top":16,"/items/bamboo_robe_bottoms":16,"/items/bamboo_gloves":16,"/items/bamboo_boots":16,"/items/magic_coffee":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/purpleheart_water_staff":12,"/items/ginkgo_water_staff":12,"/items/purpleheart_nature_staff":12,"/items/ginkgo_nature_staff":12,"/items/purpleheart_fire_staff":12,"/items/ginkgo_fire_staff":12,"/items/bamboo_hat":32,"/items/bamboo_robe_top":32,"/items/bamboo_robe_bottoms":32,"/items/bamboo_gloves":32,"/items/bamboo_boots":32}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/ginkgo_water_staff":14,"/items/redwood_water_staff":14,"/items/ginkgo_nature_staff":14,"/items/redwood_nature_staff":14,"/items/ginkgo_fire_staff":14,"/items/redwood_fire_staff":14,"/items/silk_hat":24,"/items/silk_robe_top":24,"/items/silk_robe_bottoms":24,"/items/silk_gloves":24,"/items/silk_boots":24}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/redwood_water_staff":16,"/items/arcane_water_staff":16,"/items/redwood_nature_staff":16,"/items/arcane_nature_staff":16,"/items/redwood_fire_staff":16,"/items/arcane_fire_staff":16,"/items/silk_hat":48,"/items/silk_robe_top":48,"/items/silk_robe_bottoms":48,"/items/silk_gloves":48,"/items/silk_boots":48}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/arcane_water_staff":40,"/items/arcane_nature_staff":40,"/items/arcane_fire_staff":40,"/items/radiant_hat":40,"/items/radiant_robe_top":40,"/items/radiant_robe_bottoms":40,"/items/radiant_gloves":40,"/items/radiant_boots":40,"/items/super_magic_coffee":2000}}},"Library":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/gummy":250,"/items/yogurt":250,"/items/poke":2,"/items/smack":2,"/items/quick_shot":2,"/items/water_strike":2,"/items/entangle":2,"/items/fireball":2}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/apple_gummy":500,"/items/apple_yogurt":500,"/items/scratch":2,"/items/toughness":2,"/items/precision":2,"/items/sweep":2}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/orange_gummy":1000,"/items/orange_yogurt":1000,"/items/impale":2,"/items/cleave":2,"/items/intelligence_coffee":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/plum_gummy":2000,"/items/plum_yogurt":2000,"/items/rain_of_arrows":2,"/items/ice_spear":2,"/items/flame_blast":2,"/items/toxic_pollen":2}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/peach_gummy":4000,"/items/peach_yogurt":4000,"/items/berserk":2,"/items/frenzy":2,"/items/elemental_affinity":2}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/dragon_fruit_gummy":8000,"/items/dragon_fruit_yogurt":8000,"/items/puncture":2,"/items/maim":2,"/items/stunning_blow":2}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/star_fruit_gummy":20000,"/items/star_fruit_yogurt":20000,"/items/silencing_shot":2,"/items/frost_surge":2,"/items/natures_veil":"Nature's Veil","/items/firestorm":2,"/items/super_intelligence_coffee":2000}}},"Dojo":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/cheese_spear":16,"/items/cheese_sword":8}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/cheese_spear":32,"/items/verdant_spear":32,"/items/cheese_sword":16,"/items/verdant_sword":16}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/verdant_spear":40,"/items/azure_spear":40,"/items/verdant_sword":20,"/items/azure_sword":20,"/items/attack_coffee":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/azure_spear":48,"/items/burble_spear":48,"/items/azure_sword":24,"/items/burble_sword":24}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/burble_spear":56,"/items/crimson_spear":56,"/items/burble_sword":28,"/items/crimson_sword":28}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/crimson_spear":64,"/items/rainbow_spear":64,"/items/crimson_sword":32,"/items/rainbow_sword":32}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/holy_spear":160,"/items/holy_sword":80,"/items/super_attack_coffee":2000}}},"Dining Room":{"2":{"gold":2000000,"materials":{"/items/lumber":150,"/items/birch_lumber":150,"/items/donut":125,"/items/cupcake":125,"/items/small_pouch":3}},"3":{"gold":5000000,"materials":{"/items/birch_lumber":300,"/items/cedar_lumber":300,"/items/blueberry_donut":250,"/items/blueberry_cake":250,"/items/medium_pouch":1}},"4":{"gold":12000000,"materials":{"/items/cedar_lumber":600,"/items/purpleheart_lumber":600,"/items/blackberry_donut":500,"/items/blackberry_cake":500,"/items/medium_pouch":3,"/items/stamina_coffee":1000}},"5":{"gold":25000000,"materials":{"/items/purpleheart_lumber":1200,"/items/ginkgo_lumber":1200,"/items/strawberry_donut":1000,"/items/strawberry_cake":1000,"/items/large_pouch":1}},"6":{"gold":50000000,"materials":{"/items/ginkgo_lumber":2400,"/items/redwood_lumber":2400,"/items/mooberry_donut":2000,"/items/mooberry_cake":2000,"/items/large_pouch":3}},"7":{"gold":90000000,"materials":{"/items/redwood_lumber":4800,"/items/arcane_lumber":4800,"/items/marsberry_donut":4000,"/items/marsberry_cake":4000,"/items/giant_pouch":1}},"8":{"gold":160000000,"materials":{"/items/arcane_lumber":12000,"/items/spaceberry_donut":10000,"/items/spaceberry_cake":10000,"/items/giant_pouch":3,"/items/super_stamina_coffee":2000}}}};

    // --- 1. UI & STYLES ---
    const panel = document.createElement('div');
    panel.id = 'batch-panel';
    panel.innerHTML = `
        <div id="batch-header" title="Jigglymoose's Intelligent Gear Simulator"><span>JIGS</span><button id="batch-toggle">-</button></div>
        <div id="batch-content">
            <div id="controls-grid">
                <div id="run-stop-container">
                    <button id="run-batch-button" disabled>Run Simulations</button>
                    <button id="stop-batch-button" style="display: none;">Stop</button>
                </div>
                <button id="capture-setup-button" disabled>Capture Setup</button>
                <button id="update-baseline-button" disabled>Update Baseline</button>
                <span id="baseline-display">Baseline DPS: --</span>
                <button id="export-csv-button" disabled>Export to CSV</button>
                <button id="reset-button" disabled>Reset to Baseline</button>
            </div>
            <div id="batch-status">Status: Please import a character...</div>
            <div id="jigs-progress-container" style="display: none;">
                <div id="jigs-progress-bar"></div>
            </div>
            <div id="batch-inputs-container">
                <details id="sim-settings-group" open><summary>Simulation Settings (Constant for Batch)</summary></details>
                <details id="skills-group" open><summary>Skills</summary></details>
                <details id="equipment-group" open><summary>Equipment</summary></details>
                <details id="abilities-group" open><summary>Abilities</summary></details>
                <details id="food-drink-group" open><summary>Food & Drink</summary></details>
                <details id="house-group" open><summary>House</summary><div id="house-grid-container"></div></details>
            </div>
            <div id="batch-results-container">
                <table id="batch-results-table">
                    <thead>
                        <tr>
                            <th data-sort-key="upgrade" title="The specific upgrade being tested">Upgrade</th>
                            <th data-sort-key="cost" title="The total cost of the upgrade in gold, based on market prices">Upgrade Cost</th>
                            <th data-sort-key="dpsChange" title="The raw DPS increase (+) or decrease (-) from this single change">DPS Change</th>
                            <th data-sort-key="percentChange" title="The percentage of DPS gained from this single change">% Change</th>
                            <th data-sort-key="costPerDps" title="Lower is better! This shows the gold cost for every 0.01% increase in total DPS.">Gold per 0.01% DPS</th>
                        </tr>
                    </thead>
                    <tbody></tbody>
                </table>
            </div>
        </div>
    `;
    document.body.appendChild(panel);

    GM_addStyle(`
        #batch-panel { position: fixed; bottom: 10px; right: 10px; width: 550px; max-height: 90vh; background-color: #2c2c2c; border: 1px solid #444; border-radius: 5px; color: #eee; z-index: 9999; font-family: sans-serif; display: flex; flex-direction: column; }
        #batch-header { background-color: #333; padding: 8px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #444; }
        #batch-header span { font-weight: bold; }
        #batch-toggle { background: #555; border: 1px solid #777; color: white; border-radius: 3px; cursor: pointer; margin-left: 5px; }
        #batch-content { padding: 10px; display: flex; flex-direction: column; overflow-y: auto; position: relative; }
        #batch-content.hidden { display: none; }
        #controls-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 10px; }
        #run-stop-container { grid-column: 1 / 2; display: contents; }
        #stop-batch-button { background-color: #c9302c; }
        #controls-grid button { width: 100%; padding: 8px; color: white; border: none; border-radius: 4px; cursor: pointer; }
        #baseline-display { grid-column: 1 / 2; text-align: center; align-self: center; font-size: 0.9em; color: #ccc; }
        #run-batch-button { background-color: #4CAF50; }
        #capture-setup-button { background-color: #337ab7; }
        #update-baseline-button { background-color: #f44336; }
        #export-csv-button { background-color: #5bc0de; }
        #reset-button { background-color: #f0ad4e; }
        #run-batch-button:disabled, #capture-setup-button:disabled, #update-baseline-button:disabled, #export-csv-button:disabled, #reset-button:disabled { background-color: #555; cursor: not-allowed; }
        #batch-status { margin-bottom: 5px; font-style: italic; color: #aaa; text-align: center; }
        #jigs-progress-container { width: 100%; background-color: #555; border-radius: 5px; height: 10px; margin-bottom: 10px; border: 1px solid #333; }
        #jigs-progress-bar { width: 0%; height: 100%; background-color: #4CAF50; border-radius: 5px; transition: width 0.1s linear; }
        #batch-inputs-container { display: flex; flex-direction: column; gap: 5px; max-height: 40vh; overflow-y: auto; border: 1px solid #444; padding: 10px; margin-bottom: 10px; }
        summary { font-weight: bold; cursor: pointer; padding: 4px; background-color: #333; margin-bottom: 5px; }
        #sim-settings-group summary { font-size: 0.9em; }
        details { border-left: 1px solid #444; padding-left: 10px; margin-bottom: 5px;}
        .batch-input-row { display: grid; grid-template-columns: 100px 1fr; align-items: center; margin-bottom: 5px; gap: 5px; }
        .batch-input-row-equip, .batch-input-row-ability { display: grid; grid-template-columns: 60px 1fr 80px; align-items: center; margin-bottom: 5px; gap: 5px; }
        .batch-input-row label, .batch-input-row-equip label, .batch-input-row-ability label { font-size: 0.9em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .batch-input-row select, .batch-input-row input, .batch-input-row-equip select, .batch-input-row-equip input, .batch-input-row-ability select, .batch-input-row-ability input { background-color: #1e1e1e; color: #ddd; border: 1px solid #555; width: 100%; box-sizing: border-box; }
        #house-grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
        .house-grid-item { display: flex; flex-direction: column; align-items: center; }
        .house-grid-item label { font-size: 0.8em; margin-bottom: 2px; }
        .house-grid-item input { width: 90%; text-align: center; }
        #batch-results-container { margin-top: 10px; max-height: 30vh; overflow-y: auto; }
        #batch-results-table { width: 100%; border-collapse: collapse; }
        #batch-results-table th, #batch-results-table td { border: 1px solid #444; padding: 5px; text-align: left; font-size: 0.9em; }
        #batch-results-table th { background-color: #333; cursor: pointer; }
        #batch-results-table th:hover { background-color: #444; }
        .sorted-asc::after { content: ' ▲'; }
        .sorted-desc::after { content: ' ▼'; }
    `);

    // --- 2. GET ELEMENTS & DEFINE DATA ---
    const statusDiv = document.getElementById('batch-status');
    const groupContainers = { skills: document.querySelector('#skills-group'), house: document.querySelector('#house-grid-container'), abilities: document.querySelector('#abilities-group'), equipment: document.querySelector('#equipment-group'), food: document.querySelector('#food-drink-group'), sim: document.querySelector('#sim-settings-group'), };

    let baselineDps = 0;
    let marketData = null;
    let isBatchRunning = false;
    let itemIdToNameMap = {};
    let detailedResults = [];
    const skillKeywords = ["Combat", "Stamina", "Intelligence", "Attack", "Melee", "Defense", "Ranged", "Magic"];
    const equipmentKeywords = ["Head", "Necklace", "Earrings", "Body", "Legs", "Feet", "Hands", "Ring", "Main Hand", "Off Hand", "Pouch", "Back", "Charm"];
    const specialIdMap = { 'Select Zone': 'selectZone', 'Difficulty': 'selectDifficulty', 'Duration': 'inputSimulationTime' };
    let houseKeywords = [];

    // --- 3. HELPER FUNCTIONS ---
    function createNumberInput(name, value, min, max, isHouse = false) { const container = document.createElement('div'); container.className = isHouse ? 'house-grid-item' : 'batch-input-row'; const label = document.createElement('label'); label.textContent = name; label.title = name; const input = document.createElement('input'); input.type = 'number'; input.value = value; input.min = min ?? 1; input.max = max ?? 400; input.dataset.originalValue = value; input.dataset.name = name; container.appendChild(label); container.appendChild(input); return container; }
    function createSelect(name, value, options) { const row = document.createElement('div'); row.className = 'batch-input-row'; const label = document.createElement('label'); label.textContent = name; label.title = name; const select = document.createElement('select'); select.dataset.originalValue = value; select.dataset.name = name; options.forEach(opt => { const option = document.createElement('option'); option.value = opt; option.textContent = opt; select.appendChild(option); }); select.value = value; row.appendChild(label); row.appendChild(select); return row; }
    function createEquipmentRow(name, itemValue, itemOptions, enhValue) { const row = document.createElement('div'); row.className = 'batch-input-row-equip'; const label = document.createElement('label'); label.textContent = name; label.title = name; const itemSelect = document.createElement('select'); itemSelect.dataset.originalValue = itemValue; itemSelect.dataset.name = name; itemOptions.forEach(opt => { const option = document.createElement('option'); option.value = opt; option.textContent = opt; itemSelect.appendChild(option); }); itemSelect.value = itemValue; const enhInput = document.createElement('input'); enhInput.type = 'number'; enhInput.value = enhValue; enhInput.min = 0; enhInput.max = 20; enhInput.dataset.originalValue = enhValue; enhInput.dataset.name = `${name} Enhancement`; row.appendChild(label); row.appendChild(itemSelect); row.appendChild(enhInput); return row; }
    function createAbilityRow(index, itemValue, itemOptions, lvlValue) { const row = document.createElement('div'); row.className = 'batch-input-row-ability'; const name = `Ability ${index + 1}`; const label = document.createElement('label'); label.textContent = name; label.title = name; const itemSelect = document.createElement('select'); itemSelect.dataset.originalValue = itemValue; itemSelect.dataset.name = name; itemOptions.forEach(opt => { if (opt !== 'Promote') { const option = document.createElement('option'); option.value = opt; option.textContent = opt; itemSelect.appendChild(option); } }); itemSelect.value = itemValue; const lvlInput = document.createElement('input'); lvlInput.type = 'number'; lvlInput.value = lvlValue; lvlInput.min = 1; lvlInput.max = 200; lvlInput.dataset.originalValue = lvlValue; lvlInput.dataset.name = `${name} Level`; row.appendChild(label); row.appendChild(itemSelect); row.appendChild(lvlInput); return row; }
    function findPageElementByName(name, tag = 'input, select') { if (specialIdMap[name]) { return document.getElementById(specialIdMap[name]); } const labels = Array.from(document.querySelectorAll('label')); const targetLabel = labels.find(l => l.textContent.trim().toLowerCase() === name.toLowerCase()); if (!targetLabel) return null; const parentRow = targetLabel.closest('.row'); if (parentRow) { return parentRow.querySelector(tag); } return null; }
    function getDpsValue() { const resultsContainer = document.getElementById('simulationResultTotalDamageDone'); if (!resultsContainer || !resultsContainer.hasChildNodes()) { return null; } const totalLabelElement = Array.from(resultsContainer.querySelectorAll('div.col-md-5')).find(el => el.textContent.trim() === 'Total'); if (!totalLabelElement) { return null; } const dpsElement = totalLabelElement.nextElementSibling?.nextElementSibling; if (dpsElement) { return dpsElement.textContent.trim(); } return null; }
    function addResultRow(result) { const resultsTbody = document.querySelector('#batch-results-table tbody'); const row = resultsTbody.insertRow(); let costText = formatGold(result.cost); let costPerDpsText = formatGold(result.costPerDps); if (result.cost === Infinity) { costText = 'No Seller'; costPerDpsText = 'N/A'; } let upgradeText = result.upgrade; if (result.books > 0) { upgradeText += ` (${result.books.toLocaleString()} books)`; } row.dataset.upgrade = result.upgrade; row.dataset.cost = isFinite(result.cost) ? result.cost : Infinity; row.dataset.dpsChange = result.dps; row.dataset.percentChange = result.percent; row.dataset.costPerDps = isFinite(result.costPerDps) ? result.costPerDps : Infinity; row.innerHTML = `<td>${upgradeText}</td><td>${costText}</td><td>${result.dps > 0 ? '+' : ''}${result.dps.toFixed(2)}</td><td>${result.percent.toFixed(2)}%</td><td>${costPerDpsText}</td>`; }
    function formatGold(value) { if (value === 'N/A' || value === 'Free') return value; if (!isFinite(value) || value === Infinity) return "N/A"; if (value < 1000) return Math.round(value).toLocaleString(); if (value < 1000000) return `${(value / 1000).toFixed(1)}k`; return `${(value / 1000000).toFixed(2)}M`; }

    async function fetchMarketData() { if (marketData) { return marketData; } return new Promise((resolve) => { GM_xmlhttpRequest({ method: "GET", url: MARKET_API_URL, onload: function(response) { if (response.status === 200) { try { const responseObject = JSON.parse(response.responseText); const rawMarketData = responseObject.marketData; if (typeof rawMarketData !== 'object' || rawMarketData === null) { resolve(null); return; } marketData = {}; for (const itemId in rawMarketData) { const itemName = itemIdToNameMap[itemId]; if (!itemName) continue; const itemEnhancements = rawMarketData[itemId]; for (const enhancementLevel in itemEnhancements) { const prices = itemEnhancements[enhancementLevel]; const fullName = enhancementLevel === "0" ? itemName : `${itemName} +${enhancementLevel}`; marketData[fullName] = { buyer: prices.b, seller: prices.a }; } } statusDiv.textContent = 'Status: Market data loaded.'; resolve(marketData); } catch (e) { resolve(null); } } else { resolve(null); } }, onerror: function() { resolve(null); } }); }); }
    async function runSimulation(progressCallback) { return new Promise((resolve) => { let timeout, progressWatcher, dpsWatcher; const cleanup = () => { clearTimeout(timeout); clearInterval(progressWatcher); clearInterval(dpsWatcher); }; timeout = setTimeout(() => { cleanup(); resolve(NaN); }, 120000); const setupButton = document.getElementById('buttonSimulationSetup'); if (!setupButton) { cleanup(); resolve(NaN); return; } setupButton.click(); setTimeout(() => { const startButton = document.getElementById('buttonStartSimulation'); if (!startButton) { cleanup(); resolve(NaN); return; } const resultsContainer = document.getElementById('simulationResultTotalDamageDone'); if (resultsContainer) resultsContainer.innerHTML = ''; startButton.click(); progressWatcher = setInterval(() => { const progressBar = document.getElementById('simulationProgressBar'); if (progressBar) { const progress = parseInt(progressBar.textContent) || 0; if (progressCallback) progressCallback(progress); } if (progressBar && progressBar.textContent.includes('100%')) { clearInterval(progressWatcher); dpsWatcher = setInterval(() => { const dpsVal = getDpsValue(); if (dpsVal) { cleanup(); resolve(parseFloat(dpsVal.replace(/,/g, ''))); } }, 100); } }, 200); }, 300); }); }
    async function runSimulationMultiple(multiplier, progressCallback) { let totalDps = 0; let successfulRuns = 0; let allDpsResults = []; for (let i = 0; i < multiplier; i++) { if (!isBatchRunning) break; const singleRunProgress = (progress) => { if (progressCallback) { const overallProgress = ((i * 100) + progress) / multiplier; progressCallback(overallProgress); } }; const dps = await runSimulation(singleRunProgress); if (!isNaN(dps)) { totalDps += dps; successfulRuns++; allDpsResults.push(dps); } else { console.error(`JIGS: Simulation run ${i + 1} of ${multiplier} failed.`); } } if (successfulRuns === 0) { return { averageDps: NaN, individualRuns: allDpsResults }; } return { averageDps: totalDps / successfulRuns, individualRuns: allDpsResults }; }

    function setRunningState(isRunning) {
        isBatchRunning = isRunning;
        document.getElementById('run-batch-button').style.display = isRunning ? 'none' : 'block';
        document.getElementById('stop-batch-button').style.display = isRunning ? 'block' : 'none';
        document.getElementById('capture-setup-button').disabled = isRunning;
        document.getElementById('update-baseline-button').disabled = isRunning;
        document.getElementById('export-csv-button').disabled = isRunning;
        document.getElementById('reset-button').disabled = isRunning;

        if (!isRunning) {
            document.getElementById('jigs-progress-container').style.display = 'none';
        }
    }

    function exportResultsToCSV() {
        if (detailedResults.length === 0) {
            alert('No results to export. Please run a simulation first.');
            return;
        }

        let csvContent = "";
        const maxRuns = Math.max(...detailedResults.map(r => r.individualRuns.length));

        let headers = ["Upgrade", "Cost", "DPS Change", "% Change", "Gold per 0.01% DPS", "Books Needed", "Average DPS"];
        for(let i=1; i<=maxRuns; i++) { headers.push(`Run ${i}`); }
        csvContent += headers.join(',') + "\r\n";

        detailedResults.forEach(result => {
            let row = [
                `"${result.upgrade.replace(/"/g, '""')}"`,
                isFinite(result.cost) ? result.cost : '"N/A"',
                result.dps.toFixed(2),
                result.percent.toFixed(2),
                isFinite(result.costPerDps) ? result.costPerDps : '"N/A"',
                result.books,
                result.averageDps.toFixed(2)
            ];
            for(let i=0; i<maxRuns; i++) {
                row.push(result.individualRuns[i] ? result.individualRuns[i].toFixed(2) : '');
            }
            csvContent += row.join(',') + "\r\n";
        });

        const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement("a");
        const url = URL.createObjectURL(blob);
        link.setAttribute("href", url);
        link.setAttribute("download", "jigs_results.csv");
        link.style.visibility = 'hidden';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }

    function resetInputsToBaseline() {
        document.querySelectorAll('#batch-inputs-container input, #batch-inputs-container select').forEach(el => {
            if (el.dataset.originalValue !== undefined) {
                el.value = el.dataset.originalValue;
            }
        });
        statusDiv.textContent = 'Status: All inputs reset to baseline.';
    }

    // --- 4. CORE LOGIC ---
    async function buildInputsUI() {
        const runButton = document.getElementById('run-batch-button');
        const captureSetupButton = document.getElementById('capture-setup-button');
        const updateBaselineButton = document.getElementById('update-baseline-button');
        const resetButton = document.getElementById('reset-button');

        statusDiv.textContent = 'Status: Reading data from page...';
        runButton.disabled = true; captureSetupButton.disabled = true; updateBaselineButton.disabled = true; resetButton.disabled = true;
        const currentMultiplier = document.querySelector('#sim-settings-group [data-name="Multiplier"]')?.value || 1;
        Object.values(groupContainers).forEach(c => { if(c.id !== 'house-grid-container') c.innerHTML = `<summary>${c.querySelector('summary').textContent}</summary>`; else c.innerHTML = ''; });
        let itemsFound = 0;
        houseKeywords = [];
        itemIdToNameMap = {...BASIC_MATERIALS_MAP};
        document.querySelectorAll('select[id^="selectEquipment_"], select[id^="selectAbility_"]').forEach(selectEl => {
            Array.from(selectEl.options).forEach(opt => { if(opt.value && opt.text) itemIdToNameMap[opt.value] = opt.text; });
        });

        skillKeywords.forEach(name => { const pageEl = findPageElementByName(name); if (pageEl) { groupContainers.skills.appendChild(createNumberInput(name, pageEl.value, pageEl.min, pageEl.max)); itemsFound++; } });
        equipmentKeywords.forEach(name => { const itemSelect = findPageElementByName(name, 'select'); const enhInput = findPageElementByName(name, 'input'); if (itemSelect && enhInput) { const itemValue = itemSelect.options[itemSelect.selectedIndex].text; const itemOptions = Array.from(itemSelect.options).map(opt => opt.text); const enhValue = enhInput.value; groupContainers.equipment.appendChild(createEquipmentRow(name, itemValue, itemOptions, enhValue)); itemsFound++; } });
        for (let i = 0; i < 5; i++) { const abilitySelect = document.getElementById(`selectAbility_${i}`); const levelInput = document.getElementById(`inputAbilityLevel_${i}`); if (abilitySelect && levelInput) { const itemValue = abilitySelect.options[abilitySelect.selectedIndex].text; const itemOptions = Array.from(abilitySelect.options).map(opt => opt.text); const lvlValue = levelInput.value; groupContainers.abilities.appendChild(createAbilityRow(i, itemValue, itemOptions, lvlValue)); itemsFound++; } }
        document.querySelectorAll('select[id^="selectFood_"], select[id^="selectDrink_"]').forEach((el, i) => { const type = el.id.includes('Food') ? 'Food' : 'Drink'; const name = `${type} ${i % 3 + 1}`; const currentValue = el.options[el.selectedIndex].text; const options = Array.from(el.options).map(opt => opt.text); groupContainers.food.appendChild(createSelect(name, currentValue, options)); itemsFound++; });
        document.querySelectorAll('#houseRoomsList .row').forEach(row => { const labelEl = row.querySelector('div[data-i18n]'); const inputEl = row.querySelector('input'); if (labelEl && inputEl) { const name = labelEl.textContent.trim(); houseKeywords.push(name); groupContainers.house.appendChild(createNumberInput(name, inputEl.value, inputEl.min, inputEl.max, true)); itemsFound++; } });
        for (const name of Object.keys(specialIdMap)) {
            const pageEl = findPageElementByName(name);
            if (pageEl) {
                if (pageEl.tagName === 'SELECT') { const currentValue = pageEl.options[pageEl.selectedIndex].text; const options = Array.from(pageEl.options).map(opt => opt.text); groupContainers.sim.appendChild(createSelect(name, currentValue, options)); }
                else { groupContainers.sim.appendChild(createNumberInput(name, pageEl.value, pageEl.min, pageEl.max)); }
                itemsFound++;
            }
        }
        groupContainers.sim.appendChild(createNumberInput('Multiplier', currentMultiplier, 1, 100));
        if (itemsFound > 0) { statusDiv.textContent = 'Status: Idle.'; runButton.disabled = false; updateBaselineButton.disabled = false; resetButton.disabled = false;}
        else { statusDiv.textContent = 'Status: No data found. Import or use Capture Setup.'; }
        captureSetupButton.disabled = false;
    }

    async function updateBaseline() {
        setRunningState(true);
        const jigsProgressContainer = document.getElementById('jigs-progress-container');
        const jigsProgressBar = document.getElementById('jigs-progress-bar');
        statusDiv.textContent = 'Status: Applying settings and updating baseline...';
        jigsProgressContainer.style.display = 'block';
        jigsProgressBar.style.width = '0%';
        try {
            const simSettings = document.querySelectorAll('#sim-settings-group select, #sim-settings-group input');
            simSettings.forEach(uiEl => { if (uiEl.value !== uiEl.dataset.originalValue) { const pageEl = findPageElementByName(uiEl.dataset.name); if (pageEl) { if (pageEl.tagName === 'SELECT') { const opt = Array.from(pageEl.options).find(o => o.text === uiEl.value); if (opt) pageEl.value = opt.value; } else { pageEl.value = uiEl.value; } pageEl.dispatchEvent(new Event('change', { bubbles: true })); pageEl.dispatchEvent(new Event('input', { bubbles: true })); } } });
            const multiplier = parseInt(document.querySelector('#sim-settings-group [data-name="Multiplier"]').value) || 1;
            const simResult = await runSimulationMultiple(multiplier, progress => { jigsProgressBar.style.width = `${progress}%`; });
            const newBaseline = simResult.averageDps;
            if (!isNaN(newBaseline)) { baselineDps = newBaseline; document.getElementById('baseline-display').textContent = `Baseline DPS: ${baselineDps.toFixed(2)}`; simSettings.forEach(uiEl => { uiEl.dataset.originalValue = uiEl.value; }); statusDiv.textContent = 'Status: Baseline updated.'; }
            else if (isBatchRunning) { statusDiv.textContent = 'Error: Failed to update baseline. Try again.'; }
        } finally {
            setRunningState(false);
        }
    }

    async function startBatch() {
        let allPageInputs = new Map();
        setRunningState(true);
        try {
            await fetchMarketData();
            if (!isBatchRunning) { statusDiv.textContent = 'Status: Stopped by user.'; return; }
            if (!marketData) { return; }
            if (baselineDps === 0) { statusDiv.textContent = 'Error: Please set a baseline first.'; return; }

            const jigsProgressContainer = document.getElementById('jigs-progress-container');
            const jigsProgressBar = document.getElementById('jigs-progress-bar');
            jigsProgressContainer.style.display = 'block';
            jigsProgressBar.style.width = '0%';
            document.querySelector('#batch-results-table tbody').innerHTML = '';
            detailedResults = [];
            document.getElementById('export-csv-button').disabled = true;

            let allChanges = {};
            document.querySelectorAll('#batch-inputs-container input, #batch-inputs-container select').forEach(el => { if (!el.disabled && el.value !== el.dataset.originalValue) { allChanges[el.dataset.name] = { value: el.value, originalValue: el.dataset.originalValue }; } });

            const upgrades = []; const processed = new Set();
            for (const name in allChanges) {
                if (processed.has(name)) continue;
                let combinedUpgrade = { name, ...allChanges[name] };
                let enhName = `${name} Enhancement`; let lvlName = `${name} Level`;
                if (allChanges[enhName]) { combinedUpgrade.enhancement = allChanges[enhName]; processed.add(enhName); }
                else if (allChanges[lvlName]) { combinedUpgrade.level = allChanges[lvlName]; processed.add(lvlName); }
                else if (name.includes('Enhancement')) { let base = name.replace(' Enhancement', ''); if (!allChanges[base]) { combinedUpgrade = { name: base, isEnhancementOnly: true, enhancement: allChanges[name] }; } }
                else if (name.includes('Level')) { let base = name.replace(' Level', ''); if (!allChanges[base]) { combinedUpgrade = { name: base, isLevelOnly: true, level: allChanges[name] }; } }
                upgrades.push(combinedUpgrade);
                processed.add(name);
            }

            const settingChanges = upgrades.filter(u => specialIdMap[u.name] || u.name === 'Multiplier');
            const upgradeChanges = upgrades.filter(u => !settingChanges.includes(u));
            if (upgradeChanges.length === 0 && settingChanges.length === 0) { statusDiv.textContent = 'Error: No changes.'; return; }

            let simsCompleted = 0; let totalSims = upgradeChanges.length + (settingChanges.length > 0 ? 1 : 0);
            let currentBaselineDps = baselineDps;

            const updateOverallProgress = (simIndex, currentSimProgress) => { const totalProgress = ((simIndex + (currentSimProgress / 100)) / totalSims) * 100; jigsProgressBar.style.width = `${totalProgress}%`; };

            document.querySelectorAll('#batch-inputs-container input, #batch-inputs-container select').forEach(uiEl => {
                const name = uiEl.dataset.name.replace(' Enhancement', '').replace(' Level', '').trim();
                const tag = uiEl.dataset.name.includes('Enhancement') || uiEl.dataset.name.includes('Level') ? 'input' : 'select, input';
                let el;
                if (uiEl.dataset.name.startsWith('Ability')) { const index = parseInt(uiEl.dataset.name.match(/\d+/)[0]) - 1; const idPart = uiEl.dataset.name.includes('Level') ? 'inputAbilityLevel' : 'selectAbility'; el = document.getElementById(`${idPart}_${index}`); }
                else if (uiEl.dataset.name.startsWith('Food') || uiEl.dataset.name.startsWith('Drink')) { const type = uiEl.dataset.name.startsWith('Food') ? 'Food' : 'Drink'; const index = parseInt(uiEl.dataset.name.slice(-1)) - 1; el = document.querySelector(`#select${type}_${index}`); }
                else if (houseKeywords.includes(name)) { const allRoomLabels = document.querySelectorAll('#houseRoomsList div[data-i18n]'); const targetLabel = Array.from(allRoomLabels).find(div => div.textContent.trim() === name); if (targetLabel) { el = targetLabel.closest('.row').querySelector('input'); } }
                else { el = findPageElementByName(name, tag); }
                if (el) { const selectedOption = Array.from(el.options || []).find(o => o.selected); const originalValue = uiEl.tagName === 'SELECT' ? (selectedOption ? selectedOption.text : '') : el.value; allPageInputs.set(uiEl.dataset.name, { el: el, originalValue: originalValue }); }
            });

            if (settingChanges.length > 0) {
                statusDiv.textContent = 'Status: Applying settings & running new baseline...';
                settingChanges.forEach(change => { const input = allPageInputs.get(change.name); if(input && input.el) { const el = input.el; if (el.tagName === 'SELECT') { const opt = Array.from(el.options).find(o => o.text === change.value); if (opt) el.value = opt.value; } else { el.value = change.value; } el.dispatchEvent(new Event('change', { bubbles: true })); } });
                const multiplier = parseInt(document.querySelector('#sim-settings-group [data-name="Multiplier"]').value) || 1;
                const simResult = await runSimulationMultiple(multiplier, progress => updateOverallProgress(simsCompleted, progress));
                const newBaseline = simResult.averageDps;
                if (!isBatchRunning) { statusDiv.textContent = 'Status: Stopped by user.'; return; }
                if (isNaN(newBaseline)) { statusDiv.textContent = 'Error: Could not get baseline for new settings.'; return; }
                currentBaselineDps = newBaseline; baselineDps = newBaseline; document.getElementById('baseline-display').textContent = `Baseline DPS: ${baselineDps.toFixed(2)}`;
                document.querySelectorAll('#sim-settings-group select, #sim-settings-group input').forEach(uiEl => { uiEl.dataset.originalValue = uiEl.value; });
                simsCompleted++;
            }

            if (upgradeChanges.length === 0) { statusDiv.textContent = 'Status: Settings updated.'; return; }

            const multiplier = parseInt(document.querySelector('#sim-settings-group [data-name="Multiplier"]').value) || 1;

            for (const upgrade of upgradeChanges) {
                if (!isBatchRunning) break;
                allPageInputs.forEach(input => { if (input.el && !specialIdMap[input.el.id]) { const el = input.el; if (el.tagName === 'SELECT') { const opt = Array.from(el.options).find(o => o.text === input.originalValue); if (opt) el.value = opt.value; } else { el.value = input.originalValue; } el.dispatchEvent(new Event('change', { bubbles: true })); } });
                await new Promise(r => setTimeout(r, 50));

                let upgradeLabelParts = [];
                if (upgrade.isEnhancementOnly) {
                    const enhChange = upgrade.enhancement;
                    const itemName = allPageInputs.get(upgrade.name)?.originalValue || upgrade.name;
                    upgradeLabelParts.push(`${itemName} Enhancement: ${enhChange.originalValue} -> ${enhChange.value}`);
                    const input = allPageInputs.get(enhChange.name);
                    if (input && input.el) { input.el.value = enhChange.value; input.el.dispatchEvent(new Event('change', { bubbles: true })); }
                } else if (upgrade.isLevelOnly) {
                    const lvlChange = upgrade.level;
                    const itemName = allPageInputs.get(upgrade.name)?.originalValue || upgrade.name;
                    upgradeLabelParts.push(`${itemName} Level: ${lvlChange.originalValue} -> ${lvlChange.value}`);
                    const input = allPageInputs.get(lvlChange.name);
                    if (input && input.el) { input.el.value = lvlChange.value; input.el.dispatchEvent(new Event('change', { bubbles: true })); }
                } else {
                    const mainInput = allPageInputs.get(upgrade.name);
                    if(mainInput && mainInput.el) { const el = mainInput.el; if (el.tagName === 'SELECT') { const opt = Array.from(el.options).find(o => o.text === upgrade.value); if (opt) el.value = opt.value; } else { el.value = upgrade.value; } el.dispatchEvent(new Event('change', { bubbles: true })); upgradeLabelParts.push(`${upgrade.name}: ${upgrade.originalValue} -> ${upgrade.value}`);}
                    if (upgrade.enhancement) { const enhInput = allPageInputs.get(`${upgrade.name} Enhancement`); if(enhInput && enhInput.el) { enhInput.el.value = upgrade.enhancement.value; enhInput.el.dispatchEvent(new Event('change', { bubbles: true })); } upgradeLabelParts.push(`Enhancement: ${upgrade.enhancement.originalValue} -> ${upgrade.enhancement.value}`);}
                    if (upgrade.level) { const lvlInput = allPageInputs.get(`${upgrade.name} Level`); if(lvlInput && lvlInput.el) { lvlInput.el.value = upgrade.level.value; lvlInput.el.dispatchEvent(new Event('change', { bubbles: true })); } upgradeLabelParts.push(`Level: ${upgrade.level.originalValue} -> ${upgrade.level.value}`);}
                }

                statusDiv.textContent = `Status: Simulating (${upgradeChanges.indexOf(upgrade)+1}/${upgradeChanges.length}): ${upgradeLabelParts.join(' & ')}`;
                const simResult = await runSimulationMultiple(multiplier, progress => updateOverallProgress(simsCompleted, progress));
                const newDps = simResult.averageDps;
                simsCompleted++;
                if (isNaN(newDps)) { console.error(`JIGS: Failed to get DPS for upgrade: ${upgrade.name}`); continue; }

                const dpsGain = newDps - currentBaselineDps;
                const percentChange = (dpsGain / currentBaselineDps) * 100;

                let cost = 0;
                let booksNeeded = 0;
                try {
                    if (houseKeywords.includes(upgrade.name)) {
                        const startLvl = parseInt(upgrade.originalValue);
                        const endLvl = parseInt(upgrade.value);
                        const roomRecipes = HOUSE_RECIPES[upgrade.name];
                        if (roomRecipes) {
                            for (let i = startLvl; i < endLvl; i++) {
                                const recipe = roomRecipes[i + 1];
                                if (recipe) {
                                    cost += recipe.gold;
                                    for (const materialId in recipe.materials) {
                                        const materialName = itemIdToNameMap[materialId];
                                        if (!materialName) { cost = Infinity; break; }
                                        const price = marketData[materialName]?.seller === -1 ? Infinity : marketData[materialName]?.seller || Infinity;
                                        cost += price * recipe.materials[materialId];
                                    }
                                }
                                if (!isFinite(cost)) break;
                            }
                        }
                    } else if (skillKeywords.includes(upgrade.name)) { cost = 0; }
                    else if (equipmentKeywords.includes(upgrade.name) || upgrade.isEnhancementOnly) {
                        const originalItemUI = document.querySelector(`#batch-inputs-container [data-name="${upgrade.name}"]`);
                        let oldSimName = upgrade.isEnhancementOnly ? originalItemUI.dataset.originalValue : upgrade.originalValue;
                        let newSimName = upgrade.value || originalItemUI.value;

                        const oldMarketName = SIMULATOR_TO_MARKET_MAP[oldSimName] || oldSimName;
                        const newMarketName = SIMULATOR_TO_MARKET_MAP[newSimName] || newSimName;

                        const oldEnhInput = allPageInputs.get(`${upgrade.name} Enhancement`);
                        const oldEnh = upgrade.enhancement ? upgrade.enhancement.originalValue : oldEnhInput?.originalValue || 0;
                        const newEnh = upgrade.enhancement?.value || oldEnhInput?.originalValue || 0;
                        const oldPriceRaw = marketData[`${oldMarketName} +${oldEnh}`]?.buyer;
                        const newPriceRaw = marketData[`${newMarketName} +${newEnh}`]?.seller;
                        const oldPrice = (oldPriceRaw === undefined || oldPriceRaw === -1) ? 0 : oldPriceRaw;
                        const newPrice = (newPriceRaw === undefined || newPriceRaw === -1) ? Infinity : newPriceRaw;
                        cost = newPrice - (oldMarketName === 'Empty' ? 0 : oldPrice);
                    } else if (upgrade.name.startsWith('Ability') || upgrade.isLevelOnly) {
                        const abilityName = upgrade.isLevelOnly ? allPageInputs.get(upgrade.name).originalValue : upgrade.value;
                        const marketItemName = abilityName; // Direct lookup, as the scraped name IS the market name
                        const xpPerBook = SPELL_BOOK_XP[marketItemName];

                        if (abilityName === 'Empty' || xpPerBook === undefined) {
                            cost = 0;
                        } else {
                            const priceOfThisBook = marketData[marketItemName]?.seller === -1 ? Infinity : marketData[marketItemName]?.seller || Infinity;
                            const lvlInput = allPageInputs.get(`${upgrade.name.replace(' Level', '')} Level`);
                            const startLvl = upgrade.level ? upgrade.level.originalValue : lvlInput?.originalValue;
                            const endLvl = upgrade.level?.value || lvlInput?.originalValue;

                            if(Number(endLvl) > Number(startLvl)) {
                                const startXp = ABILITY_XP_LEVELS[startLvl] || 0;
                                const endXp = ABILITY_XP_LEVELS[endLvl] || 0;
                                const xpNeeded = endXp - startXp;
                                booksNeeded = Math.ceil(xpNeeded / xpPerBook);
                                cost = booksNeeded * priceOfThisBook;
                            }
                        }
                    }
                } catch (e) { cost = 'N/A'; }
                const costPerPercent = (percentChange > 0 && isFinite(cost) && cost !== 0) ? (cost / percentChange) * 0.01 : (cost === 0 && dpsGain > 0 ? "Free" : "N/A");

                const resultData = { upgrade: upgradeLabelParts.join(' & '), cost: cost, dps: dpsGain, percent: percentChange, costPerDps: costPerPercent, books: booksNeeded, averageDps: newDps, individualRuns: simResult.individualRuns };
                addResultRow(resultData);
                detailedResults.push(resultData);
            }
        } finally {
            allPageInputs.forEach(input => { if (input.el) { const el = input.el; if (el.tagName === 'SELECT') { const opt = Array.from(el.options).find(o => o.text === input.originalValue); if (opt) el.value = opt.value; } else { el.value = input.originalValue; } el.dispatchEvent(new Event('change', { bubbles: true })); }});
            if (isBatchRunning) { // If the batch finished normally
                statusDiv.textContent = 'Status: Done!';
                document.getElementById('export-csv-button').disabled = false;
            }
            setRunningState(false);
        }
    }

    // --- 5. INITIALIZATION ---
    function initializeScript() {
        const panel = document.getElementById('batch-panel');
        panel.addEventListener('click', (event) => {
            const button = event.target.closest('button');
            if (!button) return;

            switch (button.id) {
                case 'batch-toggle':
                    document.getElementById('batch-content').classList.toggle('hidden');
                    break;
                case 'run-batch-button':
                    startBatch();
                    break;
                case 'stop-batch-button':
                    setRunningState(false);
                    statusDiv.textContent = 'Status: Stopping...';
                    break;
                case 'capture-setup-button':
                    buildInputsUI();
                    break;
                case 'update-baseline-button':
                    updateBaseline();
                    break;
                case 'export-csv-button':
                    exportResultsToCSV();
                    break;
                case 'reset-button':
                    resetInputsToBaseline();
                    break;
            }
        });

        document.querySelector('#batch-results-table thead').addEventListener('click', (event) => { const headerCell = event.target.closest('th'); if (!headerCell) return; const sortKey = headerCell.dataset.sortKey; if (!headerCell) return; const tbody = headerCell.closest('table').querySelector('tbody'); const rows = Array.from(tbody.querySelectorAll('tr')); const isDesc = headerCell.classList.contains('sorted-desc'); const direction = isDesc ? 1 : -1; rows.sort((a, b) => { const valA = a.dataset[sortKey]; const valB = b.dataset[sortKey]; const numA = parseFloat(valA); const numB = parseFloat(valB); if (!isNaN(numA) && !isNaN(numB)) { return (numA - numB) * direction; } return valA.localeCompare(valB) * direction; }); tbody.innerHTML = ''; rows.forEach(row => tbody.appendChild(row)); headerCell.parentElement.querySelectorAll('th').forEach(th => th.classList.remove('sorted-asc', 'sorted-desc')); if(direction === 1) { headerCell.classList.add('sorted-asc'); } else { headerCell.classList.add('sorted-desc'); } });

        const findButtonInterval = setInterval(() => {
            const importButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.includes('Import solo/group'));
            if (importButton) {
                clearInterval(findButtonInterval);
                statusDiv.textContent = 'Status: Ready to import.';
                document.getElementById('capture-setup-button').disabled = false;
                importButton.addEventListener('click', () => {
                    statusDiv.textContent = 'Import initiated! Simulating...';
                    const resultsContainer = document.getElementById('simulationResultTotalDamageDone');
                    if(resultsContainer) resultsContainer.innerHTML = '';

                    const initialSimObserver = new MutationObserver(() => {
                        const dpsVal = getDpsValue();
                        if (dpsVal) {
                            initialSimObserver.disconnect();
                            baselineDps = parseFloat(dpsVal.replace(/,/g, ''));
                            document.getElementById('baseline-display').textContent = `Baseline DPS: ${baselineDps.toFixed(2)}`;
                            setTimeout(buildInputsUI, 200);
                        }
                    });
                    initialSimObserver.observe(document.body, { childList: true, subtree: true });
                });
            }
        }, 500);
    }

    window.addEventListener('load', initializeScript);

})();