BG3 Obsidian Laced Robe Location and Stats | Baldur’s Gate 3 Equipment
The Obsidian Laced Robe is a chest clothing in Baldur’s Gate 3 which resists fire damage and can inflict fire damage on successful saving throws.
The Obsidian Laced Robe is a chest clothing in Baldur’s Gate 3 which resists fire damage and can inflict fire damage on successful saving throws.
The Ring of Protection is a ring accessory in Baldur’s Gate 3 which boosts the dice rolls of your Armour Class and Saving Throws.
The Ring of Self Immolation is a ring accessory in Baldur’s Gate 3 which gives you a way to inflict the Heat condition on yourself.
The Incandescent Staff is a quarterstaff in Baldur’s Gate 3 that gives you a free cast of Fireball, boosts ranged spells, and grants fire resistance.
Tracks all gold spent on wayshrine travel during an event. Start tracking with /eventstart and stop with /eventstop to generate a neatly formatted expense report in chat for guild reimbursement.
What it doesLists every slash command registered by your active addons in a searchable window.Libraries that use SLASH_COMMANDS as a key‑value store are filtered out.Commands/slashfinder open the window (optional pre‑fill)/sf alias for /slashfinder
Welcome to the best Shadowheart Trickery Domain Build Guide for BG3. This build is perfect for players who want to fully embrace the Shar-aligned path in Shadowheart’s storyline after the Gauntlet of Shar. The Dark Justiciar Build works especially well in stealth-focused parties. But it is also has excellent melee damage potential. We also have […]
The post BG3 Shadowheart Dark Justiciar Build – Trickery Cleric appeared first on AlcastHQ.
EnglishDescriptionThese are alternative textures for the Tamriel Tomes user interface. It works flawlessly with both keyboard/mouse and gamepad modes.Darker Tamriel Tomes for PerfectPixel (DarkTamrielTomesUI)” is a standalone addon and does not require PerfectPixel to function.No settings/options menuNo dependenciesOptionalPerfectPixelCredits & Special ThanksMasteroshi430 Author of the original Darker Tamriel Tomes addon.⚠️ Attention / Please NoteIf you are currently using the original Darker Tamriel Tomes, please disable or uninstall it before installing this addon to avoid conflicts.==================================================German/DeutschBeschreibungDies sind alternative Texturen fr die Benutzeroberflche von Tamriel Folianten. Sie funktionieren einwandfrei sowohl mit Tastatur/Maus als auch mit Gamepad.Darker Tamriel Tomes fr PerfectPixel (DarkTamrielTomesUI) ist ein eigenstndiges Add-on und bentigt PerfectPixel nicht, um zu funktionieren.Kein Einstellungs- oder OptionsmenKeine AbhngigkeitenOptionalPerfectPixelCredits & Besonderer DankMasteroshi430 Autor des ursprnglichen Add-ons Darker Tamriel Tomes.⚠️ Achtung / Bitte beachtenFalls du derzeit das ursprngliche Darker Tamriel Tomes verwendest, deaktiviere oder deinstalliere es bitte, bevor du dieses Add-on installierst, um Konflikte zu vermeiden.
# LibZoneTemp**Version:** 2.3.11 | **API Version:** 101049 101050 | **Type:** Library*by @Kreksar5 and Claude.ai*> **AI-ASSISTED ADDON.** Written with Claude.ai and has not yet been> independently validated in-game for correctness or performance. This> notice will be removed once the library has seen real play-testing.A library for *The Elder Scrolls Online* that calculates ambient temperatures for all overland zones and hundreds of sub-zones (delves, dungeons, trials, starter zones, and Daedric realms) based on lore-accurate climate data, the current in-game time of day (via LibClockTST), weather probability, and whether the player is swimming.—## Table of Contents- (#overview)- (#whats-new-in-2311)- (#whats-new-in-2310)- (#whats-new-in-239)- (#whats-new-in-238)- (#whats-new-in-237)- (#whats-new-in-236)- (#whats-new-in-235)- (#whats-new-in-231)- (#whats-new-in-230)- (#whats-new-in-220)- (#whats-new-in-210)- (#whats-new-in-200)- (#features)- (#dependencies)- (#installation)- (#public-api)- (#slash-command)- (#settings-panel)- (#zone-coverage)- (#temperature-model)- (#design-notes)- (#for-addon-authors)- (#disclaimer)—## OverviewLibZoneTemp provides a single, consistent source for ambient temperature data across ESO zones. It is intended to be consumed by other addons that want to display, react to, or simulate temperature-driven gameplay such as weather mods, survival systems, immersion HUDs, or RP tools.All internal calculations are performed in **Celsius**. Fahrenheit conversion is available for display purposes via `lib.FormatTemperature()` and the user-facing settings panel.—## What’s New in 2.3.11- **Fixed the settings panel’s author field**, which was still the literal placeholder text `”YourName”` never actually updated even after the `## Author` manifest field itself was corrected. Also bumped the internal `LIB_VERSION` guard (previously stale at `12`) to match the manifest `## AddOnVersion`, since the panel’s displayed version reads directly from that constant.—## What’s New in 2.3.10- **Standardized the author credit** to `@Kreksar5 and Claude.ai` in the `## Author` manifest field and this README’s byline, and named Claude.ai specifically (rather than generic “AI assistance”) in the manifest description and the AI-assistance disclosure above, matching the crediting convention used across this addon’s companion libraries.—## What’s New in 2.3.9- **Fixed zone temperature overrides not applying to delves, dungeons, and trials.** Interior zones have no climate-table entry of their own and inherit their base temperature from their overland parent zone; the override lookup only ever checked the *parent’s* ID, so an override set on the interior zone itself (as offered in the settings dropdown) was saved but silently never read back. The override lookup now checks the player’s exact current zone first, before falling back to the climate zone used for the base-temperature table.- **Added a live “Currently Active Overrides” list** to the settings panel, showing every zone with an override currently set, in both C and F.- **Added a Celsius/Fahrenheit reference chart** to the override section’s description, with a short note on the kind of zone each range typically represents, to help you pick a sensible override value.—## What’s New in 2.3.8- **Corrected the `## Author` manifest field**, which was still the literal placeholder text “YourName” never actually filled in. Fixed to the real author.- **Added the required AI-assistance disclosure** to the manifest description and this README, per ESOUI’s addon release rules.- **Added a version floor (`>=1`) to the `LibClockTST` dependency**, which previously had none.—## What’s New in 2.3.7- **Full API audit against the official ESOUI API 101050 documentation.** Every function call, method call, and constant referenced across the library was cross-checked against the API doc and, where the doc didn’t cover it (third-party libraries), against the live ESOUI source. No invalid or deprecated API usage found the `LibZone`/`LibClockTST` calls (`GetAllZoneData`, `GetCurrentZoneIds`, `GetCurrentZoneAndGroupStatus`, `Instance`, `GetTime`, `RegisterForTime`) are all legitimate calls into those libraries and correctly used. No code changes required.—## What’s New in 2.3.6- **Corrected `LibAddonMenu-2.0` dependency floor**: was mistakenly bumped to `>=45` in 2.3.5, but LibAddonMenu’s own manifest documentation shows `43` as the latest available version, not `45`. Fixed to `>=43`. The 2.3.5 entry below has also been corrected to reflect the right number.—## What’s New in 2.3.5ESOUI release-rules compliance pass no zone-data changes.- **`## APIVersion` updated** from the stale `101040` to `101049 101050` (current Live/PTS).- **`LibAddonMenu-2.0` dependency floor bumped** from `>=30` to `>=43`, matching the version cited as current in LibAddonMenu’s own manifest documentation.- **Fixed a version-string mismatch**: this README’s header said `2.3.0` while the manifest’s `## Version` already said `2.3.4` now synced.- **Fixed a duplicate/mislabeled changelog section** (see below the second “What’s New in 2.3.0” entry has been relabeled `2.3.1`, since it describes a separate, later correction pass).- **Reviewed for global variable leaks** none found; the library already follows the single-global-table pattern (`LibZoneTemp = LibZoneTemp or {}` / `local lib = LibZoneTemp`).- **Reviewed against “what addons cannot do”** none found. This library only reads zone/time/weather state to compute a number; it doesn’t touch any restricted behavior.—## What’s New in 2.3.0Version 2.3.0 is the second UESP-verified correction pass, covering the remaining **405 zones** (rows 301705). **38 corrections** were applied.### Key Correction Themes**Solstice (tropical island) zones** all Solstice locations introduced in Seasons of the Worm Cult were assigned incorrect cold-zone temperatures. Solstice is a tropical island south of Murkmire (~32 C); affected zones corrected include Tarnur Mine (−8 C → 30 C), Li-Xal Pass (4 C → 28 C), Vosgah Shrine (4 C → 30 C), and Vale of Revelry (20 C → 28 C).**Nordic barrow/crypt zone mis-assignments** Shroud Hearth Barrow, Taarengrav Barrow, and Nimalten Barrow were all in **The Rift** (6 C), not the arctic; corrected from −8 C → 4 C each. Wittestadr Crypts and Trolhetta Cave are in **Eastmarch** (−2 C), not arctic.**Clockwork City** Slag Town Outlaws Refuge reassigned from 4 C → 22 C (mechanically regulated Brass Fortress environment).**Cyrodiil/Stormhaven/Deshaan** Underpall Cave (Cyrodiil, 14 C), Pariah Catacombs (Stormhaven, 10 C), Reservoir of Souls (Deshaan, 15 C) all corrected from −8 C.**Fargrave/Mirrormoor** Shattered Mirror Isle (20 C → 36 C) and Loom of the Untraveled Road (15 C → 32 C) correctly placed in Ithelia’s hot Oblivion realm.**Western Skyrim interior calibration** Proudspire Manor, Solitude Outlaws Refuge, Snowmelt Suite, and Tzinghalis’s Tower all adjusted downward to reflect WS = −10 C with appropriate interior insulation offsets.—## What’s New in 2.3.1Version 2.3.1 is the third UESP-verified correction pass (rows 301400). **16 zone entries** were corrected after cross-referencing confirmed locations against UESP.### Notable Fixes- **Kingscrest Cavern**: NE Cyrodiil cave (was 36C → 15C; not Bangkorai)- **Kushalit Sanctuary**: N Vvardenfell/Red Mountain slopes (note corrected to Vvardenfell)- **Lipsand Tarn**: Colovian Highlands Ayleid ruin in NW Cyrodiil (was 4C → 14C; not a mountain tarn)- **Lucky Cat Landing**: Senchal player house in S Elsweyr (was 22C → 28C)- **Nighthollow Keep**: Blackreach: Arkthzand Cavern deep interior (was 8C → −4C)- **Nikolvara’s Kennel**: Wrothgar cave SW of Orsinium (was 5C → −5C)- **Obsidian Scar**: Rivenspire volcanic cave (was 28C → 18C; geothermally warm but still Rivenspire)- **Odious Chapel**: Shadowfen (Stillrise Village chapel, was 12C → 28C)- **Old Coin Fort**: N tip of Amenos tropical island (was 16C → 24C)- **Maelstrom Arena**: Pocket realm of Fa-Nuit-Hen (was 10C → 20C; not cold Wrothgar)—## What’s New in 2.2.0Version 2.2.0 is a UESP-verified correction pass. **48 zone entries** from the v2.1.0 expansion have been corrected after cross-referencing each zone’s confirmed location against UESP wiki sources.### Corrections SummaryThe primary error pattern was zones assigned to the wrong parent region entirely, producing temperatures that were wildly off most commonly cold-zone values (−8 C) applied to locations in Bangkorai, Greenshade, Summerset, Southern Elsweyr, or Amenos, and vice versa. Notable individual fixes include:- **Coldrock Diggings**: Alik’r Desert mine (was −8 C → 36 C)- **Forsaken Citadel**: Southern Elsweyr (was 4 C → 28 C)- **Ghost Haven Bay**: Amenos tropical cove (was 8 C → 24 C)- **Halls of the Highmane**: Southern Elsweyr Khajiiti ruin (was 8 C → 26 C)- **Hildune’s Secret Refuge**: Rivenspire cave (was 32 C → 6 C)- **Istirus Outpost / Arena**: West Weald/Cyrodiil battleground, not Reaper’s March (was 35 C → 18 C)- **Destruction’s Solace**: Deadlands capital fortress (was 40 C → 50 C)- All 48 corrections include matching water temperature updates.—## What’s New in 2.1.0Version 2.1.0 is a UESP-verified data expansion. The zone database has grown from ~317 entries to over **1,000 named zones**, adding **705 new entries** sourced directly from the UESP wiki and covering the full breadth of ESO’s explorable areas.### New Coverage- **Delves, trials, arenas, and public dungeons** across all chapters and DLC- **Instanced interiors**: manors, Outlaws Refuges, city districts, crypts, and tombs- **Daedric and Oblivion sub-realms**: Colored Rooms, Spiral Skein, Hunting Grounds, and more- **Aquatic zones**: Abecean Sea and other ocean/coastal surfaces- **Player housing areas** with climate appropriate to their region- Air temperatures derived from lore-accurate UESP climate descriptions- Water temperatures for all new entriesAll new entries include a descriptive comment drawn from UESP source notes.—## What’s New in 2.0.0Version 2.0.0 is a major data expansion. The zone temperature tables now cover **all overland zones plus hundreds of named sub-zones**, including:### New Overland / Chapter Zones- **West Weald** (19 C) southwestern Cyrodiil woodlands and rolling hills- **Bleakrock Isle** (−8 C) frozen starter island in the Sea of Ghosts- **Bal Foyen** (20 C) warm coastal Morrowind delta- **Stros M’Kai** (34 C) arid sun-baked Hammerfell island- **Betnikh** (15 C) temperate Orc island off Glenumbra- **Khenarthi’s Roost** (30 C) warm tropical Khajiit island- **Blackreach: Arkthzand Cavern** (−5 C) cold underground Dwemer Blackreach beneath The Reach### Fire / Lava Sub-ZonesThese zones receive **explicit high temperatures** rather than inheriting from their parent, because they contain active lava, dragon fire, Oblivion portals, or extreme geothermal activity:| Zone | C | Notes ||—|—|—|| City of Ash II | 55 | Deepest Mehrunes Dagon Oblivion plane || The Path of Cinders | 53 | Lava-bordered Deadlands path || Tormented Spire Summit | 54 | Summit of most active Stonefalls volcanic vent || The Deadlands: Testing Grounds | 52 | Deadlands sub-region || Burning Gyre Keep | 52 | Deadlands fortification || City of Ash I | 50 | Mehrunes Dagon Oblivion plane || Bloodroot Forge | 48 | Hircine’s ancient forge; lava channels in The Reach || The Cauldron | 44 | Deep underground Blackwood forge dungeon || Blessed Crucible | 44 | Rift arena over a volcanic vent || Ashalmawia | 42 | Vvardenfell Daedric shrine on volcanic vent || Sunspire | 42 | Northern Elsweyr dragon trial; desert sun + dragon fire || Dragonhold | 46 | Southern Elsweyr dragon lair; volcanic peak || Khaj Rawlith | 30 | Ancient underground Anequine temple in hot Reaper’s March; warm, stuffy, dark-fire corruption |### Ice / Frozen Sub-ZonesThese zones receive **explicit low temperatures** below their parent zone:| Zone | C | Notes ||—|—|—|| Icereach | −16 | Frigid isle in the Sea of Ghosts; polar cold || Frostvault | −14 | Dwarven vault buried in a thawing glacier || Ice-Heart’s Lair | −14 | Frozen bandit cave in high Wrothgar peaks || Kyne’s Aegis | −12 | Offshore Norse sea fortress trial; freezing winds || Frostbreak Fortress | −12 | Frost-covered Orc fortress in Wrothgar || Frozen Coast | −12 | Western Skyrim coastal; sea ice and blizzards || Direfrost Keep | −10 | Ancient Nord ice fortress; permafrosted interior || The Frigid Grotto | −10 | Eastmarch; frost atronachs throughout || Stone Garden | −8 | Underground frozen lycanthrope lab under Skyrim || Bleakridge Barrow | −8 | Frozen Nord barrow in Western Skyrim || Icehammer’s Vault | −8 | Sealed ice vault in Eastmarch || Chillwind Depths | −8 | Underground frozen depths in Western Skyrim || Coldwind’s Den | −8 | High mountain den in Wrothgar; extreme cold || Greymoor Keep | −8 | Half-buried in Western Skyrim’s frozen earth || Scalecaller Peak | −6 | Icy Wrothgar mountain summit with dragon || Castle Thorn | −6 | Vampire stronghold in Western Skyrim || Labyrinthian | −6 | Ancient ruins; deeply cold || The Chill Hollow | −6 | Ice-filled Eastmarch hollow cave |### Underground / Dungeon CoverageHundreds of delves, public dungeons, group dungeons, and trials across all zones now have explicit entries rather than falling back to the overland default. Key examples:- **Fungal Grotto I & II** (24 C) warm, damp sea cavern in Stonefalls- **Darkshade Caverns I & II** (2022 C) geothermal kwama mine- **Ruins of Mazzatun** (28 C) hot, humid Hist-tree complex in Black Marsh- **Cloudrest** (14 C) Summerset trial near the summit of Eton Nir; cold at altitude- **Halls of Fabrication** (24 C) Vvardenfell trial; heat from fabricant manufacturing- **All Vvardenfell egg mines and Dwemer ruins** (3034 C) geothermal Morrowind underground- **All Clockwork City sub-zones** (2022 C) temperature-controlled mechanical realm### Water Temperature ExpansionAll newly added zones also include specific **water temperatures**, from −1 C (Frostvault glacier meltwater, Icereach polar water) to 60 C (Deadlands and City of Ash II).### Weather Profile ExpansionNewly added zones include weather profiles:- **Icereach**: 5% rain / 80% snow constant blizzard conditions- **Kyne’s Aegis**: 10% rain / 70% snow- **Bleakrock Isle**: 15% rain / 60% snow- **Scalecaller Peak**: 5% rain / 70% snow- **Frozen Coast**: 10% rain / 60% snow- All underground, Oblivion, and enclosed zones: 0% rain / 0% snow—## Features- **Lore-accurate base temperatures** for every overland zone and hundreds of sub-zones through West Weald (2024)- **Explicit overrides for fire/lava zones** City of Ash, Deadlands, Tormented Spire, Bloodroot Forge, The Cauldron, and more- **Explicit overrides for ice/frozen zones** Icereach, Frostvault, Direfrost Keep, Kyne’s Aegis, and more- **Time-of-day modifiers** using the in-game Tamriel clock (dawn, day, dusk, night)- **Climate/weather modifiers** derived from zone lore- **Water temperature** when the player is swimming (`IsUnitSwimming`)- **Interior/dungeon penalty** delves, group dungeons, public dungeons, and trials are shielded from weather; many now have their own explicit temperatures- **Parent zone inheritance** unlisted child zones fall back to their parent’s climate data- **Per-zone user overrides** via the LibAddonMenu-2.0 settings panel- **Celsius / Fahrenheit toggle** in settings- **Live updates** via LibClockTST subscription and `EVENT_PLAYER_ACTIVATED`- **Debug slash command** `/ztemp` for on-demand snapshots—## Dependencies| Library | Minimum Version | Purpose ||—|—|—|| (https://www.esoui.com/downloads/info1496-LibZone.html) | 077 | Resolves zone IDs and zone group status (delve/dungeon detection) || (https://www.esoui.com/downloads/info7-LibAddonMenu.html) | 30 | Settings panel UI || (https://www.esoui.com/downloads/info2360-LibClockTST.html) | | In-game lore time (hour/minute) for time-of-day modifiers |All three dependencies must be installed and loaded for full functionality. The library degrades gracefully if LibClockTST is unavailable (defaulting to noon), and will print a warning in the settings panel if LibZone data cannot be loaded.—## Installation1. Download and unzip `LibZoneTemp` into your addons folder: “` liveAddOnsLibZoneTemp “` The folder should contain: “` LibZoneTemp/ ├── LibZoneTemp.lua ├── LibZoneTemp.txt └── README.md “`2. Ensure all three (#dependencies) are also installed.3. Launch ESO. LibZoneTemp will initialise automatically on the `EVENT_ADD_ON_LOADED` event.4. Optionally access settings via the in-game **Settings → Addons → LibZoneTemp Settings** panel, or by typing `/libzonetemp` in chat.—## Public API“`lua– Returns the current ambient temperature for the player’s zone.– When the player is swimming, returns the zone’s water temperature.– formattedString respects the user’s unit preference (“23.4C” / “74.1F”).local tempC, zoneName, tempF, formatted = lib.GetCurrentTemperature()– Returns the base temperature for a given zone ID (before time/weather modifiers).local baseTemp = lib.GetBaseTemperatureForZone(zoneId, isInterior)– Returns the full temperature for arbitrary input values. Always Celsius.– Pass isSwimming = true to get the zone’s water temperature instead.local temp = lib.CalculateTemperature(zoneId, isInterior, loreHour, isSwimming)– Converts and formats a Celsius value using the user’s saved unit preference.– Safe to call before savedVars is loaded (falls back to Celsius).local str = lib.FormatTemperature(celsius)“`—## Slash CommandType `/ztemp` in chat to print a debug snapshot:“` Zone: City of Ash I Temperature : 50.0C (50.0C / 122.0F) Lore time : 14:30 Zone ID : 176 Swimming : false“`—## Settings PanelAccess via `/libzonetemp` or **Settings → Addons → LibZoneTemp Settings**:- **Display in Fahrenheit** toggle between C and F display- **Zone Temperature Overrides** pick any zone from a dropdown and set a custom base temperature (C). Set to 0 or blank to revert to the library default.—## Zone Coverage### Overland Zones (all factions + DLC/Chapter through West Weald)All 40+ overland zones have explicit base temperatures, weather profiles, and water temperatures.### Sub-Zones with Explicit TemperaturesOver 200 named sub-zones now have their own entries, overriding parent-zone inheritance where the environment is meaningfully different. These fall into three categories:**Fire / Lava / Volcanic** zones containing active lava rivers, Oblivion fire planes, volcanic vents, or draconic flame. Temperatures range from 30 C (warm underground ruins near volcanic activity) to 60 C (deep Deadlands and City of Ash II).**Ice / Frozen / Arctic** zones buried in glaciers, located on polar islands, or containing permafrost and frost atronachs. Temperatures range from −16 C (Icereach) to −2 C (March of Sacrifices).**Underground / Dungeons** most inherit from their parent zone via the existing fallback, but dungeons with notable microclimates (geothermal heat, deep cold, enclosed Daedric environments) have their own entry.### Fallback BehaviourAny zone not found in the tables falls back to `DEFAULT_TEMP` (20 C) for air and `DEFAULT_WATER_TEMP` (16 C) for water. Sub-zones will first try their specific `zoneId`, then fall back to their `parentZoneId`, before reaching the global default.—## Temperature Model“`finalTemp = baseTemp + interiorModifier (−3 C, reduced weather/time swing indoors) + timeOfDayModifier (−5 to +3 C based on lore hour) + weatherModifier (rain: up to −4 C; snow: up to −6 C)“`Water temperature bypasses all modifiers it returns the zone’s static water value directly.—## Design Notes**Why names and not integer IDs?** ESO zone IDs are large non-sequential integers that can change between API updates. The only stable, human-readable identifier is the English zone name. At initialisation, `BuildZoneIdMaps()` calls `LibZone:GetAllZoneData()` and inverts the name→ID mapping.**Why static weather and not live weather?** The ESO addon API does not expose a function to read the current client-side weather state. Weather is entirely client-side and visual only. LibZoneTemp therefore uses a static probability table derived from zone lore.**Why not cover every delve?** Most delves inherit cleanly from their parent overland zone. Only delves with a meaningfully different microclimate (volcanic heat, glacial cold, Daedric environment) receive their own entry. This keeps the table maintainable while covering all cases where inheritance would produce a wrong result.—## For Addon Authors“`lua– Minimal usage: get the current zone temperaturelocal temp, zoneName, tempF, formatted = LibZoneTemp.GetCurrentTemperature()if temp then d(“Current temperature in ” .. zoneName .. “: ” .. formatted)end– Check if the player is in a hot zone (e.g. for survival gameplay)local zoneId = LibZoneTemp._currentZoneIdlocal base = LibZoneTemp.GetBaseTemperatureForZone(zoneId, false)if base >= 40 then — Apply heat exhaustion debuffend“`—## DisclaimerThis Add-on is not created by, affiliated with, or sponsored by ZeniMax Media Inc. or its affiliates. The Elder Scrolls and related logos are registered trademarks or trademarks of ZeniMax Media Inc. in the United States and/or other countries. All rights reserved.
# LibArmorInsulation**Version 2.7.5** ESO API 101049 101050 License: MIT*by @Kreksar5 and Claude.ai*> **AI-ASSISTED ADDON.** Large portions of this addon’s code (the costume/outfit> scanning commands, the resolver priority logic, and several data-table> structures) and its UESP-sourced costume/style insulation data were written> with Claude.ai, and **have not yet been independently validated in-game**> for correctness or performance. Material/insulation assignments carry an> explicit “ tag in `LibArmorInsulation_StyleData.lua`> where applicable `low`-confidence entries in particular are thematic> inferences, not verified facts, and are good candidates for manual> re-checking against UESP or in-game testing before being trusted as accurate.> This notice will be removed once the addon has seen real play-testing.A reusable ESO addon library that calculates a **staggered-tier thermal insulation score (-10 to 90, in steps of 10)** for the player based on their current visual appearance taking into account armor style, material composition, body-slot coverage, and flavor-text bonuses or penalties from the style database. The score mirrors ESO’s own visual-priority rules: polymorphs beat costumes, costumes beat outfits, outfits beat worn armor.—## Table of Contents1. (#installation)2. (#dependencies)3. (#insulation-model)4. (#visual-priority-cascade)5. (#public-api)6. (#slash-commands)7. (#settings-panel)8. (#adding–editing-style-data)9. (#eso-api-functions-used)10. (#design-notes–limitations)11. (#changelog)—## Installation1. Drop the `LibArmorInsulation` folder into `Elder Scrolls OnlineliveAddOns`.2. Ensure **LibAddonMenu-2.0 (v33+)** is also installed ((https://www.esoui.com/)).3. Enable the addon in the ESO Addon Manager.—## Dependencies| Library | Required | Notes ||—|—|—|| LibAddonMenu-2.0 ≥ 33 | **Yes** | Settings panel only. The calculation API works without LAM, but the settings panel will be unavailable. || LibStyleInfo | No | Not used. Its confirmed public API exposes style IDtoname lookups and light/medium/heavy weight flags, but not material composition, body-coverage percentage, or flavor text. || LibMotifCategories | No | Not used. Motif category data (crafted/overland/dungeon/etc.) does not map to thermal properties. |> **Why maintain an internal style database?**> Both evaluated libraries were ruled out because neither provides the material or coverage data needed for insulation math. LibArmorInsulation therefore ships its own hand-authored table keyed on `ITEM_STYLE_*` integer constants, which every ESO item carries natively.—## Insulation Model### ScaleAll values resolve to one of **eleven fixed tiers**, staggered in increments of 10 from **-10 to 90**:| Tier | Label | Meaning ||—|—|—|| -10 | Magically Cooled | Magically cooled equipment (e.g. refrigeration) || 0 | No Insulation | No insulation || 10 | Minimal | Underwear/swimwear || 20 | Light | Light silks, breathable thin fabrics, or minimal coverage || 30 | Coarse Light | Linens, coarser fabrics, or small amounts of leather with light coverage || 40 | Layered/Leather | Thick or many-layered fabrics, or majority-coverage leathers || 50 | Full Leather/Light Metal | Full coverage leathers, mild coverage metals, or full-coverage multi-layered thick fabrics || 60 | Heavy Leather/Fur | Full coverage heavy/thick leathers, mild coverage furs, or moderate coverage metals || 70 | Full Fur/Metal | Full coverage furs or full coverage metals || 80 | Heavy Fur/Metal | Full coverage heavy furs and/or metals || 90 | Magically Heated | Magically heated equipment |`-10` and `90` are reserved for entries explicitly flagged `magical = true` in the data tables (Flame Atronach, Ice Wraith, etc.). Every other entry is clamped to the mundane **080** range before being snapped to the nearest tier, so ordinary armor and clothing can never accidentally read as magically hot or cold.> **Why a tier ladder instead of a continuous score?** It’s a much better fit for anything that has to *display* insulation a UI can show one of 11 discrete states/icons instead of trying to render 101 shades of meaning. It’s also easier to reason about and balance by hand: every entry in the data tables is describable in one of eleven plain-English buckets rather than an arbitrary integer.### How an entry becomes a tierArmor styles and outfit style pieces keep their existing `baseMaterial` + `coverage` + `flavorBonus` authoring fields nothing in the data tables needed to be hand-rewritten for the tier system. The Calculator computes the same full-body reference score it always has, then **snaps** that score onto the nearest tier:“`raw = 0.90 styleCoverage materialCoeff SCALE_FACTOR + flavorBonustier = SnapToTier(raw, magical) — nearest of {-10,0,10,…,80,90} — clamped to first unless the entry has `magical = true““`SCALE_FACTOR = 55.556` anchors a full set of standard leather armor at coverage 1.0 to exactly tier 50.Costumes and polymorphs keep their existing hand-authored `totalInsulation` field, which is likewise snapped to the nearest tier at lookup time (again clamped to 080 unless `magical = true`).### Per-slot percentage adjustment (unless it’s a full costume)A style/outfit piece’s tier is its **full-body reference value**. Individual armor/outfit slots then contribute a **percentage** of that tier based on how much of the body they cover:“`slot_contribution = round(tier slotPercentage)total = sum(slot_contribution for every worn slot)“`Slot percentages now sum to exactly **1.00** across the 7 thermally-relevant slots (previously 0.90, compensated by `SCALE_FACTOR`), so a full matching set (every slot the same tier) reproduces that tier as the total. Missing slots count as bare skin (tier 0) for that percentage of the body small independent-rounding artifacts of a point or two are possible when summing per-slot contributions, but the total will never drift outside the -10..90 tier range.**Costumes and polymorphs are NOT slot-adjusted.** They represent the whole body at once, so their snapped tier *is* the total, unadjusted there’s no percentage to apply.### Slot coverage weights| Slot | Percentage ||—|—|| Chest | 33% || Legs | 28% || Head | 11% || Shoulders | 11% || Hands | 6% || Feet | 6% || Waist | 5% || Rings, Neck, Weapons | 0% |### Material coefficients| Material | Coefficient | Full-set reference (pre-snap) | Snapped tier ||—|—|—|—|| fur | 1.8 | 90 | 80 (mundane cap) || wool | 1.5 | 75 | 70 (tie rounds down) || heavy_leather | 1.3 | 65 | 60 (tie rounds down) || leather | 1.0 | 50 (baseline) | 50 || scale | 0.8 | 40 | 40 || runic | 0.8 | 40 | 40 || bone | 0.7 | 35 | 30 (tie rounds down) || cloth | 0.7 | 35 | 30 (tie rounds down) || chitin | 0.6 | 30 | 30 || hide | 0.6 | 30 | 30 || iron | 0.5 | 25 | 20 (tie rounds down) || steel | 0.5 | 25 | 20 (tie rounds down) || silk | 0.5 | 25 | 20 (tie rounds down) || linen | 0.4 | 20 | 20 || aetherial | 0.4 | 20 | 20 || daedric | 0.3 | 15 | 10 (tie rounds down) || crystal | 0.2 | 10 | 10 || none | 0.0 | 0 | 0 |Ties (raw score exactly halfway between two tiers, e.g. 75 between 70 and 80) round **down** to the lower tier `Calc.SnapToTier()` picks the first tier with the smallest difference while scanning the ladder ascending, so the lower of two equidistant tiers wins.”Full-set reference” assumes coverage 1.0 and no flavor bonus; per-style `coverage`/`flavorBonus` modifiers shift the raw score up or down before it’s snapped, so an individual style’s tier can land a step away from its base material’s own tier (e.g. Nord’s fur base plus its +8 flavor bonus and 1.1 coverage pushes it to the mundane ceiling of tier 80).### Costume & polymorph scoringCostumes and polymorphs bypass per-slot math entirely. They return a single snapped tier looked up from `CostumeInsulationById` (by collectible ID), then `CostumeInsulation` (by normalized lowercase name), then the appropriate default:| Fallback | Raw default | Snapped tier ||—|—|—|| `DEFAULT_COSTUME` | 50 | 50 || `DEFAULT_POLYMORPH` | 38 | 40 |**Costume lookup priority.** Order is: user override (already a tier used as-is) → `CostumeInsulationById` (by collectible ID) → `CostumeInsulation` (by name, manually curated see Changelog 2.5.0) → `sv.costumeCache` (populated by `/scancostumes`) → `DEFAULT_COSTUME`. Run `/scancostumes` to populate the cache with keyword-derived auto-ratings for anything not already in the manual tables.—## Visual Priority Cascade`GetTotalInsulation()` and `GetInsulationBreakdown()` mirror ESO’s own visual priority rules, evaluated top-to-bottom:1. **Polymorph** detected via `GetActiveCollectibleByType(COLLECTIBLE_CATEGORY_TYPE_COSTUME)` combined with keyword/ID matching against polymorph entries. Returns a flat body-wide score; slot math is skipped.2. **Costume** same detection mechanism. Returns a flat body-wide score; slot math is skipped.3. **Active Outfit** detected via `GetEquippedOutfitIndex()`. Slot insulation is calculated from the collectible ID on each `OUTFIT_SLOT_*` using `ZO_OUTFIT_MANAGER`. Only slots with a non-zero style override contribute; if *no* outfit slots are overridden, falls through to armor.4. **Worn Armor** calls `GetItemLink(BAG_WORN, equipSlot)` then `GetItemLinkItemStyle(itemLink)` for each thermally relevant `EQUIP_SLOT_*`. `GetItemLinkItemStyle` is used (not `GetItemStyle`) because it returns the correct visual style for all item origins crafted, dropped, set, and Crown Store items alike.5. **Naked** returns 0.—## Public API“`lua– Total insulation score, one of the 11 tiers for costume/polymorph;– a composite (-10..90) for armor/outfit made of per-slot tier contributionslocal total = LibArmorInsulation.GetTotalInsulation()– Full breakdown tablelocal info = LibArmorInsulation.GetInsulationBreakdown()– info.source : “polymorph” | “costume” | “outfit” | “armor” | “naked”– info.total : integer, -10..90– info.slots : table {– = {– styleId, — ITEM_STYLE_* (armor path) or nil– collectibleId, — collectible ID (outfit/costume path) or nil– insulation, — integer slot contribution (== tier for costume/polymorph)– tier, — the style/costume’s own full-body tier (-10..90)– slotPercentage, — 0-1 weight of this slot; nil for costume/polymorph– material, — string material key– flavorNote, — string human-readable note, or “”– armorFallback, — boolean: true if outfit slot fell back to worn armor– }– }– info.costumeId : number | nil (collectible ID of active costume/polymorph)– info.costumeName : string | nil (lower-cased collectible name)– Full-set reference tier for a given ITEM_STYLE_* id (respects overrides)– Returns one of {-10,0,10,20,30,40,50,60,70,80,90}local tier = LibArmorInsulation.GetInsulationForStyle(styleId)– Set a persisted insulation override one of the 11 tier values– idType : “style” → ITEM_STYLE_* integer (worn armor)– “outfit” → collectible ID (outfit slot piece)– “costume” → collectible ID (costume or polymorph)– value = nil removes the override. Non-tier numbers are snapped to the– nearest tier rather than rejected. In the Settings panel this is always a– dropdown, so it can’t drift off-tier.LibArmorInsulation.SetOverride(idType, id, value)– Remove all user overrides (does not clear the costume cache)LibArmorInsulation.ResetOverrides()“`### Constants“`luaLibArmorInsulation.VERSION — “2.7.0”LibArmorInsulation.ADDON_NAME — “LibArmorInsulation”“`### Saved variables schema (account-wide)“`luasv.overrides — { = tier integer (-10..90) }sv.costumeCache — { = { name, insulation, autoRated } } — Populated by /scancostumes; persists across sessions. — `insulation` here is a pre-snap raw value, snapped to the — nearest tier at lookup time.“`Override keys follow the pattern `_` (e.g. `style_42`, `outfit_1234567`, `costume_9876543`). This unified key space means a single `SetOverride` call handles all three entity types without ambiguity.—## Slash Commands### General| Command | Description ||—|—|| `/insulation` | Prints the current source, total score with its nearest tier label, and per-slot breakdown style/collectible ID, insulation contribution, tier, slot percentage, material to chat. || `/styleids` | Prints the `ITEM_STYLE_*` ID worn in each armor slot. Use this to find the ID to enter in the override editor when working with worn armor. |### Outfit commands| Command | Description ||—|—|| `/outfitids` | Prints the collectible ID and name applied to each slot of every styled outfit. Use this to find the collectible ID to enter as an `outfit_` override. || `/outfitactive` | Diagnostic probes free functions and `outfitManipulator` methods to determine whether each outfit is actively equipped versus merely stored. Run with an outfit equipped, then unequipped, and compare output. || `/scanoutfitstyles` | Walks every equipped slot across all unlocked outfits and, for any collectible ID not already in the hand-curated `OutfitStyles` table, keyword-rates it (by name, then description) and saves a guessed `{baseMaterial, coverage, flavorBonus}` to `sv.outfitStyleCache`. Run this after unlocking/equipping a new style page so it gets a value other than `OutfitStyles`. |### Costume & polymorph commands| Command | Description ||—|—|| `/costumeids` | Prints the collectible ID, display name, and current lookup method (user override / ID table / name table / DEFAULT_COSTUME) for the active costume or polymorph. Also prints the override key (`costume_`) to use in the settings panel. || `/scancostumes` | Walks every owned costume and polymorph in the Collections system and assigns a keyword-derived auto-rating to each (checking the name first, then the in-game description if the name has no match), saving the results in the costume cache (`sv.costumeCache`). Run this once and again after acquiring new costumes to ensure all owned costumes receive a meaningful score rather than the default. || `/clearcostumecache` | Removes all entries from `sv.costumeCache`. Re-run `/scancostumes` afterward to repopulate. |### Review commands| Command | Description ||—|—|| `/costumesneedreview` | Filters `sv.costumeCache` down to entries where the keyword scan found no match in either the name or description (sitting on the generic 50 default). Use this as a worklist for manual UESP-backed `CostumeInsulationById` entries instead of scrolling the full `/scancostumes` output. || `/outfitstylesneedreview` | Same as `/costumesneedreview`, but for `sv.outfitStyleCache` entries with no keyword match. Use as a worklist for manual `OutfitStyles` entries. |### Diagnostic commands| Command | Description ||—|—|| `/diagcollectibles` | Three-phase diagnostic that enumerates every collectible category and subcategory, identifies which one ESO marks as `COLLECTIBLE_CATEGORY_TYPE_COSTUME`, and reports the first reachable collectible per category. Run this and share the output if `/scancostumes` finds 0 costumes. |—## Settings PanelAccessible via **Addon Settings** in the ESO main menu.| Section | What it does ||—|—|| **Current Insulation** | Live breakdown of every slot’s contribution, tier, slot percentage, the source (outfit / armor / costume / polymorph), material, and any flavor note. Press **Refresh** to recalculate. || **Manual Overrides** | Pick a **Layer** (Costume/Polymorph, Outfit, or Armor) and, for Outfit/Armor, a **Slot** each selection does a fresh live lookup of that specific layer/slot (independent of what’s actually visually showing) and auto-fills Override Type/ID/Tier with it, or with a placeholder if nothing’s active there. Use **Refresh Preview** if you changed gear without touching the Layer/Slot dropdowns. Adjust the Tier if you like, then press **Apply Override**. Choosing “(no override)” removes an existing override. || **Active Overrides** | Lists all currently active overrides with their keys, tier values, and tier labels. || **Reset** | **Reset All Overrides** removes every override and reverts to database defaults. The costume cache is not affected. |> **Tip:** Use `/styleids` to find worn armor style IDs, `/outfitids` for outfit collectible IDs, and `/costumeids` for the active costume’s collectible ID before entering values in the override editor.—## Adding / Editing Style DataAll style data lives in `LibArmorInsulation_StyleData.lua`.### Armor styles (`LibArmorInsulationData.Styles`)Keys are `ITEM_STYLE_*` integer constants. Full list: “`lua = { baseMaterial = “leather”, — string key from MaterialCoefficient table coverage = 1.0, — style-level coverage modifier flavorBonus = 0, — flat integer bonus/penalty (can be negative) flavorNote = “”, — human-readable reason shown in settings & /insulation},“`Any style not in the table falls back to “ (leather, coverage 1.0, no bonus → score 50).### Outfit styles (`LibArmorInsulationData.OutfitStyles`)Keys are **collectible IDs** (Collections system integers), not `ITEM_STYLE_*` values. The two number spaces are unrelated. Use `/outfitids` to find collectible IDs.“`lua = { baseMaterial = “leather”, coverage = 1.0, flavorBonus = 0, flavorNote = “”,},“`Falls back to “ (leather, coverage 0.9).### Costume & polymorph entries (`LibArmorInsulationData.CostumeInsulationById` and `CostumeInsulation`)`CostumeInsulationById` is keyed by collectible ID (preferred); `CostumeInsulation` is keyed by lowercase display name (legacy fallback). Both use flat `totalInsulation` values.“`lua = { totalInsulation = 72, flavorNote = “Heavy bear-fur costume” },– or by name: = { totalInsulation = 30, flavorNote = “Thin ceremonial cloth” },“`Special keys “ (50) and “ (38) are the last-resort fallbacks.—## ESO API Functions Used| Function | Source | Purpose ||—|—|—|| `GetActiveCollectibleByType(type)` | ESO | Detect active costume / polymorph || `GetCollectibleName(id)` | ESO | Resolve collectible display name for data lookup || `GetCollectibleId(catIndex, subIndex, index)` | ESO | Iterate collectibles for costume scanning || `GetCollectibleInfo(id)` | ESO | Read unlock state during `/scancostumes` || `GetNumCollectibleCategories()` | ESO | Enumerate categories in `/diagcollectibles` || `GetCollectibleCategoryInfo(catIndex)` | ESO | Get category name in `/diagcollectibles` || `GetEquippedOutfitIndex()` | ESO | Detect whether an outfit is actively worn (confirmed in-game) || `GetNumUnlockedOutfits()` | ESO | Count unlocked outfits (takes **no** arguments) || `GetOutfitName(outfitIndex)` | ESO | Outfit display name for `/outfitids` || `ZO_OUTFIT_MANAGER:GetOutfitManipulator(actorCategory, outfitIndex)` | ESO | Access outfit slot data (actorCategory is required) || `outfitManipulator:GetSlotManipulator(OUTFIT_SLOT_*)` | ESO | Access individual outfit slots || `slotManipulator:GetCurrentCollectibleId()` | ESO | Collectible ID on a given outfit slot || `GetItemLink(bagId, slotIndex, linkStyle)` | ESO | Item link string for a worn equipment slot || `GetItemLinkItemStyle(itemLink)` | ESO | Visual style ID for any item regardless of origin || `BAG_WORN` | ESO | Constant the equipped item bag || `EQUIP_SLOT_*` | ESO | Equipment slot constants || `OUTFIT_SLOT_*` | ESO | Outfit slot constants || `COLLECTIBLE_CATEGORY_TYPE_COSTUME` | ESO | Collectible category constant || `ZO_SavedVars:NewAccountWide(…)` | ESO | Account-wide saved variables || `EVENT_MANAGER:RegisterForEvent(…)` | ESO | Addon load event || `CHAT_SYSTEM:AddMessage(…)` | ESO | Chat output || `SLASH_COMMANDS` | ESO | Slash command registration || `LibAddonMenu2:RegisterAddonPanel(…)` | LAM | Settings panel registration || `LibAddonMenu2:RegisterOptionControls(…)` | LAM | Settings controls |> **`GetItemLinkItemStyle` vs `GetItemStyle`:** `GetItemStyle()` only returns non-zero for player-crafted items; every dropped, set, or Crown Store piece returns `ITEM_STYLE_NONE` (0). `GetItemLinkItemStyle(itemLink)` returns the correct visual style for all item origins and is the correct choice here.>> **`GetNumUnlockedOutfits()`:** Takes no arguments. A `GameplayActorCategory` parameter does not exist on this function. `GetNumOutfits()` is not a real ESO API function and will crash with “function expected instead of nil”.>> **`GetEquippedOutfitIndex()`:** The correct gate for the outfit path. Confirmed working in-game; returns the 1-based index of the worn outfit, or nil if none.—## Design Notes & Limitations**No per-item flavor text scraping.** ESO does not provide a Lua API to read raw item flavor text at runtime (the tooltip system renders it as formatted strings, not structured data). The `flavorBonus` values in the style database are therefore authored by hand based on in-game style descriptions and motif book lore.**Item set names are ignored.** The library uses only the base `ITEM_STYLE_*` of each piece, not the set it belongs to. Set bonuses are mechanical gameplay systems unrelated to visual insulation.**Outfit “no override” slots fall through.** If a player has an outfit active but has not overridden every slot, only overridden slots contribute from the outfit source. If *no* outfit slots are overridden, the library falls through to worn armor styles entirely.**Costume vs. outfit visual conflict.** If a player has both a costume collectible active and an outfit applied, the costume wins visually in-game (costumes always take priority). LibArmorInsulation mirrors this by detecting costumes before outfits.**Polymorphs override costumes.** A polymorph replaces the entire character model. LibArmorInsulation checks for polymorphs before costumes, consistent with ESO’s in-game visual priority.**Outfit collectible IDs ≠ ITEM_STYLE_* IDs.** Outfit slots yield Collections-system collectible IDs, which are an entirely separate number space from `ITEM_STYLE_*` integers. The library maintains separate lookup tables (`OutfitStyles` and `Styles`) for this reason, and the override key prefix (`outfit_` vs `style_`) distinguishes them in saved variables.**Costume cache is opt-in.** The `sv.costumeCache` table is only populated when you run `/scancostumes`. Until then, unrecognized costumes fall back to `DEFAULT_COSTUME` (50). Cache entries persist across sessions.**Saved variables version 4.** The upgrade path from v3 preserves existing overrides and `costumeCache`, and adds the missing `outfitStyleCache` table. The upgrade path from v2 preserves overrides and adds both cache tables. Saves from v1 are reset to defaults to avoid schema conflicts.—## Changelog### 2.7.5- **Fixed the settings panel’s author/version fields**, which were still the literal placeholder text `”YourName”` and a hardcoded `”1.0.0″` never actually updated even after the `## Author` manifest field itself was corrected. The panel now reads `author = “@Kreksar5 and Claude.ai”` and `version = LibArmorInsulation.VERSION`, and the library’s internal `ADDON_VERSION` constant (previously stale at `”2.7.0″`) is kept in sync with the manifest `## Version` going forward.### 2.7.4- **Promoted 4 costumes and 11 outfit styles from verified player overrides** into the hard-coded data tables (`CostumeInsulationById` and `OutfitStyles`), so these no longer rely on a per-account manual override or auto-rated cache guess: – `CostumeInsulationById` gained 4 entries (collectible IDs 12717, 1101, 12719, 286) with their confirmed `totalInsulation` values, each superseding a previous auto-rated cache guess. – `OutfitStyles` gained 11 entries (collectible IDs 4804, 4798, 4800, 9746, 7195, 8786, 11819, 11818, 11820, 11851, 5309). The final tier for each is confirmed correct (set manually by a player after observing the outfit in-game), but since this table stores `baseMaterial`/`coverage`/ `flavorBonus` rather than a direct tier, those fields were chosen only as the minimal combination that reproduces the confirmed tier they are **not** an independent material assessment, and the collectibles’ actual names/appearances haven’t been looked up yet. Flagged accordingly in each entry’s `flavorNote`.### 2.7.3- **Standardized the author credit** to `@Kreksar5 and Claude.ai` in the `## Author` manifest field and this README’s byline, and named Claude.ai specifically (rather than generic “AI assistance”) in the manifest description and the AI-assistance disclosure above, matching the crediting convention used across this addon’s companion libraries.### 2.7.2- **Corrected the `## Author` manifest field**, which was still the literal placeholder text “YourName” left over from the manifest template never actually filled in. Fixed to the real author.### 2.7.1- **Full API audit against the official ESOUI API 101050 documentation.** Every function call, method call, and constant referenced across the library was cross-checked against the API doc and, where the doc didn’t cover it (manager singletons, third-party libraries), against the live ESOUI source. No invalid or deprecated API usage found `ZO_OUTFIT_MANAGER` and its `GetOutfitManipulator`/`GetSlotManipulator`/`GetCurrentCollectibleId` chain, and the `LibAddonMenu`/`LibSavedVars` calls, are all legitimate and correctly used. No code changes required.### 2.7.0- **Override editor redesigned around explicit Layer/Slot dropdowns.** Replaced the auto-detected “currently active item” list (and the 2.6.3 background poll that tried to keep it in sync) with two small, permanently static dropdowns: **Layer** (Costume/Polymorph, Outfit, Armor) and **Slot** (Head/Shoulders/Chest/Hands/Waist/Legs/Feet, disabled for Costume/Polymorph since that’s whole-body). Picking either does a FRESH live lookup at that exact moment via three new independent Calculator functions `Calc.ResolveCostumeOrPolymorph()`, `Calc.ResolveOutfitSlot()`, `Calc.ResolveArmorSlot()` each of which reads live game state directly and completely ignores ESO’s usual visual precedence. This means you can now inspect (and override) your Armor style on a slot even while a costume is actively displayed instead, or check what your Outfit has set for a slot regardless of what’s currently showing. If a layer/slot has nothing active (e.g. Costume picked while no costume is worn, or an empty armor slot), a placeholder tier and an explanatory note are shown instead of leaving the fields blank. A **Refresh Preview** button re-runs the same live lookup for whenever gear changes without touching either dropdown.- **Root-cause bug fix: Settings panel re-registration was silently a no-op.** The options table powering the whole Settings panel was declared as `local optionsTable = { …, func = function() … optionsTable … end, … }`. In Lua, a local variable’s scope begins only AFTER its declaration statement finishes, so any closure written INSIDE that table constructor which refers to `optionsTable` which is exactly what the Refresh/Apply Override/Reset buttons did to push a rebuilt panel back to LibAddonMenu actually resolved to a global of that name (`nil`), not the local table being built. Every one of those buttons had therefore been calling `LAM:RegisterOptionControls(Settings.panelId, nil)` this whole time. This is almost certainly the real reason the Settings panel never seemed to refresh, going back to before the tier-system rewrite. Fixed by forward-declaring `local optionsTable` on its own line before assigning the table to it, which is the standard Lua idiom for self-referencing structures; verified with a minimal isolated reproduction before and after the fix, and with an end-to-end test through the actual button handlers.### 2.6.3- **Background auto-refresh for the “currently active item” dropdown.** Previously the list only reflected equipped gear/costume/outfit at the moment the addon loaded, or whenever Refresh/Apply Override/Reset was pressed changing costume or outfit mid-session left it stale until one of those was clicked. Added a lightweight poll (`EVENT_MANAGER:RegisterForUpdate`, every 2 seconds) that compares a cheap fingerprint of “what’s currently resolving” (source + costume/polymorph ID + each slot’s collectible/style ID) against the last-seen one, and only pushes a rebuilt options table back to LibAddonMenu when something actually changed. This deliberately avoids hooking any specific ESO gear/costume-changed event, since there’s no single confirmed event that reliably fires for every costume/outfit/armor change alike and this addon’s data tables hold to a no-unverified-API-constants standard polling a signature built from data this file already computes gets the same result using only a bedrock-stable API. Updates land within ~2 seconds of a costume/outfit/armor change, whether or not the Settings panel happens to be open at the time.### 2.6.2- **Bug fix: costume/polymorph misclassification after applying an override.** Costumes and polymorphs share the same override key namespace (`costume_`) and the same detection path (`GetActiveCollectibleByType`), since ESO has no separate `COLLECTIBLE_CATEGORY_TYPE_POLYMORPH`. A prior fix that let polymorph overrides win over the data table accidentally checked `if polymorphOverride or polymorphEntry then` meaning applying an override to a *plain costume* made it get misclassified as a polymorph on the very next lookup (`result.source` flipped from `”costume”` to `”polymorph”`, and the breakdown slot key flipped from `””` to `””`). This is what caused the “Prefill from currently active item” dropdown in Settings to mislabel a costume as a “Polymorph” once you’d set an override on it. Classification now comes ONLY from table membership (`PolymorphInsulationById`/`PolymorphInsulation`), independent of whether an override exists; the override still applies to the resolved tier either way. The insulation *value* was never wrong only the source/label but the label feeds directly into the prefill dropdown and `/insulation`/`/costumeids` output, so this is worth updating for.### 2.6.1- **Override editor prefill.** Added a “Prefill from currently active item” dropdown above the Override Type/ID fields, listing whatever costume, polymorph, outfit piece, or armor style is *actually resolving right now* using the exact same precedence `GetInsulationBreakdown()` uses (polymorph/ costume beats outfit beats worn armor; an outfit slot with no style set falls back to the armor style showing through). Picking an entry auto-fills Override Type, the ID field, and the Insulation Tier dropdown with that item’s current resolved values, and shows the game’s own display name (`GetCollectibleName()` for outfits/costumes, the resolved English style name for armor) so there’s no need to cross-reference `/styleids`, `/outfitids`, or `/costumeids` by hand. “(manual entry)” remains available as the first choice for typing an arbitrary ID. The list refreshes whenever the panel opens, when **Refresh** is pressed, when an override is applied, or when overrides are reset.### 2.6.0- **Staggered tier system.** Replaced the free-floating 0100 continuous scale with eleven fixed tiers, staggered in increments of 10 from -10 to 90 (Magically Cooled, No Insulation, Minimal, Light, Coarse Light, Layered/Leather, Full Leather/Light Metal, Heavy Leather/Fur, Full Fur/Metal, Heavy Fur/Metal, Magically Heated). Every style, outfit piece, costume, and polymorph now resolves to one of these tiers via `Calc.SnapToTier()`. `-10`/`90` are reserved for entries flagged `magical = true` (Flame Atronach, Ice Wraith); everything else is clamped to the mundane 080 range before snapping.- **Per-slot percentage adjustment.** A style/outfit piece’s tier is now a full-body reference value; individual slots contribute a *percentage* of that tier based on body coverage (`slot_contribution = round(tier slotPercentage)`). `SlotCoverage`/`OutfitSlotCoverage` were rebalanced from summing to 0.90 up to exactly 1.00, so a full matching set reproduces its tier exactly as the total. Costumes and polymorphs are **not** slot-adjusted their snapped tier is the total, unadjusted, since they’re whole-body.- **Dropdown-based override editor.** The Settings panel’s “Insulation value” free-text editbox was replaced with an “Insulation tier” dropdown listing the eleven tiers by name, so an override can never drift off-tier. Programmatic `SetOverride()` calls snap any value passed in to the nearest tier instead of clamping 0100; `nil` (not `0`) now clears an override, since `0` is a legitimate tier (“No Insulation”).- **Saved-variable migration (v4 → v5).** Existing overrides from the old 0100 scale are snapped onto the nearest tier on first load rather than being discarded, preserving prior manual tuning as closely as the new scale allows.- **Bug fix:** `NearestMaterialForTarget()` (used by `/scanoutfitstyles` to guess a base material for unrecognized outfit pieces) was missing the 0.90 slot-weight-sum factor present in every other full-body reference calculation in this file, so its reference numbers (fur=100, leather=55.6, …) didn’t match the documented full-set reference table (fur=90, leather=50, …). Fixed to multiply by 0.90 like everywhere else.- `GetInsulationForStyle()` now simply delegates to the new `Calc.GetStyleTier()` instead of duplicating the formula.- Version bumped to 2.6.0 (`AddOnVersion: 8`), `SAVED_VARS_VERSION` to 5.### 2.5.3- **Corrected `LibAddonMenu-2.0` dependency floor**: was mistakenly bumped to `>=45` in 2.5.1, but LibAddonMenu’s own manifest documentation shows `43` as the latest available version, not `45`. Fixed to `>=43`. The 2.5.1 entry below has also been corrected to reflect the right number.### 2.5.2- **”What addons cannot do” compliance verified** against the actual ESOUI rules text (previously unverifiable due to a fetch issue with that specific thread see 2.5.1’s note). Checked all 24 prohibited-behavior items: this addon doesn’t touch the character-select/crown-store/scrying/Tribute UI, doesn’t read other players’ or NPCs’ positions, doesn’t change any visual effect/texture/sound/nameplate/camera/tooltip, doesn’t move/cast/interact with anything, doesn’t touch quickslot items, doesn’t write outside `SavedVariables`, doesn’t talk to external web services, and doesn’t share data between players. It’s a pure read-only calculator using documented API functions (the explicitly sanctioned “what addons could do” item #7), plus a settings panel and slash commands. **No code changes were required.** – Specifically confirmed: `CHAT_SYSTEM:AddMessage()`, used throughout this addon’s slash commands, is the standard local-only system-message print and is **not** the prohibited “send messages to chat directly” behavior (that rule targets auto-sending player-authored text into channels other players see, e.g. simulating Enter on `/say` or guild chat this addon never does that).### 2.5.1- **ESOUI release-rules compliance pass**, checked against the ESOUI “Please read: Before you release a new/update your addons” guidelines: – **AI-code disclosure added.** Per the rule requiring AI involvement to be named at the top of the addon description when the AI-written code/data hasn’t been independently validated, added a disclosure to both the manifest `## Description` and the top of this README. Most of this addon’s commands and data tables were built with AI assistance and are not yet confirmed working/performant in actual gameplay. – **Added the missing `## AddOnVersion` manifest tag.** Libraries need this so dependent addons can use `DependsOn: LibArmorInsulation>=N` version checks (the same mechanism this addon already uses for its own `LibAddonMenu-2.0` dependency). Starting value: `6` (one per tracked release so far); increment by 1 on every future release regardless of the semantic `## Version` string. – **`## APIVersion` updated** from the stale `101040` to `101049 101050` (current Live and PTS as of this pass), so the addon no longer shows as “Outdated” in-game. – **`LibAddonMenu-2.0` dependency floor bumped** from `>=33` to `>=43`, matching the version explicitly cited as current in the ESOUI rules thread at time of this pass. `>=` (rather than no version check) is required so the addon doesn’t silently load against a pre-Summerset LibStub-only build of LAM. – **Reviewed for global variable leaks** none found. The codebase already follows the recommended single-global-table pattern (`LibArmorInsulation`, `LibArmorInsulationData`, `LibArmorInsulationCalc`, `LibArmorInsulationSettings`), with all other identifiers declared `local`. – **Not independently verified this pass:** the “What AddOns must not do” rules thread (linked from the same guidelines post) could not be fetched to confirm compliance line-by-line at the time. **Resolved in 2.5.2** once the actual rules text was provided directly see that entry for the confirmed result.### 2.5.0- **152 manually-researched costume entries added to `CostumeInsulation`.** Sourced from UESP search-result snippets (direct page fetches are blocked by UESP’s bot detection, so coverage is best-effort rather than exhaustive roughly 134 of ~308 total ESO costumes were found with usable description text; the rest were left for `/scancostumes` to auto-rate). Each entry’s `flavorNote` carries a “ tag: – **high** the source text stated an explicit material (e.g. “studded leather doublet,” “Scaly Cloth Scraps,” “Fighters Guild Cool-Weather Gear”). – **medium** a strong contextual/regional inference (e.g. a scout outfit explicitly tied to a named cold-climate zone). – **low** a thematic guess with no explicit material cue in the source; these are reasonable starting points but are good candidates for manual re-verification, and `/scancostumes` will not override them since `CostumeInsulation` now outranks the scanner cache (see below). – One entry (**Old Orsinium Sentry**) corrects an earlier same-session guess after better source text was found later in the research pass.- **Costume resolver priority reordered.** `CostumeInsulation` (this manually-curated table) now outranks `sv.costumeCache` (the `/scancostumes` runtime cache) in `GetInsulationBreakdown()`. New priority order: user override → `CostumeInsulationById` → `CostumeInsulation` → `sv.costumeCache` → `DEFAULT_COSTUME`. Previously the scanner cache outranked this table, which meant a manually-verified entry could be silently shadowed by a coarser auto-rated guess.- **`OutfitStyles` gains a `Styles` fallback.** Most outfit-style collectibles are account-wide unlockable versions of existing crafting motifs, just reached through a different ID space. `GetOutfitStyleData()` now resolves an unrecognized collectible ID’s name via `GetCollectibleName()` (stripping a trailing `” Style”` suffix) and checks `Styles` before falling back to the `/scanoutfitstyles` cache so a style whose name matches an existing motif entry needs no new per-ID data at all. `/scanoutfitstyles` reports these as “ in its output.- **9 new motifs added to `Styles`** to close a coverage gap found while researching outfit styles: Swordthane, Dark Executioner, Necrom Armiger, Order of the Lamp, Ancient Mirrormoor, Companion Revelry, Fharun Moonlight, Chosen of Anu, Chosen of Padomay. These are Crown Crate/Collector’s Edition unlocks rather than world-found motif books, so they postdate the original “Motifs 1129” coverage. Material assignments are UESP-sourced where the style explicitly resembles an existing entry (e.g. Dark Executioner → resembles Dark Brotherhood armor); the rest are lower-confidence thematic inferences from cosmic-duality/regional lore with no explicit material description available.### 2.4.0- **`/scancostumes` now scans description text, not just name.** `AutoRate` checks the costume’s name against the keyword list first (names are usually more deliberately worded), then falls back to `GetCollectibleDescription()` flavor text if the name has no hit. Each cache entry now records a `matchSource` (`”name”`, `”description”`, or `”none”`) so genuinely unmatched entries sitting on the generic 50 fallback can be identified.- **New `/scanoutfitstyles` command.** Outfit style pages had no auto-detection path at all (`OutfitStyles` was a hand-only stub table). This walks every unlocked outfit’s 7 slots (same enumeration as `/outfitids`) and, for any collectible ID not already in `OutfitStyles`, keyword-rates it via name + description and writes a guessed `{baseMaterial, coverage, flavorBonus}` into the new `sv.outfitStyleCache`. `GetOutfitStyleData()` now checks this cache as a fallback before `OutfitStyles`.- **New `/costumesneedreview` and `/outfitstylesneedreview` commands.** Filter each respective cache down to entries where the keyword scan found no match in either name or description a short worklist for manual UESP-backed overrides instead of scrolling the full scan output.- **Shared keyword table.** `KEYWORD_RATINGS` and `AutoRate` were hoisted out of `/scancostumes` to file scope so `/scanoutfitstyles` draws from the same list a new pattern only needs adding once.- **Removed an unused dynamic costume-discovery path.** An earlier in-progress approach (`BuildCostumeKeywordMap()`, brute-force probing every collectible ID 1→N) was removed before release it would have silently outranked the better-designed `/scancostumes` + `sv.costumeCache` resolution order. Not shipped in any prior version; mentioned here for the record.- **Saved variables bumped to version 4;** v2/v3 saves are migrated automatically, preserving existing overrides and caches.### 2.3.0- **Name-keyed style table.** `LibArmorInsulationData.Styles` is now keyed by the English name returned by `GetItemStyleName()` (e.g. “, “) instead of raw `StyleItemIndex` integers. This mirrors the zone-ID lesson: style integers can be reassigned between ESO API updates, but the English name is stable.- **Runtime ID maps built at login.** `LibArmorInsulationCalc.BuildStyleIdMaps()` is called during `EVENT_ADD_ON_LOADED`. It iterates `GetItemStyleName(i)` for `i = 1, GetHighestItemStyleId()` and populates two tables: – `LibArmorInsulationData.StyleIdByName` `{ = 5, }` – `LibArmorInsulationData.StyleNameById` `{ = “Nord”, }` These maps are rebuilt every login from live game data, so any future ZOS reassignment of integer IDs is handled automatically without a data-table update.- **Lookup chain in Calculator.** All style lookups now follow: `styleId → StyleNameById → Styles → DEFAULT`. The integer from `GetItemLinkItemStyle()` is still accepted everywhere in the public API; it is resolved to a name internally and never stored as a persistent key.- **`styleName` added to breakdown results.** Each slot entry in `GetInsulationBreakdown().slots` now includes a `styleName` field (string or nil) alongside the existing `styleId`, so callers can display a human-readable name without performing their own lookup.- **Override keys unchanged.** Saved-variable override keys remain `”style_N”` (integer-based) because the player set them against a specific integer at a known point in time. They continue to work the integer is used only as a unique token for the saved-variable key, not for a data-table lookup.### 2.2.0- **Full style coverage:** `LibArmorInsulationData.Styles` now contains entries for all crafting-motif style IDs known as of ESO Update 49 (motifs 1129, IDs 1129). Previously only ~67 IDs were populated; ~62 motifs from the Thieves Guild, Dark Brotherhood, Morrowind, Summerset, Elsweyr, Greymoor, Blackwood, High Isle, Necrom, and Gold Road chapters were missing entirely.- **Corrected several ID-to-name mappings** that were shifted by one due to a miscount of the gap between the racial styles (110) and the first post-racial motifs: – Telvanni was “; corrected to “ (Motif 50). – Dragonscale was “; ID 36 is Dark Brotherhood (Motif 36). Dragonscale is not a craftable motif removed; dragon-scale aesthetics are covered by individual dungeon-set pieces with no motif ID. – Snowhawk “ was an invention with no confirmed ITEM_STYLE_* counterpart; Snowhawk is an **outfit-style collectible only** (not a motif). Entry removed from `Styles`; use the `OutfitStyles` collectible table instead. – IDs 8089 ancestral styles renumbered to match confirmed Motifs 8795. – IDs 90105 updated to match Greymoor / Blackwood / High Isle motif numbering.- **Zone ID statement evaluated:** The claim that “ESO zone IDs are large non-sequential integers the only stable identifier is the English zone name `BuildZoneIdMaps()` inverts the name→ID mapping” does **not apply to style IDs** and has **no relevance to this library**. Style IDs (`StyleItemIndex` / `ITEM_STYLE_*`) are small sequential integers starting at 1 and are stable across API updates the exact opposite of zone IDs. This library does not use `LibZone`, `BuildZoneIdMaps()`, or any zone-ID machinery. No changes were required.### 2.1.0- Outfit slots now look up insulation by **collectible ID** via `ZO_OUTFIT_MANAGER` rather than by `ITEM_STYLE_*` ID, matching how ESO actually stores outfit style data internally.- Added `LibArmorInsulationData.OutfitStyles` table (keyed by collectible ID) separate from `Styles` (keyed by `ITEM_STYLE_*`).- `SetOverride` now accepts an `idType` parameter (`”style”`, `”outfit”`, `”costume”`) to disambiguate the three ID spaces.- Added `sv.costumeCache` a persistent per-account cache populated by `/scancostumes`.- New slash commands: `/costumeids`, `/scancostumes`, `/clearcostumecache`, `/outfitids`, `/outfitactive`, `/diagcollectibles`.- Added `LibArmorInsulationData.CostumeInsulationById` (collectible ID keyed) alongside the existing name-keyed `CostumeInsulation` table.- Saved variables bumped to version 3; v2 saves are migrated automatically.- Detection of active outfit changed from `GetActiveOutfitIndex()` to `GetEquippedOutfitIndex()` (confirmed correct ESO API function).### 1.0.0- Initial release.—## LicenseMIT free to use, modify, and redistribute with attribution.