Skip to content

Greed System

Summary: Greed is a hidden per-item risk meter that escalates with crafting. Every craft on an equippable accrues a hidden 0–100 value; as it crosses tiers (Clean → Touched → Tainted → Corrupting) the chance of consumption rises. On consumption the item is permanently locked from further crafting and gains a rolled Blessing from a curated, loot-sealed pool. The numeric meter is never shown — only the visual tier state and escalating color communicate risk. Solo players get risk-free crafts in the Clean band, so danger only exists when the player chooses to keep pushing. Server-authoritative; rides the item state-module + modifier-pool infrastructure.

Table of Contents


Architecture Overview

┌──────────────────────────────────────────────────────────────┐
│  UGreedConfigDataAsset (DA_GreedConfig)                       │
│   • tier thresholds (31 / 61 / 86)                           │
│   • per-tier consumption risk (0 / 4 / 10 / 25 %)            │
│   • per-craft gain range (5–15)                             │
│   • BlessingPool → UModifierPoolDataAsset (sealed)          │
└─────────┬────────────────────────────────────────────────────┘
          │ loaded on all machines
┌──────────────────────────────────────────────────────────────┐
│  UGreedSubsystem (GameInstanceSubsystem)                     │
│   • loads config on Initialize                              │
│   • registers BlessingPool via                             │
│     UModifierPoolManager::RegisterAuxiliaryPool()           │
│     → blessing IDs resolve everywhere (tooltips on clients) │
└─────────┬────────────────────────────────────────────────────┘
          ▼  (server, during a craft)
┌──────────────────────────────────────────────────────────────┐
│  UCraftingContainerComponent::Server_ExecuteCraft           │
│   1. CanExecuteCraft → ItemConsumed gate (blocks if locked) │
│   2. ApplyOutcome (existing crafting)                       │
│   3. UGreedCraftingLibrary::AccrueGreedAndRollConsumption   │
│        → FGreedItemState (state module) updated            │
│        → on consume: roll Blessing, set bConsumed          │
│   4. Client_OnCraftingComplete(Outcome, Mods, GreedEvent)  │
└─────────┬────────────────────────────────────────────────────┘
          ▼  (on equip)
┌──────────────────────────────────────────────────────────────┐
│  FEquipmentFragment::ApplyGlobalModifiersToCharacter        │
│   → ApplyGreedBlessingToCharacter() applies the Blessing    │
│     GE (+ optional granted proc ability)                    │
└──────────────────────────────────────────────────────────────┘

Key Design Principles

Principle Implementation
Hidden meter, visible state GreedValue is server-internal; UI reads only VisualState (EGreedState) and bConsumed
No punishment for stopping Clean band (0–30) is hard-coded 0% risk; risk exists only by choice
Server authority Accrual, consumption roll, and blessing roll all run server-side
Reuse item state modules FGreedItemState rides UItemObject::StateModules — same pattern as Remnant state
Generic sealed-pool pattern Blessings register via RegisterAuxiliaryPool(), reusable by future sealed pools (runes, corrupted mods)
Ordered consumption moment The consumption event is delivered in the crafting-complete RPC payload, not via module replication ordering, so the result panel and the reveal land together

Core Concepts

Why Greed Exists

Greed delivers risk/reward depth and scarcity without rarity (GDD Progression, Trading, Player Experience pillars). Power doesn't come from a rarity tier — it comes from how far a player dares push an item before it locks. Consumed items with strong blessings become scarce, tradeable assets. Crucially, because the Clean band is risk-free, the system never punishes a cautious player; the tension is entirely opt-in.

Greed States

EGreedState is the only greed signal the UI reads.

State Hidden value Consumption risk Meaning
Clean 0–30 0% Safe to craft
Touched 31–60 4% Mild risk
Tainted 61–85 10% Notable risk
Corrupting 86–100 25% High risk
Consumed locked No further crafting; Blessing granted

Greed vs Other Item State

Greed is an item-state lifecycle system (peer to Remnant Sealed/Awakened), not a crafting mechanic — it integrates into crafting but owns its own config, subsystem, state module, validation surface, and equip-path application. See Item State Modules for the shared per-instance state pattern.


Risk Mechanics

Each executed craft on an equippable:

1. Lazily attach FGreedItemState on first craft (AddStateModule)
2. GreedValue += random in [GainMin, GainMax]   (config, e.g. 5–15)
3. VisualState = DeriveState(GreedValue, Config)
4. Roll consumption against current tier's risk %
       hit → ConsumeItem(): bConsumed = true, roll Blessing
5. MarkStateModulesDirty() → replicate

Risk is read from the tier the item is in after accrual. The Clean band's 0% is hard-coded in code (not a config field) so it can never be mistuned into a punishing state.


