The buffs and debuffs system provides temporary effects that modify player and NPC stats, combat abilities, and other attributes for a limited duration.
- Overview
- Player Commands
- Effect Types
- Effect Properties
- Effect Categories
- Expiration Notifications
- Sources of Effects
- Builder Guide: Creating Effects
- Developer Reference
The effect system allows for:
- Temporary Stat Modifications: Boost or reduce strength, dexterity, intelligence, etc.
- Combat Stat Modifiers: Affect accuracy, evasion, critical chance, attack speed, armor
- Damage Over Time (DoT): Poison, burn, bleed effects that deal periodic damage
- Healing Over Time (HoT): Regeneration effects that restore HP periodically
- Special Effects: Stuns, invulnerability, damage shields, thorns
- Stacking: Some effects can stack multiple times for increased potency
- Source Tracking: Effects remember who/what applied them
Use the buffs command to see all active effects on your character:
> buffs - Show all active buffs and debuffs
> effects - Alias for buffs
> debuffs - Alias for buffs
Example Output:
=== Active Effects ===
Buffs:
Bless (+12 toHit) - 4:32 remaining
Regeneration (+5 HP/tick) - 1:15 remaining
Debuffs:
Poison (4 poison dmg/tick) - 0:08 remaining
3 active effects (2 buffs, 1 debuff)
Use 'buffs detail' for more information.
Use buffs detail for extended information including sources and stack counts:
> buffs detail
> buffs d
Example Output:
=== Active Effects ===
Buffs:
Bless
Effect: +12 toHit
Time remaining: 4:32
Source: Aria the Cleric
Debuffs:
Poison
Effect: 4 poison dmg/tick
Time remaining: 0:08
Stacks: 2/5
Source: Giant Spider
2 active effects (1 buff, 1 debuff)
The system supports the following effect types:
| Type | Description | Example |
|---|---|---|
stat_modifier |
Modifies a core stat (STR, DEX, INT, etc.) | +5 Strength |
combat_modifier |
Modifies a combat stat | +10 toHit |
damage_over_time |
Deals periodic damage | 5 poison dmg/tick |
heal_over_time |
Restores HP periodically | +10 HP/tick |
haste |
Increases attack speed | +30% speed |
slow |
Decreases attack speed | -20% speed |
stun |
Prevents attacking | Cannot attack |
invulnerable |
Immune to all damage | Immune to damage |
damage_shield |
Absorbs incoming damage | 50 absorption |
thorns |
Reflects damage to attackers | Reflect 5 damage |
Effects can modify these core stats:
strength- Physical power, melee damagedexterity- Agility, accuracy, evasionconstitution- Health, staminaintelligence- Magic power, mana poolwisdom- Magic resistance, perceptioncharisma- Social interactions, pricesluck- Critical chance, loot quality
Effects can modify these combat stats:
toHit- Accuracy bonus (added to hit rolls)toCritical- Critical hit chance (percentage, 0-100)toBlock- Block chance (percentage, requires shield)toDodge- Dodge/evasion chance (percentage)attackSpeed- Attack speed modifier (1.0 = normal)damageBonus- Flat damage bonus to all attacksarmorBonus- Armor class bonus (damage reduction)
Every effect has the following properties:
| Property | Type | Description |
|---|---|---|
id |
string | Unique identifier for the effect |
name |
string | Display name shown to players |
type |
EffectType | The effect type (see above) |
duration |
number | Remaining duration in milliseconds |
magnitude |
number | Effect strength/power |
category |
EffectCategory | 'buff', 'debuff', or 'neutral' |
description |
string | Short description for display |
| Property | Type | Description |
|---|---|---|
source |
Living | Who/what applied the effect |
stat |
StatName | For stat_modifier: which stat to modify |
combatStat |
CombatStatName | For combat_modifier: which combat stat |
tickInterval |
number | For DoT/HoT: ms between ticks |
nextTick |
number | For DoT/HoT: ms until next tick |
damageType |
DamageType | For DoT: type of damage dealt |
stacks |
number | Current stack count |
maxStacks |
number | Maximum allowed stacks |
hidden |
boolean | If true, not shown in buffs command |
Effects are categorized for display purposes:
| Category | Color | Description |
|---|---|---|
buff |
Green | Beneficial effects (stat boosts, heals, speed) |
debuff |
Red | Harmful effects (stat reductions, DoTs, slows) |
neutral |
Yellow | Neither beneficial nor harmful |
Categories are determined automatically based on effect type and magnitude, but can be explicitly set.
When effects expire, players receive color-coded notifications:
-
Buffs expiring (yellow): Warning that a beneficial effect ended
{yellow}Bless has worn off.{/} -
Debuffs expiring (green): Good news that a harmful effect ended
{green}Poison has worn off.{/}
Hidden effects (hidden: true) do not generate expiration notifications.
Effects can come from many sources:
> bless - Cleric buff spell (+toHit)
> haste - Speed increase
> poison - Thief DoT ability
- Monster special attacks (poison bites, fire breath)
- Critical hit effects
- Weapon enchantments
- Potions (temporary stat boosts)
- Enchanted equipment (passive effects)
- Scrolls and wands
- Area effects (magical zones)
- Traps (poison darts, fire traps)
- Blessings from shrines
The Effects factory provides convenient methods for creating common effects:
import { Effects, EffectDurations } from '../std/combat/effects.js';
// Damage over time effects
const poison = Effects.poison(30000, 5); // 30s, 5 dmg/tick
const burn = Effects.burn(15000, 8); // 15s, 8 fire dmg/tick
const bleed = Effects.bleed(20000, 4); // 20s, 4 bleed dmg/tick
// Healing over time
const regen = Effects.regeneration(60000, 10); // 60s, +10 HP/tick
// Stat modifiers
const strBuff = Effects.strengthBuff(30000, 5); // 30s, +5 STR
const dexBuff = Effects.dexterityBuff(30000, 5); // 30s, +5 DEX
const intBuff = Effects.intelligenceBuff(30000, 5); // 30s, +5 INT
const conBuff = Effects.constitutionBuff(30000, 5); // 30s, +5 CON
// Combat modifiers
const accuracy = Effects.accuracy(30000, 10); // 30s, +10 toHit
const evasion = Effects.evasion(30000, 10); // 30s, +10 toDodge
const critBuff = Effects.criticalChance(30000, 5); // 30s, +5% crit
const armorBuff = Effects.armorBonus(30000, 10); // 30s, +10 armor
const dmgBuff = Effects.damageBonus(30000, 5); // 30s, +5 damage
// Speed modifiers
const haste = Effects.haste(20000, 0.3); // 20s, +30% speed
const slow = Effects.slow(10000, 0.2); // 10s, -20% speed
// Special effects
const stun = Effects.stun(3000); // 3s stun
const invuln = Effects.invulnerable(5000); // 5s invulnerability
const thorns = Effects.thorns(30000, 5); // 30s, reflect 5 dmg
const shield = Effects.damageShield(100); // 100 damage absorption
// Debuffs
const weakness = Effects.weakness(15000, 3); // 15s, -3 STRFor more control, use the generic methods:
// Generic stat buff/debuff
const customStatBuff = Effects.statBuff('wisdom', 8, 60000, 'Divine Wisdom');
const customStatDebuff = Effects.statDebuff('strength', 5, 30000, 'Curse of Weakness');
// Generic combat stat buff/debuff
const customCombatBuff = Effects.combatBuff('toCritical', 10, 30000, 'Eagle Eye');
const customCombatDebuff = Effects.combatDebuff('toDodge', 15, 20000, 'Entangle');
// Fully custom effect
const custom = Effects.custom({
name: 'Arcane Shield',
type: 'damage_shield',
duration: 60000,
magnitude: 50,
category: 'buff',
description: '50 damage absorption',
});Use the EffectDurations constants for consistency:
import { EffectDurations } from '../std/combat/effects.js';
EffectDurations.veryShort // 5 seconds (5000ms)
EffectDurations.short // 10 seconds (10000ms)
EffectDurations.medium // 30 seconds (30000ms)
EffectDurations.long // 1 minute (60000ms)
EffectDurations.veryLong // 2 minutes (120000ms)
EffectDurations.extended // 5 minutes (300000ms)
EffectDurations.permanent // 10 minutes (600000ms)// Apply an effect
target.addEffect(Effects.poison(30000, 5));
// Check for specific effect
if (target.hasEffect('poison')) {
// ...
}
// Remove an effect by ID
target.removeEffect('poison_12345');
// Get all effects
const effects = target.getEffects();
// Check for effect type
const isStunned = target.hasEffectType('stun');
const isPoisoned = target.hasEffectType('damage_over_time');When defining guild skills that apply buffs:
// In your guild skill definitions
{
id: 'cleric:bless',
name: 'Bless',
description: 'Bless a target, increasing their accuracy.',
type: 'buff', // Routes to executeBuffSkill()
target: 'single',
guild: 'cleric',
guildLevelRequired: 3,
manaCost: 15,
cooldown: 30000,
maxLevel: 50,
learnCost: 200,
advanceCostPerLevel: 40,
effect: {
baseMagnitude: 10, // Base bonus
magnitudePerLevel: 0.5, // Scales with skill level
combatStatModifier: 'toHit', // Which stat to modify
duration: 300000, // 5 minutes
},
useVerb: 'bless',
useMessage: 'You bless $T with divine favor!',
}The system automatically:
- Creates the effect with proper category ('buff' or 'debuff')
- Generates a description based on magnitude and stat
- Tracks the source (caster)
- Handles expiration notifications
{
id: 'thief:poison_blade',
name: 'Poison Blade',
description: 'Coat your blade with poison, dealing damage over time.',
type: 'debuff', // Routes to executeDebuffSkill()
target: 'single',
guild: 'thief',
guildLevelRequired: 5,
manaCost: 20,
cooldown: 15000,
effect: {
baseMagnitude: 3,
magnitudePerLevel: 0.2,
tickInterval: 2000, // Damage every 2 seconds
damageType: 'poison',
duration: 20000,
},
useVerb: 'poison',
}interface Effect {
/** Unique effect ID */
id: string;
/** Display name */
name: string;
/** Effect type */
type: EffectType;
/** Remaining duration in milliseconds */
duration: number;
/** Effect strength/magnitude */
magnitude: number;
/** Tick interval for DoT/HoT effects */
tickInterval?: number;
/** Time until next tick */
nextTick?: number;
/** Who applied this effect */
source?: Living;
/** Current stack count */
stacks?: number;
/** Maximum stacks */
maxStacks?: number;
/** Stat modification (for stat_modifier type) */
stat?: StatName;
/** Combat stat modification (for combat_modifier type) */
combatStat?: CombatStatName;
/** Damage type for DoT effects */
damageType?: DamageType;
/** Effect category for display (buff/debuff/neutral) */
category?: EffectCategory;
/** Short description for display */
description?: string;
/** If true, effect is hidden from the buffs command */
hidden?: boolean;
/** Custom tick callback */
onTick?: (target: Living, effect: Effect) => void;
/** Called when effect expires */
onExpire?: (target: Living, effect: Effect) => void;
/** Called when effect is removed early */
onRemove?: (target: Living, effect: Effect) => void;
}class Living {
/** Add an effect to this entity */
addEffect(effect: Effect): void;
/** Remove an effect by ID */
removeEffect(effectId: string): boolean;
/** Check if entity has a specific effect */
hasEffect(effectId: string): boolean;
/** Check if entity has any effect of a given type */
hasEffectType(type: EffectType): boolean;
/** Get all active effects */
getEffects(): Effect[];
/** Get a specific effect by ID */
getEffect(effectId: string): Effect | undefined;
/** Process effect ticks (called by heartbeat) */
tickEffects(deltaMs: number): void;
}Effects are processed every heartbeat (2 seconds):
- Duration Countdown: All effect durations are reduced
- Tick Processing: DoT/HoT effects deal damage or heal when
nextTickreaches 0 - Stack Handling: Reapplying stackable effects increases stack count
- Expiration: Effects with duration <= 0 are removed and trigger notifications
- Stat Recalculation: Modified stats are recalculated after effect changes
| File | Purpose |
|---|---|
mudlib/std/combat/types.ts |
Effect interface and type definitions |
mudlib/std/combat/effects.ts |
Effect factory functions |
mudlib/std/living.ts |
Effect management on Living entities |
mudlib/cmds/player/_buffs.ts |
Player buffs command |
mudlib/daemons/guild.ts |
Buff/debuff skill execution |
// In an NPC definition
specialAttack(target: Living): void {
if (Math.random() < 0.3) { // 30% chance
const poison = Effects.poison(20000, 4);
poison.source = this;
target.addEffect(poison);
target.receive('{green}The spider bites you, injecting venom!{/}\n');
}
}// In a potion item
use(player: Living): void {
// Instant heal
player.heal(30);
// Plus regeneration buff
player.addEffect(Effects.regeneration(30000, 5));
player.receive('{cyan}You drink the potion and feel revitalized!{/}\n');
this.destroy(); // Consume the potion
}// In a room with magical properties
onEnter(who: Living): void {
// Apply blessing when entering sacred ground
const blessing = Effects.custom({
name: 'Sacred Ground',
type: 'heal_over_time',
duration: 60000,
magnitude: 2,
tickInterval: 5000,
category: 'buff',
description: '+2 HP every 5s',
hidden: false,
});
who.addEffect(blessing);
who.receive('{yellow}You feel the blessing of this sacred place.{/}\n');
}// Poison that stacks up to 5 times
const poison = Effects.poison(15000, 3, 2000, 5);
// First application: 3 damage per tick
target.addEffect(poison);
// Second application: 6 damage per tick (2 stacks)
target.addEffect(Effects.poison(15000, 3, 2000, 5));
// Duration refreshes, stacks increase, damage increases