StatDictionary
is a simple, type-safe stat container designed for games that rely on numerical attributes - whether for character attributes, item properties, or entire gameplay systems.
It provides a lightweight, deterministic way to manage values and modifiers in a clean, unified manner. Instead of manually tracking fields like health
, damage
, or experience
across different objects, you can store and manipulate them all through a single API that supports direct values, percentage-based modifiers, computed formulas, and value clamping - all with built-in validation and safety checks.
Some use cases:
- Character stats in an RPG (health, strength, defense, etc.)
- Item attributes (damage, accuracy, etc.)
- Procedural generation or balancing tools (scalable numeric profiles)
- Buff/Debuff systems (temporary modifiers)
- Basically any system where values need to be dynamically modified
Use it as a drop-in stat core for RPGs, shooters, roguelikes, strategy games, or a general-purpose numeric container for managing large number of values.
- Strongly typed via generics (
StatDictionary<T>
whereT : Enum
) - Clean separation between base values and modifiers, which can be applied separately at any time
- Computed stats via registered formulas (
RegisterFormula
,RecalculateFormulas
) - Per-stat clamping with configurable min-max ranges
- Ability to work with stat containers as a whole (merge, clone, scale)
- Adjustable precision (eliminates double epsilon problem)
- Simple, expressive API covering most use cases
- Fully deterministic and side-effect free
- No external dependencies
Use provided nuget package or download the source.
🔧 dotnet add package TrimKit.StatDictionary
Decide on your stats enum:
public enum Stats
{
Health,
Stamina,
Strength,
Agility,
Intelligence
// etc.
}
Create and manipulate your stats:
// create a stat dictionary with precision up to 8 digits
var stats = new StatDictionary<Stats>(precision: 8);
// set base values
stats.SetValue(Stats.Health, 100);
stats.SetValue(Stats.Strength, 20);
// add modifiers
stats.AddValue(Stats.Health, 100); // now 200
stats.AddPercent(Stats.Health, 10); // will be +10%
// apply all percentage modifiers
stats.ApplyPercentages(); // now Health = 220
// etc.
Use stored formulas to calculate derived stats:
// register a computed formula
stats.RegisterFormula(Stats.Health, ctx => ctx.Values[Stats.Strength] * 2);
stats.RegisterFormula(Stats.Stamina, ctx => ctx.Values[Stats.Agility] * 5);
// and recalculate at any time
stats.RecalculateFormulas();
Use value clamps:
// defines a clamp for Health to be between 0 and 100
stats.SetClamp(Stats.Health, 0, 100);
// apply when needed
stats.ApplyClamps();
Merge, clone, or reset:
var other = new StatDictionary<Stats>();
other.SetValue(Stats.Health, 25);
other.SetPercent(Stats.Health, 5);
// combine two dictionaries
stats.Merge(other);
// clone
var copy = stats.Clone();
// reset what isn't needed
stats.ClearValues();
stats.ClearPercentages();
stats.ClearFormulas();
stats.ClearClamps();
- This library uses
double
to store all values, so you will inevitably encounter epsilon problem (i.e. stuff like 100.000000000000003) at some point unless you enable rounding in the constructor, which is recommended. StatDictionary
is not thread-safe by design. If you need to share it between threads, wrap access in your own synchronization mechanism.- When first instantiated,
StatDictionary<T>
validates that your enum does not contain duplicate values (aliases). If duplicates exist, it throws an exception at startup to prevent logic bugs. - Formulas are recalculated using a snapshot of current values, ensuring formula order doesn't affect results.
Constructor | Description |
---|---|
StatDictionary(int precision = -1) |
Creates StatDictionary with a given precision. Use 0…15 to enable or -1 disables rounding. |
Method / Property | Description |
---|---|
void SetValue(T stat, double amount) |
Sets base value for a stat. |
void AddValue(T stat, double amount) |
Adds to base value. |
double GetValue(T stat) |
Gets current base value. |
double this[T stat] |
Indexer for direct access. |
void ScaleValues(double factor) |
Multiplies all base values by a factor. |
void ClearValues() |
Clears all base values. |
Method / Property | Description |
---|---|
void SetPercent(T stat, double amount) |
Sets a percentage modifier. |
void AddPercent(T stat, double amount) |
Adds to a percentage modifier. |
double GetPercent(T stat) |
Gets the current percentage modifier. |
void ApplyPercentages(bool discardUnused = false) |
Applies (bakes in) percentage modifiers. |
void ScalePercentages(double factor) |
Scales all percentages by a factor. |
void ClearPercentages() |
Clears all percentage modifiers. |
Method / Property | Description |
---|---|
void RegisterFormula(T stat, Func<StatContext, double> formula) |
Registers or replaces a formula used to calculate a stat dynamically. |
bool UnregisterFormula(T stat) |
Removes a formula from a stat. |
void RecalculateFormulas() |
Recalculates values using registered formulas. |
void ClearFormulas() |
Removes all formulas. |
Method / Property | Description |
---|---|
void SetClamp(T stat, double min, double max) |
Defines a min-max range for a stat. |
bool RemoveClamp(T stat) |
Removes a clamp if present. |
void ApplyClamps(bool enforceForMissingStats = false) |
Applies clamps to ensure values stay within defined ranges. |
void ClearClamps() |
Removes all clamps. |
Method / Property | Description |
---|---|
StatDictionary<T> Merge(StatDictionary<T> other, bool mergeFormulas = false, bool mergeClamps = false) |
Merges another dictionary into this one. |
StatDictionary<T> Clone() |
Creates a copy. |
bool IsLocked |
Returns true if the container is locked. |
void Lock() |
Prevents further modifications. |
- v1.0 - Initial release.
This library is part of the TrimKit collection - a set of small, focused C# libraries that make game development more enjoyable by reducing the need for boilerplate code and providing simple reusable building blocks that can be dropped into any project.
- TrimKit.EventBus - Lightweight, mutation-safe event bus (event aggregator).
- TrimKit.GameSettings - JSON-based persistent settings manager.
- TrimKit.VirtualFileSystem - Unified file hierarchy abstraction to enable modding and additional content in games.
- TrimKit.StatDictionary - Simple character stat container for RPG or other games relying on stat heavy calculations.
Each module is independent and can be used standalone or combined with others for a complete lightweight foundation.
Contributions are welcome!
You can start with submitting an issue on GitHub.
This library is released under the MIT License.