Blessings

A Blessing is a single rolled modifier from MP_GreedBlessings — a curated pool of 13 authored definitions (8 stat + 3 per-type + 2 proc), all EModifierType::Unique, EModifierScope::Global, Unique-tier value ranges. Two kinds:

  • Stat blessings — a flat/increased attribute bump applied as a GE (the 8 core stat lines plus the 3 per-damage-type "Maw" lines).
  • Proc blessings — grant a proc ability (Blessing_FinalWord, Blessing_RedLedger) alongside the GE. Both reuse existing Echo proc bases, so they added no new C++: Blessing_FinalWord is a config-only child of UPressureReleaseAbility triggered by Event.Combat.KillCredited (kill → fiery burst); Blessing_RedLedger is a child of GA_OnHit_Bleed (a Bleed-on-hit proc).

The blessing pool is registered as an auxiliary pool on UModifierPoolManager (like the Remnant Echo pool): resolvable by ID everywhere for tooltips, but excluded from ordinary loot rolls. Roll selection respects spawn weights and the item's level/tags.

Application happens on equip via FEquipmentFragment::ApplyGreedBlessingToCharacter() — it reads FGreedItemState.Blessing, applies the GE, and grants the optional ability; symmetric removal on unequip. Same handle-storage pattern as Echo Mods.


Presentation Layer (UI)

The greed signal the player actually sees is color encoding STATE, never power (GDD). The numeric meter is never surfaced — every UI surface reads only VisualState (EGreedState), bConsumed, and the rolled Blessing, never the hidden GreedValue. Clean renders nothing (its color is White and the surfaces collapse), so a cautious player sees no decoration at all; decoration only appears once the player has chosen to push past the safe band.

State → Color

A single shared table, FGreedStateColors, is the one source of truth for the tint so the tooltip and crafting window can never drift. The Consumed tint doubles as the Blessing-line color.

State Default tint (linear RGB) Surface behavior
Clean White (none) Renders nothing; state line / badge collapsed
Touched (0.85, 0.75, 0.45) muted gold State line + badge tinted
Tainted (0.9, 0.5, 0.15) amber State line + badge tinted
Corrupting (0.85, 0.2, 0.15) red State line + badge tinted
Consumed (0.65, 0.3, 0.9) violet State line/badge tint and the Blessing line color

Defaults live in code (the baked seed in FGreedStateColors) and on DA_GreedConfig (the Presentation category). Designers retune the hues in the config asset — no recompile. The baked seed exists so reads before subsystem init, in tests, and in editor previews (no play world) still get sensible colors.

Color Data Flow

┌────────────────────────────────────────────┐
│ UGreedConfigDataAsset (DA_GreedConfig)      │
│   Presentation tints: Touched / Tainted /   │
│   Corrupting / Consumed                     │
└───────────────┬─────────────────────────────┘
                │ UGreedSubsystem::LoadAndRegisterConfig()
                │   → FGreedStateColors::SetFromConfig()
┌────────────────────────────────────────────┐
│ FGreedStateColors  (live static table)      │
│   seeded with baked defaults                │
│   (pre-init / tests / editor previews work) │
└───────┬───────────────────────────┬─────────┘
        │ read by                    │ read by
        ▼                            ▼
 ItemTooltipViewModel          CraftingViewModel / UCraftingWidget
   (greed line tint +            (badge tint +
    blessing-row color)           overlay StateColor)

Surfaces

Surface What it shows How
Item tooltip (UItemTooltipWidget) A greed state line; a Blessing row for Consumed items GreedStateText tinted from FGreedStateColors (collapsed on Clean); the rolled Blessing renders in BlessingModifiersBox under a BlessingHeaderText (detailed / Alt header, mirroring the Echo header)
Crafting window (UCraftingWidget) A passive greed badge + a corruption overlay on the target item GreedStateText badge collapsed when Clean; GreedStateImage is a corruption overlay driven by a MID (Progress scalar + StateColor vector), eased between states rather than hard-cut; re-resolves on cube add / remove / swap and stays live via OnItemStateModulesChanged

The badge is passive — there is no warning or confirm prompt, because the meter is hidden by design; it is purely a risk signal. The crafting overlay's fill is discrete per stateClean 0 / Touched .25 / Tainted .5 / Corrupting .75 / Consumed 1 — carried by CraftingViewModel::TargetGreedFill and tweened toward its target by the widget.


Crafting Integration

Two touch points in UCraftingContainerComponent, both server-side:

