# Aeshnidae.AwakenedRewards

What awakening earns beyond the harder dungeons (Tom, 2026-09-19). Two rewards,
both read off the character's awakening count - `PropertyInt 9040`, which
`Aeshnidae.Awakens` owns and this mod only reads:

1. **Loot rolled in a scaled dungeon copy is better, and gated.** A corpse or chest
   in a personal copy of tier N (`Aeshnidae.InstancesNoDat`; the tier is the
   opener's awakenings) rolls with the profile's quality mod raised by 2% a tier
   and, every 5 tiers, one loot tier higher (to ACE's top tier, 8). Every piece that
   can be worn carries a wield requirement of **N awakenings**, so the richer loot
   can be traded and sold but not worn by a character who has not earned the tier.
   The retail landblocks and tier-0 copies roll retail.

2. **Attuned quest items scale to their first owner's awakenings, once.** An
   attuned item that becomes a character's for the first time - from an NPC, a
   chest, a corpse, the ground - grows by 2% per awakening of that character, at
   most +40% (reached at 20 awakenings): damage, elemental bonus, armour, the
   damage/elemental/weapon/mana-conversion modifiers (the part above 1.0), the
   ratings. Re-earning a quest item at a higher awakening yields a better one.
   Attuned already binds it to that character, so it needs no wield gate.

Both append **" (Awakened N)"** to the item's name - the client has no way to show a
custom property, and the name is how a player (or staff, in a dispute) tells a
tier-3 drop from a retail one.

```
/awakened-rewards   /ar     the terms in force, your count, and the stamps on the item you last appraised
/awakened-rewards-reload    admin: re-read Settings.json
/ar-tierfeed [send]         admin, console-safe: the tier feed - who is where, what each client was told, open copies' tiers
```

## The tier feed (2026-10-03)

A third, smaller job, housed here because this mod already asks InstancesNoDat for a copy's
tier and is a hot reload (a new mod would be a `mod-load`, and reloading InstancesNoDat
itself has disturbed open copies before). `TierFeed.cs` tells each player's **own client** the
difficulty tier where they stand: **PropertyInt 9051** on the player's object, a *private*
update (`GameMessagePrivateUpdatePropertyInt`, only that player receives it), **never saved**
to the character - it describes the place, not the character.

- The tier is `Instances.TierOf(player.CurrentLandblock)`, InstancesNoDat's own rule
  (`Scaling.TierFor`): a personal copy by its opener, an awakened world by its number, 0 for
  real Dereth and every unscaled copy. Every monster in one copy or world shares it.
- Sent on login (always), on every finished teleport, on arrival in a copy (the
  `Arrival.OnArrival` hook, which names the copy's tier itself), and from the heartbeat when
  the answer differs from the last one sent - walking across a landblock edge. Only changes
  are re-sent, so a crossing costs one 13-byte message.
- Why: awakened worlds and scaled copies hold the same monsters, names and weenie ids as real
  Dereth, scaled 5% a tier (health, skills, damage; resistances are not). RynthAi learns
  monsters by weenie id, so a world-10 fight spoiled its real-Dereth numbers. With the tier it
  keeps each tier's numbers apart. Nothing else reads 9051; the retail client stores it and
  shows nothing.
- `SendTierToClient` (true) switches it off; `LogTierFeed` (false) logs every send.

## The loot, exactly

ACE rolls random loot in `LootGenerationFactory.CreateRandomLootObjects(TreasureDeath)`,
called by `Creature.GenerateTreasure` for corpses, `GeneratorProfile.TreasureGenerator`
for chests, and `Creature.GenerateInventoryTreasure` for spawn inventory. The prefix
hands it a **clone** of the cached profile with `LootQualityMod = min(1, mod + N x 2%)`
and `Tier = min(8, tier + N / 5)`; the cached profile, shared by every creature of
that treasure type on the server, is never touched. The postfix stamps every
non-stackable item with `PropertyInt 9043` = N, scales its numbers by N x
`PercentPerAwakening` capped at `MaxPercent` - the same table and curve as an attuned
item (`ScaleCopyLootStats`; Tom, 2026-09-19: the weapon's damage and modifiers must
be better, not only better-rolled) - and stamps the name.

**Which copy:** the dying creature's own `CurrentLandblock`, recorded by a bracket
on `Creature.GenerateTreasure`; failing that, the copy InstancesNoDat is ticking on
this thread (`Context.Ambient`). The tier is InstancesNoDat's own answer -
`Scaling.Counted` for a *personal* copy with scaling on, else 0 - reached by
reflection through `ModManager` (`Instances.cs`), so this mod loads and runs with
InstancesNoDat absent (everything is then retail) and re-binds after a `/mod find`.

**The gate** is a postfix on `Player.CheckWieldRequirements` (`WieldGate` in
Loot.cs): an item stamped 9043 = N refuses to go on a character with fewer than N
awakenings, as `SkillTooLow`, with a chat line saying why. The first version wrote
a real `WieldRequirement.IntStat` against 9040 on the item instead - ACE's own check
handles it exactly - but the retail client names the stat in the appraisal panel and
has no name for 9040: "Wield requires unknown quality 10". The stamp in the name is
the visible rule; the postfix is the enforced one.

Stacks (pyreals, arrows, components) are never stamped: a stack merges by weenie id
and a renamed half-stack would fight the merge.

## The quest items, exactly

"First ownership" has no single seam in ACE - `Container.TryAddToInventory` sees a
pack-to-pack move exactly as it sees a pickup, and by then the item's old owner is
wiped - so three callers are hooked: `Player.TryCreateInInventoryWithNetworking`
(NPC gives, quest rewards, crafting, vendors; a prefix, so the numbers ride in the
create message), `Player.DoHandleActionPutItemInContainer` and
`Player.DoHandleActionGetAndWieldItem` (pickups into the pack or straight to the
hand, from the ground, a corpse or a chest; postfixes, taken only when the item's
root owner was not the player and the destination's is). The client already knows
an item it could see, so those two send `GameMessageUpdateObject` afterwards, as
Tailoring does after renaming a held piece, and save the biota at once.

Not on login, not on a pack shuffle, not on equip from the pack. `PropertyInt 9042`
= the count applied is stamped even when it is 0, so an item first owned by an
unawakened character is never scaled by a later one, and nothing is ever scaled
twice. Stacks are skipped. Spells, wield, level and skill requirements are not
touched; nothing on a non-attuned item is.

## Settings

`ScaleCopyLoot`, `LootQualityPercentPerTier` (2.0), `LootTierStepEveryTiers` (5; 0
disables the step), `MinimumAwakeningsToWieldLoot`, `ScaleAttunedItems`,
`PercentPerAwakening` (2.0), `MaxPercent` (40.0 - loot has a ceiling built into its
tiers; quest items need one written down), `StampNames`.

| File | |
| --- | --- |
| `Loot.cs` | The profile clone, the stamp, the gate and the name on copy loot. |
| `Attuned.cs` | The three first-ownership hooks and the scaling. |
| `Instances.cs` | The reflective binding to InstancesNoDat: which copy, what tier. |
| `Stamps.cs` | The three property ids and the name stamp. |
| `Commands.cs` | `/awakened-rewards`, `/ar`, `/awakened-rewards-reload`. |
| `Settings.cs` | The terms. |

## Loot table additions (housed here to avoid a restart)

**Stamina kits.** A share (`StaminaKitShare`, 0.33) of ACE's healing-kit rolls hand
back a stamina kit of the tier instead: Lesser (44711) up to tier 3, Greater (44714)
from `GreaterStaminaKitFromTier` (4), and below that tier the Greater anyway at
`GreaterStaminaKitChanceBelow` (0.15). Same slot, same overall kit frequency.
