Realistic Needs and Diseases (0.19.5)
# Realistic Needs and Diseases
**Version 0.19.5** ESO API 101049 101050 License: MIT
*by @Kreksar5 and Claude.ai*
> Thematically inspired by *Realistic Needs and Diseases*, the well-known
> Skyrim mod name and concept used as an homage, **not a port, and not
> affiliated with or endorsed by that mod’s authors.**
> **AI-ASSISTED ADDON, UNTESTED.** Built with Claude.ai, not yet run in a
> live client. Several pieces are explicitly flagged below as
> unverified/best-guess and need confirming in-game before you trust them.
## Credits
– **RolePlayNeeds Tamriel Survival!** (the ESOUI addon) by **matheusbk2**:
two implementation details were confirmed by inspecting its actual
published source (checked directly against the official v0.7 BETA
release) the `bindings.xml`/keybind naming convention (`RPN_CHECK_NEEDS`
action name, `RPN_CheckNeeds()` handler mirrored here as
`RND_CHECK_NEEDS`/`RND_CheckNeeds()`), and the inventory-seed-on-login fix
for reliable first-consumption detection (`ForceInventoryScan()` called
from `EVENT_PLAYER_ACTIVATED`, seeding a `lastInventoryState` table before
any inventory event can fire this addon’s equivalent is
`SeedLastSlotState()`). No code was copied; this addon is an original
implementation, not a port or derivative (see disclaimer above).
**Correction:** three other items were previously credited to
RolePlayNeeds here a `RPN_REAGENT_TRAITS` alchemy-trait lookup table, a
“two-stage” water-loot detection fix, and a labeled-row HUD/window
layout. None of these exist in RolePlayNeeds’ actual published source.
They were mistakenly attributed to RPN when they were really from a
local, personally-modified copy of RolePlayNeeds that was never
published the credit has been removed, and the affected code comments
in `RealisticNeedsAndDiseases_Data.lua` and `RealisticNeedsAndDiseases.lua`
corrected to no longer claim RPN corroboration for that data.
– **No Interact** by **Rhyono**: the world-object “Sit” interaction detection
(`RETICLE:TryHandlingInteraction` hook and the `INTERACTIVE_WHEEL_MANAGER`/
formerly `FISHING_MANAGER` reassignment technique for catching interact
keypresses) follows the same technique this addon uses to read reticle
state and intercept interactions. No code was copied.
A from-scratch survival-needs and disease addon for ESO: hunger, thirst, and
fatigue meters that decay naturally over time, accelerated (never slowed) by
environmental temperature, plus 5 independently-trackable diseases cured by
specific alchemy ingredients rather than potions.
This is an original implementation not a patch or derivative of
RolePlayNeeds (or anything else). See `RealisticNeedsAndDiseases_design_notes`
section below for what changed across this build’s iterations.
—
## Needs
– **Hunger**, **Thirst**, **Fatigue**, each 0100. All decay/restore rates and
warning thresholds below are now **player-adjustable in Settings**.
– **Decay is always-on at a natural baseline rate** (default: hunger empties
in 4h, thirst in 3h, fatigue in 6h all adjustable). Temperature never
reduces decay below that baseline, it only ever accelerates it on top.
Inside the comfort band (10C25C), you get pure baseline decay. Outside
it, the relevant meter(s) drain up to 22.5x faster.
– Hot environments → thirst accelerates.
– Cold environments → hunger and fatigue accelerate.
– Eating restores hunger (default +30, adjustable), drinking restores thirst
(default +30, adjustable).
– **Harvesting an alchemy water node in the wild also restores thirst
directly** (default +15, adjustable) uses the same confirmed
alchemy-water-solvent item IDs already validated elsewhere in this project
(883, 1187, 4570, 2326523268, 64500, 64501), via `EVENT_LOOT_RECEIVED`.
⚠️ This is a simpler single-stage v1 see the in-code warning about the
event’s unverified parameter order. (An earlier version of this note
claimed RolePlayNeeds’ published source needed a two-stage detection
pattern for this; that wasn’t accurate see the correction in Credits.)
– Temperature source: `Frostfall:GetEffectiveTemp()` preferred,
`LibZoneTemp.GetCurrentTemperature()` fallback, flat baseline otherwise.
### How fatigue works (and how to recover)
Fatigue is the simplest of the three meters by design there’s no “activity
tracking” (no movement-speed sampling, no combat-time accounting, unlike
RolePlayNeeds’ approach). It’s purely:
1. **Time-based baseline drain.** Fatigue ticks down at a flat natural rate
(default: empties over 6 hours of played time) every 5-second tick,
identical in mechanism to hunger and thirst there’s nothing fatigue-
specific about *what* drains it, only the rate.
2. **Temperature-accelerated, in both directions.** Unlike hunger (cold-only)
and thirst (hot-only), fatigue accelerates at **either** temperature
extreme being too cold *or* too hot tires you out faster, up to 2x the
baseline rate, ramping over the same 20C-beyond-comfort-band range as the
other two meters (see `Calculator.GetFatigueDecayRate()`).
3. **Recovery now exists (as of 0.7.0)** three mechanics, all gated on not
moving and being out of combat: standing still for a few minutes (slow
regen), sitting for about a minute (faster regen), or sleeping/meditating
for a while (full restore, with an optional fade-to-black effect). See the
“Fatigue recovery” section below for details. Before 0.7.0, this section
used to note fatigue recovery as a missing/incomplete design that gap is
now closed.
—
### Overlay art credit
The disease status-overlay images (`Frostbite.dds`, `Heatstroke.dds`,
`MagesBane.dds`, `FightersBane.dds`, `ThiefsBane.dds`) were generated using
AI text-to-image tools (https://perchance.org/ai-text-to-image-generator) and Google’s
Gemini Image Creator and edited/converted to the game’s `.dds` format
using Paint.NET. They are not hand-drawn or sourced from ESO’s own art
assets.
## Diseases
5 independent diseases any subset active simultaneously, each at its own
severity tier (Mild / Moderate / Severe). Contraction chances are all
player-adjustable in Settings.
| Disease | Trigger | Trait | Tint |
|—|—|—|—|
| Frostbite | Sustained cold exposure (5 min) | Protection | Pale blue |
| Heatstroke | Sustained hot exposure (5 min) | Protection | Warm orange |
| Mage’s Bane | Taking Magic/Fire/Cold/Shock damage (chance per hit) | Restore Magicka | Purple |
| Fighter’s Bane | Taking Physical damage (chance per hit) | Restore Health | Bruised red |
| Thief’s Bane | Taking Poison/Disease damage (chance per hit) | Restore Stamina | Sickly yellow-green |
### Two trigger mechanisms
**Frostbite and Heatstroke** use sustained exposure: once the player’s
effective temperature (Frostfall-preferred, LibZoneTemp-fallback see
`Calculator.GetCurrentTemperature()`) has sat outside the comfort band
continuously for 5 minutes, the disease becomes possible and re-rolls every
60 seconds for as long as the exposure continues, until contracted or the
player leaves the trigger range. Settings caps these 050% per roll.
**Mage’s Bane, Fighter’s Bane, and Thief’s Bane** are each rolled on a
qualifying hit of real typed combat damage instead no exposure timer.
`Disease.lua` checks `damageType` on `EVENT_COMBAT_EVENT` against a
`DAMAGE_TYPE_TO_DISEASES` lookup (Magic/Fire/Cold/Shock → Mage’s Bane,
Physical → Fighter’s Bane, Poison/Disease → Thief’s Bane) the game’s own
real damage-type classification for the hit, not a name-keyword guess.
Since combat damage can land far more often than the exposure re-roll
interval, Settings caps these much lower: 010% per hit, in 0.5% steps.
⚠️ The combat event’s parameter order and several of these `DAMAGE_TYPE_*`
constants are unverified against a live client see in-code comments and
`/rnd debug combat on`.
### Curing: real ingredients, sourced from UESP
Each disease is cured by raw alchemy **ingredients** (never potions), and
**each disease has its own distinct trait** with 3 tiers of real,
UESP-verified ingredients (common → rare):
| Disease | Trait | Tier 1 | Tier 2 | Tier 3 |
|—|—|—|—|—|
| Frostbite | Protection | Mudcrab Chitin | Beetle Scuttle | Vile Coagulant *or* Powdered Mother of Pearl |
| Heatstroke | Protection | Mudcrab Chitin | Beetle Scuttle | Vile Coagulant *or* Powdered Mother of Pearl |
| Mage’s Bane | Restore Magicka | Corn Flower | Lady’s Smock | Vile Coagulant (Harrowstorms) |
| Fighter’s Bane | Restore Health | Mountain Flower | Water Hyacinth | Crimson Nirnroot (Blackreach) |
| Thief’s Bane | Restore Stamina | Blessed Thistle | Chaurus Egg | Dragon’s Blood (Dragons) |
A tier-N ingredient cures that disease at severity ≤ N (a rare ingredient can
overkill-cure a mild case; a common one can’t touch a severe case).
**The ingredient names/traits/sources above are real, verified data from
UESP, and every `itemId` in `RealisticNeedsAndDiseases_Data.lua` has already
been filled in from an in-game scan except Thief’s Bane’s tier-3 Dragon’s
Blood**, which is still a placeholder (`150731`, UESP-sourced but not yet
confirmed against a real scan, since the player hasn’t had one to check).
### Care-cure: an alternative path for stronger afflictions
Instead of needing the rare tier-matched ingredient outright, a Severe or
Moderate case can also be cured gradually: eating the disease’s **tier-1
(cheapest) ingredient repeatedly while hunger, thirst, AND fatigue are all
above a threshold** (default 70/100, adjustable) builds “care-cure progress.”
Once enough doses accumulate (default 5, adjustable), the disease downgrades
one severity tier Severe → Moderate → Mild → cured resetting progress
each step. Slower and requires sustained good self-care, but only ever needs
the cheap ingredient. Check progress anytime with `/rnd checkneeds`.
—
## Status window
A Frostfall-style colored-text window same structural pattern as
Frostfall’s own `TemperatureHUD.lua`: a draggable top-level window with a
tooltip-style backdrop, four label+value+status rows (HUNGER/THIRST/
FATIGUE/DRUNKENNESS), each showing a large number colored on a
red→yellow→green ramp, **plus the current band status message text**
(e.g. “I’m hungry.”) underneath each number, colored the same as the number.
A DISEASES section below lists each active disease’s name, severity, and a
text color matching that disease’s overlay tint.
*(This row layout is inherited directly from this developer’s own Frostfall
addon, not from RolePlayNeeds RolePlayNeeds’ actual UI is a set of small
per-meter icon windows, not labeled text rows. An earlier version of this
README incorrectly implied the row layout traced back to RolePlayNeeds; see
the correction in Credits above.)*
– Drunkenness’s color ramp is inverted (high = red/bad, low = green/good)
the only row where that’s the case.
– Drag the window anywhere position persists across sessions
(`sv.settings.statusBarPosition`), with a “Reset status window position”
button in Settings if it ever ends up off-screen.
– **Toggle the whole window on/off** via the “Show needs status window”
checkbox in Settings.
– The window resizes dynamically to fit however many diseases are currently
active (0 to 5).
—
## Settings panel
Everything below is adjustable without editing code:
– Temperature-coupling on/off, hours-to-empty for each meter, restore amounts
for food/drink/harvest/coffee, warning thresholds, status bar visibility.
– Contraction chance per disease (the three combat-damage diseases Mage’s
Bane, Fighter’s Bane, Thief’s Bane capped lower than Frostbite/
Heatstroke, reflecting how much more often a hit can land than an
exposure re-roll).
– Care-cure “well cared for” threshold and doses-required.
– Per-category emote choice (dropdown, real account-aware enumeration see
Feedback section below) and an overall emote enable/disable toggle.
– Drunkenness gain-per-drink, threshold, unaided sober-up time, and
resting sober-up multiplier.
– Fatigue recovery thresholds for all three resting mechanics, plus the
fade-to-black toggle.
Debug/reset actions live in `/rnd debug` now, not Settings buttons see
Slash Commands below.
—
## Dependencies
“`
## DependsOn: LibAddonMenu-2.0>=43
## OptionalDependsOn: Frostfall>=4 LibZoneTemp>=10
“`
—
## Slash Commands
All commands are unified under `/rnd`.
| Command | Description |
|—|—|
| `/rnd checkneeds` | Prints current hunger/thirst/fatigue/drunkenness, active temperature source, and each active disease’s severity plus care-cure progress. |
| `/rnd debug` (no args) | Prints the full list of debug subcommands below. |
### `/rnd debug` subcommands
| Subcommand | What it does |
|—|—|
| `/rnd debug ingredients` | Scans your backpack (and bank, if open) for any item whose name matches one of the ingredient names recorded in `Data.lua`. For each match: prints the real `itemId`, applies it in-memory **for this session only**, and prints a ready-to-paste line for `Data.lua`. |
| `/rnd debug combat on` / `off` | Dumps raw `EVENT_COMBAT_EVENT` args to chat for hits taken, confirming parameter order and showing the real `damageType` flags when it matches `DAMAGE_TYPE_DISEASE`. |
| `/rnd debug loot on` / `off` | Dumps raw `EVENT_LOOT_RECEIVED` args to chat for everything looted/harvested, confirming parameter order and `itemId` position. |
| `/rnd debug disease <1-5> <0-3>` | Directly sets a disease’s severity for testing, bypassing normal contraction rolls entirely. Index: 1=Frostbite, 2=Heatstroke, 3=Mage’s Bane, 4=Fighter’s Bane, 5=Thief’s Bane. Severity 0 clears it. |
| `/rnd debug curedisease` | Clears every active disease. Does **not** touch needs. |
| `/rnd debug resetneeds` | Resets hunger/thirst/fatigue/drunkenness to full/sober. Does **not** touch diseases. |
| `/rnd debug emptyNeeds` | Inverse of `resetneeds` drops hunger/thirst/fatigue to 0 and drunkenness to 100 (its worst value), for quickly testing the recovery mechanics without waiting for real decay. Does **not** touch diseases. |
| `/rnd debug testemote ` | Immediately plays the currently-configured emote for that category, bypassing the tick timer for previewing your Settings choice. |
(`/rnd debug curedisease` and `/rnd debug resetneeds` replace the previous
combined `/rnd debug cureall` split so you can clear one without affecting
the other. The previous `/realneeds` alias has also been removed; use
`/rnd checkneeds`.)
None of the debug subcommands run on their own; every one is opt-in.
—
## Feedback system
Restructured to **mirror Frostfall’s own hot/cold feedback architecture**
(this project’s own prior work, reused the way the inventory-consumption
pattern was reused elsewhere) both the notification mechanism and the
emote system are now built the same way Frostfall builds theirs, just with
different thresholds/categories/values.
### Notifications `ZO_Alert`, not `CENTER_SCREEN_ANNOUNCE`
`CENTER_SCREEN_ANNOUNCE` is gone. Notifications now use
`ZO_Alert(UI_ALERT_CATEGORY_ALERT, nil, text)` the exact call Frostfall
uses for its own hot/cold messages, confirmed working in this project’s own
code already, not a best-effort guess.
### Status bands 4 fixed bands per category, dividing 0-100 equally
As of 0.9.0, each of hunger/thirst/fatigue/drunkenness has its own 4-band
status message system, with **every band transition (worsening or
improving) firing its own notification** closer to Frostfall’s actual
8-band design than the earlier binary normal/critical version of this file
was. Bands are fixed quartiles of the 0-100 range not tied to any
adjustable threshold setting anymore (the old warning-threshold sliders are
gone, superseded by this).
| Band | Hunger (75-100 → 0-25) | Thirst | Fatigue | Drunkenness (0-25 → 75-100) |
|—|—|—|—|—|
| 1 (best) | “I’m stuffed!” | “My thirst is quenched!” | “I’m well rested!” | “I’m completely sober.” |
| 2 | “I could use a snack.” | “I’m a bit thirsty.” | “I could use a break.” | “I’m-*hic*-a bit tipsy.” |
| 3 | “I’m hungry.” | “I need a drink.” | “I’m tired, I should find a place to sleep.” | “I’m-*hic*-nah’ drunk, you-*hic*-you’re drunk!” |
| 4 (worst) | “I’m starving!” | “I’m dehydrated!” | “I’m exhausted! I might pass out soon!” | “WOOOOOO! I-*hic*-cannaht-*hic*-feel ma facsh!” |
Bands 1-2 = value 50-100 (or 0-50 for drunkenness); bands 3-4 = the bottom
half of badness. **Emotes only play at bands 3 and 4** bands 1-2 update
the status window and fire notifications, but never trigger an emote.
### Emotes real `PLAYER_EMOTE_MANAGER` enumeration, not guessed indices or hijacked commands
Two designs ago this used guessed numeric `PlayEmoteByIndex` indices (mostly
unconfirmed). Last version switched to hijacking `SLASH_COMMANDS`
strings. **This version replaces both** with the same real, confirmed
mechanism Frostfall already uses:
`PLAYER_EMOTE_MANAGER:GetEmoteListForType(category)` /
`:GetEmoteItemInfo(emoteId)` to enumerate every emote the player actually
owns (including personality-overridden variants), `FindEmoteIdByDisplayName`
to resolve a default by name with a slash-name fallback, and
`GetEmoteIndex(emoteId) → PlayEmoteByIndex(index)` to actually play it. This
is the real, accountable sense of “use the game’s API to look up available
options” an enumeration call already proven to work in this project,
not a filter over a hand-curated list.
A dedicated, deterministic 20-second timer (Frostfall’s own equivalent runs
every 15 seconds deliberately different value here) checks each category’s
band and plays its configured emote once it reaches band 3 or 4, gated
on `IsUnitInCombat`, `IsMounted`, and `IsAnyMajorUIOpen` same three guards
Frostfall’s `TickEmote` uses, no random chance roll anymore.
| Category | Default emote | UESP description | Audio? |
|—|—|—|—|
| Hunger | **Angry** | “Throw your arms to the side and snarl” | No |
| Thirst | Breathless | “Hunch over with your hands on your knees and pant” | No |
| Fatigue | Yawn | “Stretch your arms and yawn” | **Yes** |
| Disease | Sickened | “Heave over and vomit” | No |
| Drunkenness | Drunk | “Teeter and reel in a drunken stupor” | No |
Hunger and thirst have no literal match in ESO’s emote list closest
thematic analogues, flagged honestly. Fatigue, disease, and drunkenness all
have genuine exact matches.
⚠️ If you’d previously played an earlier version, your saved `hunger`
emote choice may still be the old default (Headache) the new default
(Angry) only applies automatically on a fresh save, since changing a
*default* doesn’t retroactively overwrite a value you (or a previous default)
already resolved. Reselect it in Settings if you want the new default.
Events that notify (chat + `ZO_Alert`): every band transition for
hunger/thirst/fatigue/drunkenness (not just entering/leaving one critical
threshold anymore), disease contraction/escalation, both cure paths.
Harvest-restores-thirst, alcohol, and coffee messages stay chat-only
deliberately (frequent actions; an alert every time would be noisy).
—
## Drunkenness (new)
Drinking an alcoholic beverage (detected by name keyword mead, ale, wine,
rum, rotmeth, sujamma, etc., same honest name-heuristic limitation as the undead-damage
keyword list used previously) builds up drunkenness (default +15/drink,
adjustable). Unlike hunger/thirst/fatigue, **high drunkenness is the bad
state**, not low.
– Decays on its own over time (default: fully sober in ~2 hours unaided).
– **Resting accelerates sobering up** (default 4x faster) any of the three
fatigue-recovery mechanics below (standing still, sitting, sleeping) also
reduces drunkenness while active.
– The status window’s color ramp is inverted for this row specifically
(high = red/bad, low = green/good) everywhere else, high = good.
—
## Coffee (new explicitly the user’s own idea, not adapted from any third-party addon)
Drinking coffee (detected by name keyword: coffee, joe, espresso) restores
fatigue (+25 default, adjustable) **in addition to** the normal thirst
restore every drink already gives. Built fresh for this addon same
inventory-consumption-detection pattern used everywhere else here, not
borrowed implementation from any other addon.
—
## Fatigue recovery
Three mechanics, all gated on **not moving and being out of combat** (your
own position/combat state only reading your own state is allowed; the
restriction is on reading *other* units’ positions):
1. **Standing still** for several minutes (default 3, adjustable) → slow passive regen.
2. **Sitting** (any `/sit` variant, `/sitchair`, or interacting with a real in-world chair/bench) for about a minute (default 60s, adjustable) → faster regen than just standing.
3. **Sleeping/meditating** for a while (default 60s, adjustable) → full restore, with an optional fade-to-black/fade-back screen effect simulating the passage of time.
All three also accelerate drunkenness decay while active (see Drunkenness above).
For mechanic 3: `/sleep`, `/sleep2`, and `/faint` are real sleep-pose emotes.
**ESO has no literal “/meditate” emote** `/pray` and `/kneelpray` are used
as the closest thematic stand-ins, flagged honestly rather than implying a
real meditate animation exists.
Detection works by **hooking** the relevant slash commands (preserving their
original behavior, then tracking state alongside) rather than polling some
“is the player sitting/sleeping” state query I don’t have confident
knowledge of such a query existing, so instrumenting the actions we already
know are real (confirmed via UESP) is the more honest mechanism.
Sitting is *also* detected when the player interacts with a real in-world
object whose reticle prompt reads “Sit” (a placeable chair, a bench, etc.),
independent of and in addition to the `/sit`-family slash-command hooking
above. This reads the reticle’s interaction text via
`GetGameCameraInteractableActionInfo()` (the same technique the NoInteract
addon, by Rhyono, uses for reading reticle state) and uses the moment
`INTERACTIVE_WHEEL_MANAGER.StartInteraction` fires which happens on every
single interact keypress, confirmed via the current ESOUI source’s
`GAME_CAMERA_INTERACT` keybind definition as the trigger. This went through
two wrong turns before landing here: first hooking `FISHING_MANAGER.
StartInteraction` (following NoInteract’s own code too literally broke on
load once ZOS removed the standalone `FishingManager` class, and was never
actually correct even before that, since that function only ever dispatched
to the fishing radial-wheel UI), then hooking the bare global
`GameCameraInteractStart()` directly (broke because that function is
genuinely protected no addon can even read it, per the actual
in-game error). `INTERACTIVE_WHEEL_MANAGER` is the unprotected, still-existing
Lua object that replaced `FISHING_MANAGER`, and its `:StartInteraction`
method is exactly the modern equivalent of what NoInteract’s hook was
reaching for. See `Rest.lua`’s `HookWorldInteractionDetection`.
There is no RND-specific `/rnd sit` or `/rnd sleep` command anymore both
were removed once native command hooking and world-interaction detection
were confirmed reliable enough to cover sitting/sleeping on their own.
Just sit or sleep normally; RND detects it.
Entering rest mode via sitting or sleeping prints a chat-only confirmation
(“You take a seat to rest.” / “You settle in to sleep.”) regardless of which
of the three detection paths triggered it the `/sit`-/`/sleep`-family
slash-command hooks, the world-object interaction hook, or (in principle)
any future path, since the message lives in `Rest.lua`’s shared
`OnSitTriggered`/`OnSleepTriggered` rather than being duplicated at each
hook site. It only fires on the actual transition into that pose, not on
every re-trigger while already seated/sleeping, so repeatedly interacting
with the same chair doesn’t spam chat.
The fade-to-black overlay reuses the same generic top-level-window pattern
already used for disease overlays, manually stepping alpha up then down via
`EVENT_MANAGER:RegisterForUpdate` (no `ZO_AlphaAnimation`/animation-manager
API assumed, since I didn’t have confident knowledge of that API’s exact
shape either).
—
## Known gaps
– **Dragon’s Blood (Thief’s Bane tier 3)** is the only remaining unconfirmed ingredient itemId wasn’t in the player’s last scan. Run `/rnd debug ingredients` again once you have one.
– Some of Mage’s Bane’s damage-type triggers (Fire/Cold/Shock) are believed-real but individually unconfirmed against a live client resolved defensively (skipped with a chat warning, not a crash, if any turn out wrong). `DAMAGE_TYPE_MAGIC` was already confirmed elsewhere in this project.
– Combat event parameter order unverified; loot event parameter order unverified; `DAMAGE_TYPE_DISEASE` constant unverified `/rnd debug combat on` / `loot on` will confirm these.
– **`EVENT_POWER_UPDATE`’s parameter order is unverified** (the new stamina-exertion mechanic) `/rnd debug power on` will confirm.
– **`LibFoodDrinkBuff` is now a required dependency**, using its real confirmed `IsAbilityAFoodOrDrinkBuff(abilityId)` method (found by inspecting a real working usage, not guessed) as a consumption-confirmation gate. `/rnd debug libcheck` still exists for double-checking the method exists in your installed version.
– **Native command hooking for `/sit`/`/sleep` is confirmed reliable**, not just assumed: the current ESOUI source shows ZOS’s own `PLAYER_EMOTE_MANAGER` registers every native emote (including `/sit`, `/sleep`, `/sitchair`, etc.) into this exact same addon-visible `SLASH_COMMANDS` table itself. `/rnd sleep` and `/rnd sit` were removed once this, plus the world-object interaction hook below, were confirmed reliable enough to cover sitting/sleeping without an explicit command. If a future ESO client ever stops routing these through `SLASH_COMMANDS`, this path would silently stop working with no explicit-command fallback; worth keeping an eye on across major game updates.
– **World-object “Sit” interaction detection hooks `INTERACTIVE_WHEEL_MANAGER.StartInteraction`**, confirmed via the current ESOUI source’s `GAME_CAMERA_INTERACT` keybind definition to fire on every interact keypress. This took two wrong attempts to get right: `FISHING_MANAGER.StartInteraction` (copying NoInteract’s code too literally broke on load once ZOS removed the standalone `FishingManager` class, and was never actually correct even before that), then the bare global `GameCameraInteractStart()` directly (broke because it’s a genuinely protected engine function no addon can access). `INTERACTIVE_WHEEL_MANAGER` is the unprotected object that replaced `FISHING_MANAGER`. The reticle-text word-match currently only actually fires on “Sit”; “Sleep” is wired up defensively in case a bed-type interactable ever uses that exact prompt, but that hasn’t been observed in-game.
– The fade-to-black overlay’s manual alpha-stepping is untested in a live client.
– “/pray” and “/kneelpray” are thematic stand-ins for “meditate,” which has no real ESO emote.
– Alcohol and coffee detection are both name-keyword heuristics will miss any drink whose name doesn’t contain a listed keyword; expand `RN.ALCOHOL_KEYWORDS`/`RN.COFFEE_KEYWORDS` in `Data.lua` as needed.
– The exertion-reference constant (300 stamina/tick for max multiplier) and the emote interval range (5-25s) are both first-draft tuning values, not playtested.
—
## Changelog
### 0.19.5
– **Corrected a misattribution to RolePlayNeeds**, verified against the
official published v0.7 BETA release (the developer supplied the actual
release zip for comparison). Three things previously credited to
RolePlayNeeds in the README’s Credits section, and referenced as
RPN-verified in code comments, do not exist in RolePlayNeeds’ real
published source they were additions made to a local, personally-
modified copy of RolePlayNeeds, not the published addon:
– A `RPN_REAGENT_TRAITS` alchemy-trait lookup table (no such table, and
no “reagent” string at all, appears anywhere in RolePlayNeeds’ source).
– A “two-stage” water-loot detection fix (RolePlayNeeds’ actual
`EVENT_LOOT_RECEIVED` handler is single-stage, with no inventory-update
confirmation step).
– A labeled-row HUD/window layout (RolePlayNeeds’ real UI is a set of
small per-meter icon windows, structurally nothing like this addon’s
or Frostfall’s label+value+status row layout).
– Two credits were confirmed genuinely accurate and are kept: the
`bindings.xml`/keybind naming convention, and the inventory-seed-on-
login fix (`ForceInventoryScan()` on `EVENT_PLAYER_ACTIVATED`).
– Corrected the affected code comments in `RealisticNeedsAndDiseases_Data.lua`
(Mage’s Bane’s ingredient-sourcing note, which also named the wrong
ingredients Fighter’s Bane’s set, not Mage’s Bane’s own) and
`RealisticNeedsAndDiseases.lua` (the water-loot detection note) to stop
claiming RPN corroboration for data that was never actually corroborated
against it.
### 0.19.4
– **Corrected the README’s “Diseases” section, which still described the
disease roster from before the 0.14.00.16.0 overhaul** (Bone Chill, Swamp
Rot, Ashen Cough, Blood Fever, Wormwood Plague all since renamed,
redesigned, or removed) instead of the current 5: Frostbite, Heatstroke,
Mage’s Bane, Fighter’s Bane, Thief’s Bane. Also fixed:
– The overview paragraph’s disease count (“6 independently-trackable” →
“5”) and the manifest’s `## Description` (“7 diseases” → “5”).
– The trigger/trait/ingredient tables, rewritten to match
`RealisticNeedsAndDiseases_Data.lua`’s actual current definitions,
including the two real trigger mechanisms (sustained exposure for
Frostbite/Heatstroke vs. per-hit damage-type for the other three) and
their real Settings-panel chance caps (050% vs. 010%).
– The “only itemId is unverified” claim, which was stale now that nearly
every ingredient has a real in-game-scanned `itemId` only Thief’s
Bane’s tier-3 Dragon’s Blood is still unconfirmed.
– The `/rnd debug disease <1-5>` index labels, the “Known gaps” section’s
Ashen-Cough-specific bullets, the Settings-panel summary’s Wormwood
Plague reference, and a leftover `/realneeds` mention (the alias was
removed in an earlier version replaced with `/rnd checkneeds`).
– This section (and the changelog entries below it) is a historical
record and intentionally left describing the old names as they existed
at the time.
### 0.19.3
– **Fixed the settings panel’s author field**, which still read the older
`”Krek (@Kreksar5)”` form rather than the now-standardized `”@Kreksar5 and
Claude.ai”`. The panel’s `version` field already read from `RN.VERSION`
correctly; that constant is bumped alongside the manifest going forward.
### 0.19.2
– **Expanded the “Overlay art credit” note** for the disease status-overlay
images to also credit Google’s Gemini Image Creator (alongside the
Perchance AI Text-to-Image Generator) for generation, and Paint.NET for
editing/converting them to the game’s `.dds` format.
### 0.19.1
– **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.
### 0.19.0
– **Added a standalone “Enable the disease system” toggle** in Settings, next
to the disease contraction-chance sliders. Independent of the existing
master “Enable the needs/disease system” switch: turning this off stops all
new disease contraction and severity escalation (both the sustained cold/
heat exposure path and the combat-damage path funnel through the same
`RollContraction` choke point in `RealisticNeedsAndDiseases_Disease.lua`,
so gating it there covers both with one check) while leaving hunger,
thirst, fatigue, and drunkenness decaying/restoring exactly as normal.
Curing an already-active disease still works while this is off. Disease
overlay tints clear the moment this is switched off and reappear at their
correct severity when switched back on, mirroring how the master switch
already handles overlays. The `/rnd status` (`/rnd`) command now also notes
when the disease system specifically is off (separate from the existing
note for the master switch).
– **Fixed `RN.VERSION` being stuck at `”0.18.0″`** in
`RealisticNeedsAndDiseases.lua` it hadn’t tracked the manifest’s
`## Version` since 0.18.1, so `/rnd status` and the settings panel’s
displayed version were both quietly stale. Now synced to match the
manifest.
### 0.18.9
– **Added the required `## AddOnVersion` tag** (previously missing entirely
this is the integer the ingame addon manager and Minion use to detect
updates, separate from the informational `## Version` tag). Started at 9.
– **Added a version floor (`>=19`) to the `LibFoodDrinkBuff` dependency**,
which previously had none.
– **Bumped the optional `LibZoneTemp` dependency floor** from `>=10` to
`>=12`, matching LibZoneTemp’s current `## AddOnVersion`.
– **Added the AI-assistance disclosure to the manifest description** (it was
already present in this README, but not in the manifest’s `## Description`,
which is what Minion shows at a glance).
– **Added a Credits section** naming RolePlayNeeds (matheusbk2) and No
Interact (Rhyono) for the implementation techniques referenced during
development.
### 0.18.8
– **Full API audit against the official ESOUI API 101050 documentation.**
Every function call, method call, and constant referenced across the addon
was cross-checked against the API doc and, where the doc didn’t cover it
(manager singletons, `ZO_` helpers, third-party libraries), against the
live ESOUI source.
– **Fixed a nonexistent constant in the combat-event filter**:
`REGISTER_FILTER_TARGET_TYPE` does not exist anywhere in the ESO API and
would have errored registering the disease-contraction combat-event
filter. Corrected to `REGISTER_FILTER_TARGET_COMBAT_UNIT_TYPE`, the real
constant (confirmed against both the API doc and live ESOUI usage).
– Everything else checked out: `EVENT_MANAGER`, `SCENE_MANAGER`,
`PLAYER_EMOTE_MANAGER`, `RETICLE`, `INTERACTIVE_WHEEL_MANAGER`, `ZO_Alert`,
`ZO_PreHook`, `ZO_CreateStringId`, and the `LibFoodDrinkBuff`/
`LibAddonMenu`/`LibSavedVars` calls are all legitimate and correctly used.
### 0.18.7
– **Added `/rnd debug emptyNeeds`** the inverse of the existing
`/rnd debug resetneeds`: drops hunger/thirst/fatigue to 0 and drunkenness
to 100 (its worst value, since high drunkenness is the bad state), for
quickly reaching a state where the recovery mechanics actually have
something to recover without waiting for real decay. Doesn’t touch
diseases, same as `resetneeds`.
– **Sitting/sleeping now prints a chat-only confirmation on entering rest
mode** (“You take a seat to rest.” / “You settle in to sleep.”), restoring
feedback that existed under the old `/rnd sit`/`/rnd sleep` commands but
had gone silent once those commands were removed in favor of automatic
detection. Added at the single shared point (`OnSitTriggered`/
`OnSleepTriggered` in `Rest.lua`) that every detection path funnels
through, so it fires the same way regardless of which path triggered it,
and only on the actual transition into that pose not on every
re-trigger while already resting, to avoid spamming chat.
### 0.18.6
– **Further expanded the sit-recognition word list** with more synonyms for
“an object you sit on”: “stool”, “pew”, “throne”, “couch”, “sofa”,
“settee”, “loveseat”, “ottoman”, “stump”, “log” (alongside the existing
“sit”, “seat”, “chair”, “bench”). Only “Sit” and “Chair” have actually
been observed in-game; the rest are speculative/defensive the same way
“Sleep” already was harmless if they never match anything real.
### 0.18.5
– **Expanded the world-object sit-recognition word list** from just “Sit” to
“Sit”, “Seat”, “Chair”, “Bench”. Also widened what gets checked against
that list: previously only the reticle’s action verb was captured; now the
interactable’s own name (e.g. “Wooden Chair”, “Tavern Bench”) is captured
alongside it and both are checked together, since “Chair”/”Bench” are
realistically going to appear in an object’s name rather than in the verb.
### 0.18.4
– **Fixed the world-object “Sit” interaction detection silently never
firing.** No crash this time the addon loaded fine, but interacting
with a real chair never engaged fatigue tracking or the
emote-interruption protection, even though the fix in 0.18.3 resolved the
load errors. Root cause: `GetGameCameraInteractableActionInfo()` returns
`(action, interactableName, …)` the reticle prompt’s *verb* (“Sit”,
“Loot”, “Talk to”) is the FIRST value, and the target’s own display name
(“Wooden Chair”, an NPC’s name, etc.) is the SECOND. The code was doing
`local _, text = GetGameCameraInteractableActionInfo()`, which discards
“action” and keeps “interactableName” copied directly from NoInteract’s
own destructuring without noticing NoInteract wants
`interactableName` for ITS purpose (matching NPC/object names against a
blacklist), the opposite of what this addon needs (matching the action
verb “Sit”). Since object display names essentially never contain the
literal word “sit”, the word-match silently never matched anything. Fixed
to read `action` (the first return value) instead.
### 0.18.3
– **Fixed a second load-time crash**: `Attempt to access a private function
‘GameCameraInteractStart’ from insecure code`. 0.18.2’s fix for the
previous crash had reassigned the bare global `GameCameraInteractStart`,
reasoning it was the function the interact keybind calls true, but it
turns out to be a genuinely protected engine function that addons cannot
even read, let alone override. Traced the actual current keybind chain in
ESOUI’s `bindings.xml`:
`if not INTERACTIVE_WHEEL_MANAGER:StartInteraction(ZO_INTERACTIVE_WHEEL_TYPE_FISHING)
then GameCameraInteractStart() end`
`INTERACTIVE_WHEEL_MANAGER` is a plain, unprotected Lua object (the
direct successor to the now-removed `FISHING_MANAGER`), and its
`:StartInteraction` method fires on every single interact keypress before
the engine ever considers falling through to the protected call. Hooked
that instead same reassign-and-preserve-original pattern NoInteract
uses, just pointed at the object that actually exists and is actually
reachable in the current client.
### 0.18.2
– **Fixed a load-time crash** (`attempt to index a nil value` in
`HookWorldInteractionDetection`) caused by 0.18.1’s world-object
interaction hook reassigning `FISHING_MANAGER.StartInteraction` ZOS has
removed the standalone `FishingManager` class from the current client, so
that global no longer exists at all. Root cause went deeper than a missing
nil-check, though: pulling the current ESOUI source (github.com/esoui/esoui)
confirmed that function was never actually the right hook for this even
when it did exist `FISHING_MANAGER:StartInteraction()` only ever
dispatched to the fishing radial-wheel UI, so it would never have fired for
a chair’s “Sit” prompt regardless. Replaced with a hook on the bare global
`GameCameraInteractStart()`, confirmed via
`esoui/ingame/globals/bindings.xml`’s `GAME_CAMERA_INTERACT` keybind
definition to be exactly what the interact key invokes for ordinary
(non-fishing-wheel) interactions. Also upgraded the native `/sit`/`/sleep`
slash-command hooking from “assumed reliable” to confirmed: the current
source shows ZOS’s own `PLAYER_EMOTE_MANAGER` registers those commands into
the same addon-visible `SLASH_COMMANDS` table this addon hooks.
### 0.18.1
– **Added world-object interaction detection for sitting.** Interacting with
a real in-world chair/bench (reticle prompt “Sit”) now starts the same
fatigue-recovery/emote-interruption-protection tracking as `/rnd sit` used
to, without needing any slash command at all. Modeled on the NoInteract
addon’s (by Rhyono) two-hook reticle-interaction pattern: `ZO_PreHook` on
`RETICLE:TryHandlingInteraction` to read the current prompt text via
`GetGameCameraInteractableActionInfo()`, and wrapping
`FISHING_MANAGER.StartInteraction` (original preserved, always called) as
the point where the interaction is actually confirmed. RND never blocks
the interaction only observes it. Matching is whole-word (not substring),
so prompts like “Deposit” or “Visit” can’t false-positive against “Sit”.
See `Rest.lua`’s `HookWorldInteractionDetection`.
– **Removed `/rnd sleep` and `/rnd sit`.** Now that native `/sit`-/`/sleep`-
family command hooking and the new world-object interaction detection
both reliably start rest tracking on their own, the explicit commands
(which existed specifically to *guarantee* tracking started, back when
the native-command hook’s reliability was still uncertain) are obsolete.
Sitting/sleeping normally by any means now triggers fatigue recovery
and emote-interruption protection automatically; `Rest.TriggerSit()` /
`Rest.TriggerSleep()` are removed along with the commands.
– **Removed the `sleepPose`/`sitPose` configurable-emote settings.** These
existed only to let `/rnd sleep`/`/rnd sit` play a player-chosen emote
for that category; with those commands gone there’s no remaining code
path that plays a “configured” sit/sleep emote whatever the player
actually does (which real emote, which chair) is simply the emote.
Removed from `Feedback.EMOTE_DISPLAY_DEFAULTS`/`EMOTE_SLASH_FALLBACKS`/
`EmoteIds`, the Settings emote dropdown list, `emoteChoiceId` defaults,
and `/rnd debug testemote`’s category list.
### 0.17.2
– **Fixed Frostfall version gate in manifest** `OptionalDependsOn` was
declaring `Frostfall>=4`, which caused ESO’s addon loader to treat Frostfall
as absent even when v3.x (including v3.4.4) was installed. RND then fell back
to LibZoneTemp’s raw ambient zone temperature for all temperature checks,
silently bypassing Frostfall’s insulation, weather/precipitation, swimming
drag, and spell-resist reagent buff. Changed to `Frostfall>=3` so the 3.x
series is correctly recognized.
– **No code changes required** `Calculator.GetCurrentTemperature()` already
called `Frostfall:GetEffectiveTemp()` (not `GetZoneAmbientTemp()`) when
Frostfall was present, which is the correct value: the player’s physical
temperature after insulation/weather/swimming drift, plus any active
spell-resist reagent offset. All hunger/thirst/fatigue decay acceleration and
frostbite/heatstroke exposure checks therefore already used effective
temperature the manifest gate was the only thing preventing them from doing
so when Frostfall 3.x is installed.
– Expanded the `GetCurrentTemperature()` comment block in Calculator.lua to
document what `GetEffectiveTemp()` includes, and added a note explaining the
manifest version gate and what happens if it fails.
– **Reduced Frostbite/Heatstroke’s initial exposure threshold from 10
minutes to 5 minutes**, per request `Disease.EXPOSURE_THRESHOLD_SECONDS`
is now `300` instead of `600`. The first contraction roll now happens
after 5 minutes of continuous qualifying exposure instead of 10; the 60s
re-roll interval after that threshold (0.17.0) is unchanged. Updated the
matching comments in Data.lua, Disease.lua, and the main file, and the
Settings panel’s description text, so nothing still says “10 minutes.”
### 0.17.0
– **Corrected misunderstanding from 0.16.0**: Frostbite/Heatstroke’s
contraction check now actually REPEATS every 60s once the initial 600s
exposure threshold is met, for as long as the player stays in the
qualifying temperature range it doesn’t just roll once and then wait
out another full 600s. 0.16.0’s “verification” confirmed the OLD
single-roll-per-threshold-crossing behavior was internally consistent,
but that behavior itself was wrong per what was actually wanted here;
this is the real fix.
– `CheckSustainedCold`/`CheckSustainedHeat` are now both thin wrappers
around a new shared `CheckSustainedExposure` helper in Disease.lua.
Exposure time keeps accumulating past the 600s threshold (it no longer
resets at that point); once past it, a second timer
(`rollTimerSeconds`, new) ticks up and fires a contraction roll every
`Disease.EXPOSURE_REROLL_INTERVAL_SECONDS` (60s, new constant).
– It stops repeating the instant EITHER stated condition is met: the
disease is contracted (`sv.diseaseState` becomes non-nil
checked every tick before rolling, so it can’t roll once more after
contracting), or the player leaves the qualifying temperature range
(both timers reset to 0 immediately, so re-entering starts the full
600s wait over from scratch, not a resumed countdown).
– If a roll succeeds, the long 600s exposure timer is ALSO reset at that
moment (on top of the disease now blocking further rolls) so if this
case gets cured later while the player is still standing in the same
cold/heat, it requires a full fresh 600s wait before anything can roll
again, rather than immediately resuming 60s re-rolls right after a cure.
– Sandbox-simulated 30 minutes of continuous exposure to confirm this
behaves as intended: first roll at 660s (600s wait + first 60s
interval), further rolls roughly every 60s after that, stopping
immediately on contraction; also confirmed leaving exposure zeroes
both timers.
– `/rnd debug frostbiteTimer`/`heatstrokeTimer` (0.16.0) updated to match:
they now report which of three states you’re in not yet at the
initial threshold, past it and actively re-rolling every 60s, or
already contracted and not currently rolling at all rather than a
single elapsed/remaining number that no longer matched the real
mechanic. `Disease.GetSustainedExposureStatus` now also takes `sv` (to
check whether the disease is already active) and reports
`pastThreshold`/`contracted`/`rerollIntervalSeconds`/`rollTimerSeconds`
alongside the original fields.
### 0.16.0
– **Verified, by direct code inspection: Frostbite/Heatstroke’s contraction
check does NOT fire repeatedly after the threshold is met.** In both
`CheckSustainedCold` and `CheckSustainedHeat`, the exposure counter is
reset to 0 in the SAME branch that calls `RollContraction`, immediately
before the roll so once the threshold crosses, the very next tick
starts accumulating from 0 again, and a full new
`EXPOSURE_THRESHOLD_SECONDS` (600s) must elapse before the next roll.
The contraction check fires exactly once per threshold-crossing, not on
every later tick spent still standing in the cold/heat past the
threshold. No code change was needed here this was a read of the
existing logic, confirming it was already correct.
– **Added `/rnd debug frostbiteTimer` and `/rnd debug heatstrokeTimer`.**
Each prints whether you’re currently in the qualifying temperature range,
the current temperature, elapsed/threshold exposure seconds, time
remaining until the next contraction roll, and the dice-roll chance that
roll will actually succeed (`sv.settings.diseaseChances.`). Backed by
a new `Disease.GetSustainedExposureStatus(kind)` accessor (`kind` is
`”cold”` or `”heat”`) that reads the exact same `exposureSeconds` counter
`CheckSustainedCold`/`CheckSustainedHeat` themselves use this is a
read-only view of the real running state, not a separate estimate that
could drift out of sync with it. If you’re not currently exposed, it
says so rather than printing a stale or misleading countdown (the real
counter resets to 0 the instant you leave the trigger range, so there’s
nothing counting down until you re-enter it).
### 0.15.0
– **Ashen Lung removed completely, per request** (0.14.0/0.14.1/0.14.2’s
disable-but-keep-the-code approach is gone this isn’t a deeper hide,
it’s an actual deletion):
– Removed from `RN.DISEASE_ORDER` and `RN.Diseases` (Data.lua) its
disease definition, cure ingredients, and flavor text no longer exist
anywhere.
– Removed `RN.ASHEN_LUNG_EXCLUDED_HOUSES` (the ~100-entry player-house
list) entirely it had no other purpose.
– Removed `Disease.CheckVolcanicHeatExposure`, its call in
`Disease.OnTick`, and its three constants
(`VOLCANIC_HEAT_THRESHOLD_CELSIUS`, `VOLCANIC_EXPOSURE_THRESHOLD_SECONDS`,
and the `volcanicHeatExposure` exposure-timer entry) from Disease.lua.
– Removed `Calculator.GetCurrentZoneName()` it existed solely to
support Ashen Lung’s house-exclusion check and had no other callers.
– Removed the whole `Disease.ASHEN_LUNG_ENABLED`/`Disease.IsDiseaseEnabled`
disable-switch mechanism added in 0.14.00.14.2 it existed
specifically to hide Ashen Lung without deleting it; now that it’s
actually deleted, the indirection has nothing left to gate. Settings.lua,
DebugHelper.lua, and the main file’s `/rnd debug` usage builder are all
reverted to their simpler pre-0.14.0 forms (just without Ashen Lung).
– `RN.DISEASE_ORDER` is now `{ frostbite, heatstroke, bloodworms, mageBane,
fightersBane, thiefsBane }` 6 entries. `/rnd debug disease <1-6>`;
index 3 (Ashen Lung’s old slot) is gone rather than left as a gap, since
there’s no longer any reason to preserve a hole for a disease that no
longer exists at all.
– Removed `textures/overlays/AshenLung.dds` from the package.
– Thief’s Bane keeps its Restore Stamina ingredient set (Blessed Thistle /
Chaurus Egg / Dragon’s Blood) it was always its own copy of the data,
just originally commented as “shared with Ashen Lung.” That comment
is now corrected; the ingredients/itemIds themselves are unchanged.
– **Found and fixed the actual cause of the sleep/sit-pose emote interrupt**
(0.13.0’s gating logic was correct but insufficient this is the real
fix). Root cause: `Rest.lua`’s movement detection used a single 5-world-
unit tolerance (`POSITION_EPSILON`) for two very different purposes (a)
detecting “stood still long enough to regen” and (b) detecting “got up
out of the sit/sleep pose.” The pose-transition animation itself (and
likely ongoing idle sway while held) plausibly shifts the tracked root
position by more than 5 units within a single 5-second tick on its own,
which was flipping `_isSeated`/`_isSleeping` back to `false` within
roughly one tick of being set re-opening `CanPlayEmotesNow`’s gate and
letting a status emote fire (and visibly cancel the pose) only seconds
into `/rnd sleep` or `/rnd sit`, even though the emote-gating logic itself
was working exactly as designed.
– Added a separate, much more lenient `REST_POSITION_EPSILON` (150 world
units) used only while `_isSeated`/`_isSleeping` is already true;
`POSITION_EPSILON` (5) still governs the idle-standing regen mechanic
unchanged.
– `OnSitTriggered`/`OnSleepTriggered` now reset the position baseline
(`_lastPosition = nil`) at the moment a pose starts, so the
settle-into-pose position shift itself is never compared against a
pre-pose baseline at all eliminating the original one-tick race on
top of widening the ongoing tolerance.
– **Honesty note**: 150 is a deliberately generous guess, NOT measured
against real in-client position deltas during a sleep/sit pose. If
interruptions still happen after this, the right next step is logging
the actual per-tick `distSq` while seated/sleeping and raising this
further to match say so and I’ll add a temporary debug print for it.
### 0.14.2
– **Scrubbed Ashen Lung from the debug surface too, per request** (0.14.1
deliberately left this alone; now fully hidden there as well):
– `/rnd debug` ‘s help text now builds its disease-index line dynamically
from `RN.DISEASE_ORDER`/`RN.Disease.IsDiseaseEnabled` instead of a
hardcoded string a disabled disease’s name never prints, and its
index number is simply skipped (the list jumps 1, 2, 4, 5…) rather
than relabeling every later index, since index positions are meant to
stay stable/permanent (see Data.lua’s own comment on `DISEASE_ORDER`).
– `/rnd debug disease 3 ` now returns the exact same generic
“Invalid disease index” message a genuinely out-of-range index would
so probing index numbers by hand can’t distinguish “doesn’t exist”
from “exists but disabled.”
– `/rnd debug ingredients` had a separate leak: Ashen Lung shares its
exact ingredient set with Thief’s Bane, so scanning Blessed Thistle/
Chaurus Egg/Dragon’s Blood was printing an “applied to Ashen Lung
tier N” line right alongside the Thief’s Bane one. The ingredient
name index it scans against is now built only from enabled diseases.
– All of this still routes through the same single
`Disease.IsDiseaseEnabled` switch added in 0.14.1 re-enabling Ashen
Lung restores it everywhere (Settings, Healer’s Guide, AND now the
debug surface) with that one flag, no separate debug-side toggle to
remember.
### 0.14.1
– **Clarified per request: Ashen Lung being “disabled” means invisible to
the player, not just inert.** 0.14.0’s greyed-out Settings slider and
unchanged Healer’s Guide entry still told the player it existed
fixed:
– New `Disease.IsDiseaseEnabled(diseaseId)` in Disease.lua, a single
centralized check (currently just `diseaseId == “ashenLung”` gated on
`Disease.ASHEN_LUNG_ENABLED`) used everywhere a disease might be shown
in the UI, so future per-disease disables only need one new line here
rather than hunting down every display call site again.
– Settings panel’s chance-slider loop now skips disabled diseases
entirely (no slider rendered at all, not greyed out) and the
disease-chances description text no longer mentions Ashen Lung by
name, since that text alone would have told the player it exists even
with no slider.
– The Healer’s Guide loop (built from `RN.DISEASE_ORDER`) now also skips
disabled diseases no header, flavor text, or cure-ingredient lines
for Ashen Lung while it’s off.
– **Still NOT hidden, by design**: `/rnd debug disease 3 ` and
the `/rnd debug` help text still reference Ashen Lung by name. That’s
a developer-only debug command, not a normal player-facing surface
if you’d also like it scrubbed from there, say so and I’ll do it, but
I didn’t want to take away your own ability to force-test a disabled
disease without being asked.
– Data.lua’s definition, the overlay window (created at load for every
disease but only ever shown if `Overlay.RefreshDisease` is called, which
natural contraction can no longer trigger), and the asset file are all
still fully intact, per “disable, don’t remove.”
### 0.14.0
– **Ashen Lung’s natural contraction trigger is now disabled, per request**
disease definition, Healer’s Guide entry, overlay (now `AshenLung.dds`,
unchanged), and `/rnd debug disease 3 ` index slot are all left
fully intact; only `Disease.CheckVolcanicHeatExposure` is switched off,
gated by a new `Disease.ASHEN_LUNG_ENABLED = false` constant in
Disease.lua. While disabled, its exposure timer stays pinned at 0 and the
disease can never roll naturally from standing in extreme heat `/rnd
debug disease 3 ` can still force it directly for testing, and
any case already active before this was disabled still progresses/cures
normally (this only blocks NEW natural contraction). Flip the constant
back to `true` to re-enable.
– Settings panel’s Ashen Lung chance slider is now greyed out (`disabled`
field see the UNVERIFIED-AGAINST-LAM-SOURCE caveat in Settings.lua;
worst case if it doesn’t visually grey out, the slider is still
functionally inert) with an explanatory tooltip, and its label/the
disease-chances description text both updated to say so, rather than
silently offering a control that does nothing.
– Also fixed two stale wording leftovers from 0.13.x while touching this
file: Mage’s Bane’s Settings label and description said “frost” instead
of the corrected “cold” (matching 0.13.2’s `DAMAGE_TYPE_COLD` fix), and
Fighter’s Bane’s label still mentioned “bleed” despite 0.13.2 dropping
that trigger entirely.
### 0.13.2 hotfix
– **Fixed a load-time crash**: `RealisticNeedsAndDiseases_Disease.lua:324:
table index is nil`. 0.13.1’s `DAMAGE_TYPE_TO_DISEASES` table used
`DAMAGE_TYPE_FROST` as a literal table key but that global doesn’t
exist in ESO’s API (the real constant is `DAMAGE_TYPE_COLD`; “Frost
Damage” is UI flavor text, the engine kept the older “Cold” name). A
nonexistent global evaluates to `nil`, and Lua refuses `nil` as a table
key in a literal constructor, which failed the whole file (and so the
whole addon) at load.
– **Rebuilt the table defensively** via a new `AddDiseaseTrigger(diseaseId,
globalNameAsString)` helper in Disease.lua: each damage-type global is
looked up by NAME through `_G` rather than written as a
literal key, so a wrong or renamed constant is just a normal `nil` value
that gets skipped (and reported once to chat, naming exactly which
globals didn’t resolve) instead of crashing the file. This closes off the
whole class of bug, not just this one instance.
– **Dropped `DAMAGE_TYPE_BLEED` from Fighter’s Bane’s trigger entirely**,
rather than swapping in another guess “Bleed” appears to be a status
*effect* applied by Physical damage in ESO’s combat model, not a
separate `DamageType` of its own, so there may be no such constant to
reference at all. Fighter’s Bane is wired to `DAMAGE_TYPE_PHYSICAL` only
for now; if a real distinct Bleed damage-type constant is ever confirmed,
add it through `AddDiseaseTrigger` the same defensive way, not as a
literal key.
– Mage’s Bane’s widened trigger is otherwise unchanged in effect Magic,
Fire, Cold (was mis-typed Frost), and Shock just spelled correctly now
and resolved defensively.
### 0.13.1
– **Status emotes no longer interrupt a sit/sleep pose already in
progress.** New `Rest.IsResting()` exposes Rest.lua’s existing
`_isSeated`/`_isSleeping` tracking (already covering `/rnd sit`, `/rnd
sleep`, and the hooked native `/sit`-/`/sleep`-family commands, including
`/sitchair` so this also covers sitting in a real chair) to
`Feedback.CanPlayEmotesNow()`. Hunger/thirst/fatigue/drunkenness/disease
status emotes now check this before firing, both on the immediate
on-band-entry fire (`CheckBandTransition`, which previously fired
unconditionally it’s now gated through the same combat/mounted/major-UI/
resting checks the periodic retrigger already used) and on every periodic
retrigger while a category stays bad. `Feedback.CanPlayEmotesNow` is now
exposed on the `Feedback` table itself so Disease.lua’s own direct
`OnDiseaseChanged` emote call can gate through it too, rather than firing
unconditionally.
– **Disease redesign, per spec:**
– **”Magic Blight” renamed to “Mage’s Bane”** (`magicBlight` → `mageBane`
diseaseId). Its overlay art asset is renamed alongside it
`Magicpox.dds` → `MagesBane.dds` per request, with the `overlayTexture`
path updated to match. Its trigger is widened from `DAMAGE_TYPE_MAGIC`
alone to any of the four standard “spell damage” types Magic, Fire,
Frost, Shock each now an independent entry in Disease.lua’s new
`DAMAGE_TYPE_TO_DISEASES` map. Cure (Restore Magicka: Corn Flower /
Lady’s Smock / Vile Coagulant) is unchanged.
– **New “Fighter’s Bane”** triggered by martial damage taken (Bleed or
Physical), cured with the SAME Restore Health ingredient set Bloodworms
already uses (Mountain Flower / Water Hyacinth / Crimson Nirnroot), by
request. Now has a generated PLACEHOLDER overlay texture
(`FightersBane.dds`, simple colored vignette, not hand-drawn) swap in
real custom art whenever you have it, no code changes needed beyond the
file itself.
– **New “Thief’s Bane”** triggered by martial damage taken (Poison or
Disease), cured with the SAME Restore Stamina ingredient set Ashen Lung
already uses (Blessed Thistle / Chaurus Egg / Dragon’s Blood the last
still unconfirmed, as on Ashen Lung’s own entry). Also given a generated
PLACEHOLDER overlay texture (`ThiefsBane.dds`), same caveat as Fighter’s
Bane’s above. Note Disease damage now feeds BOTH Bloodworms and Thief’s
Bane each rolls independently off its own `diseaseChances` entry on
the same qualifying hit.
– `Disease.lua`’s old single-disease-per-damage-type
`DAMAGE_TYPE_TO_DISEASE` map is replaced with `DAMAGE_TYPE_TO_DISEASES`
(a list per damage type), so any number of diseases can independently
roll off the same hit. `DAMAGE_TYPE_FIRE`, `DAMAGE_TYPE_FROST`,
`DAMAGE_TYPE_SHOCK`, `DAMAGE_TYPE_BLEED`, and `DAMAGE_TYPE_PHYSICAL` are
newly referenced here same real-but-individually-unconfirmed-in-a-
live-client status the project’s existing `DAMAGE_TYPE_DISEASE`/
`DAMAGE_TYPE_MAGIC` constants already carried; worth a
`/script d(damageType)` check the first time each should newly fire.
– `RN.DISEASE_ORDER` updated to `{ frostbite, heatstroke, ashenLung,
bloodworms, mageBane, fightersBane, thiefsBane }` existing entries
kept in their original index positions, new ones appended, per the
project’s own stated convention for this list. `/rnd debug disease
<1-7>` index range and help text updated to match (the old help text’s
1-5 labels were already stale Bone Chill/Swamp Rot/Ashen Cough/Blood
Fever/Wormwood Plague names from before an earlier rename corrected
to the current names while touching this anyway).
– Settings panel’s `ROLLABLE_DISEASES`/`LOW_CHANCE_DISEASES` lists and
description text updated for the rename and two additions. The
Healer’s Guide submenu needed no changes it was already built
dynamically from `RN.Diseases`/`RN.DISEASE_ORDER`.
– **Not migrated**: existing SavedVariables with the old `magicBlight`
key under `diseaseChances` are left in place as an inert leftover (same
as this project’s earlier disease renames) the new `mageBane`/
`fightersBane`/`thiefsBane` keys are filled in from defaults on next
load via the existing recursive-fill, without touching anything you’d
already customized under the old key.
– **Placeholder texture format note**: `FightersBane.dds`/`ThiefsBane.dds`
are uncompressed 32bpp BGRA DDS files (no DX10 header), unlike the
project’s existing real-art textures which are all BC7/DX10-compressed
10241024. Both are valid DDS and should load the same way in-game, but
the format difference is worth knowing if you swap in real art later
matching the existing BC7/DX10 10241024 convention isn’t required, just
consistent with what’s already there.
### 0.12.5
– **Disease notifications now include a cure hint.** New
`Feedback.GetCureHintText(diseaseId, severity)` looks up the cheapest
ingredient(s) that would actually cure at the given severity (the tier
exactly matching it; any rarer tier also works but there’s no reason to
suggest it when the matching one is enough) and the curative trait name,
e.g. “A dose of Mudcrab Chitin (Protection) would ease this.” Wired into:
contraction (“You have contracted Frostbite. A dose of…”);
worsening (“Your Heatstroke has worsened to Severe. A dose of Vile
Coagulant or Powdered Mother of Pearl (Protection) would ease this.”); and
`/checkneeds`’s disease lines. `/rnd debug checkneeds` untouched.
– **Added a 1-minut