Point Behavior
CanExecuteCraft() Adds ECraftingValidationResult::ItemConsumed — any recipe targeting a consumed equippable fails early (feeds Client_OnCraftingFailed)
After ApplyOutcome() For ModifyExisting outcomes on an item with an FEquipmentFragment, calls AccrueGreedAndRollConsumption(), captures the FGreedCraftEvent, and passes it through the extended Client_OnCraftingComplete(Outcome, ResultModifiers, GreedEvent) RPC

Accrual is per executed craft, not per operation — matching GDD semantics. Only equippables accrue (gated on FEquipmentFragment presence).


Replication & Authority

Operation Server Client
Greed accrual / consumption / blessing roll UGreedCraftingLibrary
FGreedItemState transport Mutated server-side, MarkStateModulesDirty() Reads via OnRep_StateModules
Consumption reveal ordering Delivered in Client_OnCraftingComplete payload Receives event with result panel
Blessing definition resolution Registered pool Registered on all machines (tooltips work client-side)
Persistence (trade/save) FGreedItemState serialized by reflection Round-trips like Remnant Awakened state

Public Contracts

UGreedCraftingLibrary (static)

Method Purpose
IsItemConsumed(Item) Query consumed state (any context)
DeriveState(GreedValue, Config) Hidden value → EGreedState
AccrueGreedAndRollConsumption(Item, Config) Server. One craft's accrual; returns FGreedCraftEvent
ConsumeItem(Item, Config) Server. Lock item + roll Blessing

UGreedSubsystem (GameInstanceSubsystem)

Member Purpose
GetConfig() Loaded UGreedConfigDataAsset
GetConfigFromWorld(WorldContext) Static convenience accessor

Data Types

Type Purpose
EGreedState Clean / Touched / Tainted / Corrupting / Consumed
FGreedItemState State module: GreedValue (hidden), VisualState, bConsumed, Blessing
FGreedCraftEvent RPC payload: NewState, bConsumptionTriggered, BlessingDescription
UGreedConfigDataAsset Thresholds, per-tier risk, gain range, blessing pool ref

Source References

Component Location
EGreedState, FGreedItemState, FGreedCraftEvent Public/Greed/Types/GreedTypes.h
UGreedConfigDataAsset Public/Greed/Data/GreedConfigDataAsset.h
UGreedSubsystem Public/Greed/Subsystems/GreedSubsystem.h
UGreedCraftingLibrary Public/Greed/GreedCraftingLibrary.h
Crafting integration Private/Crafting/Components/CraftingContainerComponent.cpp
Blessing apply/remove Private/Inventory/Items/Fragments/ItemFragment.cppApplyGreedBlessingToCharacter()
ItemConsumed result Public/Crafting/Types/CraftingTypes.h
Auxiliary pool API Public/Inventory/Modifiers/ModifierPoolManager.hRegisterAuxiliaryPool()
FGreedStateColors (shared tint table) Public/Greed/Types/GreedStateColors.h
Presentation tints (config) Public/Greed/Data/GreedConfigDataAsset.h
Color push to live table Private/Greed/Subsystems/GreedSubsystem.cppLoadAndRegisterConfig()
Tooltip greed line + blessing row binding Public/UI/Widgets/Tooltip/ItemTooltipWidget.h
Crafting badge / corruption overlay Public/UI/Widgets/Crafting/CraftingWidget.h + Public/UI/ViewModels/Crafting/CraftingViewModel.h
Overlay material / textures /Game/MaterialLibrary/UI/Greed/M_UI_GreedState, /Game/TextureLibrary/UI/Greed/T_UI_GreedFlow + T_UI_GreedNoise
Config asset /Game/DataAssets/Greed/DA_GreedConfig
Blessing pool /Game/DataAssets/Items/Modifiers/MP_GreedBlessings
Tests Source/ProjectEternal/Private/Tests/GreedSystemSpec.cpp


Recent Changes

Date Change Impact
2026-06-11 Greed system v1 Hidden per-item meter (FGreedItemState) accrues on craft; tier-gated consumption locks the item and rolls a Blessing from the sealed MP_GreedBlessings pool; UGreedSubsystem registers the pool as auxiliary; ItemConsumed crafting gate; blessings applied on equip
2026-06-17 Presentation layer Added the UI surface: shared FGreedStateColors table (seeded defaults, pushed from DA_GreedConfig via UGreedSubsystem::LoadAndRegisterConfig) drives the tooltip greed line + Consumed Blessing row and the crafting badge + eased corruption overlay (discrete fill per state, live via OnItemStateModulesChanged). Color encodes state not power; Clean renders nothing; meter stays hidden
2026-06-17 Blessing pool finalized Pool reaches its full 13 authored definitions (8 stat + 3 per-type + 2 proc); the proc pair (Blessing_FinalWord on Event.Combat.KillCredited, Blessing_RedLedger Bleed-on-hit) reuses Echo proc bases, no new C++