mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
Document the CC, immunity and DR design
Records why the three identities are separate, the DBC mechanic table each [cc:type] now follows, and which DR pools the immunity safeguard may act on. Every vMaNGOS and nampower claim behind the preceding four commits is cited to the source line that establishes it, so the next person to touch this can check the premises instead of re-deriving them: the DRTYPE_ALL set is three stun pools and nothing else, Charge and Intercept are the only triggered stuns forced into the controlled pool, and SPELL_MISS puts the spell ID in arg3. Also notes the mouseover learning gap as designed behavior rather than a bug, since the failure on the other side is permanent bad SavedVariables data.
This commit is contained in:
@@ -0,0 +1,188 @@
|
|||||||
|
# CC, Learned Immunity and Diminishing Returns
|
||||||
|
|
||||||
|
How SuperCleveRoidMacros classifies crowd control, what it learns about NPC
|
||||||
|
immunity, and why diminishing returns only ever acts as a brake on that
|
||||||
|
learning. Source citations are into `C:\Git\vmangos` and `C:\Git\nampower`.
|
||||||
|
|
||||||
|
## Three identities, never conflated
|
||||||
|
|
||||||
|
```text
|
||||||
|
ccType Exact DBC mechanic. Backs [cc:*] and CC landing verification.
|
||||||
|
Conditionals.lua, CleveRoids.CCMechanics
|
||||||
|
|
||||||
|
immunityType Exact DBC mechanic, recorded against an NPC name when a CC
|
||||||
|
spell comes back IMMUNE.
|
||||||
|
Utility.lua, MECHANIC_TO_IMMUNITY_TYPE / GetSpellImmunityType
|
||||||
|
|
||||||
|
DR type The real Vanilla diminishing-return pool. Internal only.
|
||||||
|
Utility.lua, GetSpellImmunityDRType
|
||||||
|
```
|
||||||
|
|
||||||
|
The first two are the same taxonomy used for different purposes; the third is
|
||||||
|
a separate axis and is never exposed to macros. Folding any pair together is
|
||||||
|
what produced the bugs this design replaces: a target that resisted Gouge got
|
||||||
|
written down as stun-immune, and every later Kidney Shot was suppressed
|
||||||
|
against a target that had never resisted one.
|
||||||
|
|
||||||
|
## `[cc:*]` follows the DBC mechanic exactly
|
||||||
|
|
||||||
|
One conditional, one mechanic. Several names may alias a single mechanic, but
|
||||||
|
no name spans two:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[cc:stun] -> mechanic 12 only
|
||||||
|
[cc:freeze] -> mechanic 13 only
|
||||||
|
[cc:knockout] -> mechanic 14 only
|
||||||
|
[cc:sap] -> mechanic 30 only
|
||||||
|
[cc:daze] -> mechanic 27 only
|
||||||
|
```
|
||||||
|
|
||||||
|
Bare `[cc]` and `[cc:any]` still mean "any loss-of-control effect" and are
|
||||||
|
driven by `CleveRoids.CCTypesLossOfControl`, which is deliberately independent
|
||||||
|
of the per-mechanic table. An aggregate does not require redefining the parts.
|
||||||
|
|
||||||
|
Current aliases:
|
||||||
|
|
||||||
|
```text
|
||||||
|
slow -> snare
|
||||||
|
disoriented -> disorient
|
||||||
|
grip -> fumble (mechanic 6 is DBC Fumble)
|
||||||
|
incap / incapacitate / incapacitated -> knockout (immunity input only)
|
||||||
|
```
|
||||||
|
|
||||||
|
Aliases exist so published macros keep working. They resolve to the canonical
|
||||||
|
mechanic name before anything is stored, so SavedVariables only ever hold
|
||||||
|
canonical keys.
|
||||||
|
|
||||||
|
## Vanilla DBC mechanic taxonomy
|
||||||
|
|
||||||
|
The identities SCRM names. Where a mechanic is named at all, it is named as
|
||||||
|
itself:
|
||||||
|
|
||||||
|
| DBC mechanic | ID | Canonical `ccType` |
|
||||||
|
|---|---:|---|
|
||||||
|
| Charm | 1 | `charm` |
|
||||||
|
| Disoriented | 2 | `disorient` |
|
||||||
|
| Disarm | 3 | `disarm` |
|
||||||
|
| Distract | 4 | `distract` |
|
||||||
|
| Fear | 5 | `fear` |
|
||||||
|
| Fumble | 6 | `fumble` |
|
||||||
|
| Root | 7 | `root` |
|
||||||
|
| Pacify | 8 | `pacify` |
|
||||||
|
| Silence | 9 | `silence` |
|
||||||
|
| Sleep | 10 | `sleep` |
|
||||||
|
| Snare | 11 | `snare` |
|
||||||
|
| Stun | 12 | `stun` |
|
||||||
|
| Freeze | 13 | `freeze` |
|
||||||
|
| Knockout | 14 | `knockout` |
|
||||||
|
| Bleed | 15 | `bleed` |
|
||||||
|
| Polymorph | 17 | `polymorph` |
|
||||||
|
| Banish | 18 | `banish` |
|
||||||
|
| Shackle | 20 | `shackle` |
|
||||||
|
| Turn | 23 | `turn` |
|
||||||
|
| Horror | 24 | `horror` |
|
||||||
|
| Interrupt | 26 | `interrupt` |
|
||||||
|
| Daze | 27 | `daze` |
|
||||||
|
| Sapped | 30 | `sap` |
|
||||||
|
|
||||||
|
Mechanics 16, 19, 21, 22, 25, 28 and 29 (Bandage, Shield, Mount, Persuade,
|
||||||
|
Invulnerability, Discovery, Immune Shield) are real DBC values but are not
|
||||||
|
crowd control, so they get no conditional.
|
||||||
|
|
||||||
|
## Diminishing returns
|
||||||
|
|
||||||
|
DR matters here for exactly one reason: a target at DR level 4 returns
|
||||||
|
`IMMUNE`, and that must not be recorded as permanent immunity. Nothing else in
|
||||||
|
the addon consumes DR state, and there is no player-facing DR feature.
|
||||||
|
|
||||||
|
### Only three pools can diminish a creature
|
||||||
|
|
||||||
|
`GetDiminishingReturnsGroupType` in
|
||||||
|
`vmangos/src/game/Spells/SpellEntry.h:52-79` returns `DRTYPE_ALL` for exactly
|
||||||
|
three groups:
|
||||||
|
|
||||||
|
```text
|
||||||
|
DIMINISHING_CONTROL_STUN
|
||||||
|
DIMINISHING_TRIGGER_STUN
|
||||||
|
DIMINISHING_KIDNEYSHOT
|
||||||
|
```
|
||||||
|
|
||||||
|
Every other staged group — sleep, both roots, fear, Warlock fear, charm,
|
||||||
|
polymorph, silence, disarm, Death Coil, freeze, banish, knockout — is
|
||||||
|
`DRTYPE_PLAYER`, and `Unit.cpp:7881` applies those only when the victim is a
|
||||||
|
player. So on an NPC target they can never generate a DR immunity, and they
|
||||||
|
must never hold back immunity learning.
|
||||||
|
|
||||||
|
This is why `GetSpellImmunityDRType` returns nil for everything that is not
|
||||||
|
mechanic 12. It is not an approximation; it is the complete `DRTYPE_ALL` set.
|
||||||
|
|
||||||
|
### Classifying a stun into its pool
|
||||||
|
|
||||||
|
`SpellEntry::GetDiminishingReturnsGroup(bool triggered)`
|
||||||
|
(`vmangos/src/game/Spells/SpellEntry.cpp:281-432`) resolves the pool, and the
|
||||||
|
`triggered` argument means the runtime fact of whether an aura triggered the
|
||||||
|
cast — this is not a pure DBC property. SCRM reconstructs it from nampower
|
||||||
|
`SPELL_CAST_EVENT` correlation, which only fires for client-initiated casts:
|
||||||
|
a spell ID with a recent `CleveRoids.pendingCasts` entry was cast deliberately,
|
||||||
|
one without reached `SPELL_GO`/`SPELL_MISS` as a proc.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Kidney Shot -> stun_kidneyshot
|
||||||
|
Charge / Intercept stun -> stun_control
|
||||||
|
other mechanic-12, client cast -> stun_control
|
||||||
|
other mechanic-12, proc -> stun_trigger
|
||||||
|
```
|
||||||
|
|
||||||
|
Kidney Shot is matched on rogue family bit 21 (`0x00200000`,
|
||||||
|
`SpellClassMask.h:228`) rather than a bare ID list, so custom ranks that keep
|
||||||
|
their DBC family data still classify correctly. Charge (7922) and Intercept
|
||||||
|
(20253, 20614, 20615) are internally triggered but explicitly returned as
|
||||||
|
controlled stun by `SpellEntry.cpp:389-399`; they are the only such exceptions
|
||||||
|
in 1.12.
|
||||||
|
|
||||||
|
### Pools that exist but do not apply
|
||||||
|
|
||||||
|
Recorded so nobody re-adds them to the safeguard. Knockout and Sap are
|
||||||
|
distinct mechanics that share `DIMINISHING_KNOCKOUT`
|
||||||
|
(`SpellEntry.cpp:424-425`), and Horror maps to `DIMINISHING_DEATHCOIL`
|
||||||
|
(`SpellEntry.cpp:428-429`). Both are `DRTYPE_PLAYER`. `DIMINISHING_LIMITONLY`
|
||||||
|
is a PvP duration cap rather than a staged pool, and `DIMINISHING_NONE` is not
|
||||||
|
a pool at all.
|
||||||
|
|
||||||
|
### The safeguard itself
|
||||||
|
|
||||||
|
`recentCCHits[targetGUID][drType]` counts landed CC per DR pool, so the three
|
||||||
|
stun pools keep separate histories. A hit more than `DR_RESET_WINDOW` (20s)
|
||||||
|
after the previous one in that pool restarts the count at 1, matching the DR
|
||||||
|
decay the skip check uses. Without that reset the counter only ever climbed,
|
||||||
|
so three stuns on a target permanently disqualified it from ever teaching the
|
||||||
|
addon anything again.
|
||||||
|
|
||||||
|
An `IMMUNE` result is treated as DR, and discarded, only while the pool holds
|
||||||
|
three or more hits inside the window. Otherwise it is learned.
|
||||||
|
|
||||||
|
## nampower `SPELL_MISS` arguments
|
||||||
|
|
||||||
|
`TriggerSpellMissEvent` (`nampower/spellevents.cpp:888-917`) signals both
|
||||||
|
`SPELL_MISS_SELF` and `SPELL_MISS_OTHER` with the same layout:
|
||||||
|
|
||||||
|
```text
|
||||||
|
arg1 = casterGuid arg2 = targetGuid arg3 = spellId arg4 = missInfo
|
||||||
|
```
|
||||||
|
|
||||||
|
`SPELL_MISS_SELF` carries `casterGuid` too, even though it is by definition
|
||||||
|
the player — the event is chosen by comparing the caster to the active player
|
||||||
|
GUID, not by changing the payload. Reading `arg1` as the spell ID hands a GUID
|
||||||
|
string to the immunity recorder and silently loses every miss.
|
||||||
|
|
||||||
|
## Known limitation: mouseover casts
|
||||||
|
|
||||||
|
`ProcessSpellMissSelf` refuses to learn permanent immunity when it cannot
|
||||||
|
resolve a queryable unit, because the temporary-immunity-buff check needs one
|
||||||
|
— an NPC under Divine Shield must not be recorded as permanently immune. A CC
|
||||||
|
cast at a mouseover that is not the current target can therefore return
|
||||||
|
`IMMUNE` without being learned.
|
||||||
|
|
||||||
|
This is the safeguard working as designed, not a known bug. Do not loosen it
|
||||||
|
without a reproduction showing a real failure, since the failure mode on the
|
||||||
|
other side is permanent bad data in `CleveRoids_ImmunityData`.
|
||||||
Reference in New Issue
Block a user