Buff System Overview
Buffs are the core of AFNM’s combat system. They represent temporary effects, enhancements, debuffs, and resource pools that drive combat mechanics. Understanding buffs is essential because techniques primarily work by creating and manipulating buffs.
Complete Buff Interface
import { Buff, BuffEffect, Scaling } from 'afnm-types';
interface Buff {
// Identity
name: string; // Unique identifier displayed to players
icon: string; // Image asset for visual representation
// Stacking behavior
canStack: boolean; // Whether multiple instances can exist
stacks: number; // Current number of stacks
maxStacks?: number; // Optional stack limit
// Visual properties
colour?: string; // Optional background color for buff icon
effectHint?: string; // Brief description when tooltip isn't sufficient
tooltip?: string; // Custom tooltip (auto-generated if omitted)
combatImage?: CombatImage; // Visual effects during combat
// Combat properties
stats?: { [key: string]: Scaling }; // Passive stat modifications
type?: TechniqueElement; // Element type for enhancement/affinity
buffType?: string; // Grouping for modifyBuffGroup effects
priority?: number; // Execution order (lower = earlier)
// Effect timing
onCombatStartEffects?: BuffEffect[]; // Once when combat begins
onRoundStartEffects?: BuffEffect[]; // Start of each round
onTechniqueEffects?: BuffEffect[]; // Before/after each technique
onRoundEffects?: BuffEffect[]; // End of each round
// Advanced mechanics
interceptBuffEffects?: InterceptEffect[]; // Intercept other buff applications
triggeredBuffEffects?: TriggeredEffect[]; // Respond to custom triggers
condition?: BuffCondition; // When buff effects are active
// Timing modifiers
afterTechnique?: boolean; // onTechniqueEffects trigger after instead of before
// System properties
cantUpgrade?: boolean; // Prevent mastery upgrades
}
Buff Lifecycle
Understanding when and how buffs execute is crucial for creating effective combat content:
1. Application Phase
When a buff is applied to a character, the system:
- Checks if the buff can stack with existing instances
- Applies any intercept effects from other buffs
- Updates the character’s buff list
2. Execution Phase
During combat, buffs execute their effects based on timing:
- Priority order: Lower
priority
values execute first - Timing triggers: Each timing type executes at its designated moment
- Condition checks: Effects only execute if conditions are met
3. Modification Phase
Buffs can be modified during combat:
- Stack counts can increase/decrease
- Effects can be intercepted or triggered
- Buffs can be consumed or negated
4. Cleanup Phase
Buffs are removed when:
- Stack count reaches zero (through
add
effects with negative values) - Explicitly consumed by techniques or other buffs
- Combat ends (most buffs don’t persist)
Effect Timing
Buffs can trigger effects at different times during combat:
onCombatStartEffects
Triggers once when combat begins. Used for setup effects.
onRoundStartEffects
Triggers at the start of each round, before any techniques are used.
onTechniqueEffects
Triggers before each technique use (default) or after if afterTechnique: true
.
onRoundEffects
Triggers at the end of each round, after all techniques have been used.
Advanced Timing
interceptBuffEffects
- Intercepts when specific buffs are appliedtriggeredBuffEffects
- Responds to custom trigger events. See Triggers for detailspriority
- Controls execution order (lower numbers execute first)
Real Examples
Resource Buffer - Sunlight
import { Buff } from 'afnm-types';
import sunIcon from '../assets/icons/sunlight.png';
export const sunlight: Buff = {
name: 'Sunlight',
icon: sunIcon,
canStack: true,
effectHint: 'Used to empower Celestial techniques',
stats: {
power: {
value: 0.05,
stat: 'power',
scaling: 'stacks',
max: { value: 1, stat: 'power' },
},
},
onTechniqueEffects: [],
onRoundEffects: [],
stacks: 1,
combatImage: {
image: sunIcon,
position: 'floating',
entrance: 'rotate',
stacksScale: 0.15,
},
cantUpgrade: true,
};
Self-Consuming Effect - Moonchill
import { Buff } from 'afnm-types';
import moonchillIcon from '../assets/icons/moonchill.png';
export const moonchill: Buff = {
name: 'Moonchill',
icon: moonchillIcon,
type: 'celestial',
canStack: true,
stats: {
power: { value: -0.3, stat: 'power' },
},
onTechniqueEffects: [
{
kind: 'add',
amount: { value: -1, stat: undefined },
},
],
onRoundEffects: [],
stacks: 1,
cantUpgrade: true,
};
Conditional Buff - Lunar Attunement
import { Buff } from 'afnm-types';
import lunarAttunementIcon from '../assets/icons/lunar-attunement.png';
export const lunarAttunement: Buff = {
name: 'Lunar Attunement',
icon: lunarAttunementIcon,
canStack: true,
maxStacks: 10,
condition: {
kind: 'condition',
condition: `${window.modAPI.utils.flag(moonlight.name)} > 0`,
tooltip: 'If you have <name>Moonlight</name> then',
},
stats: {
celestialBoost: {
value: 5,
stat: undefined,
scaling: 'stacks',
max: { value: 50, stat: undefined },
},
},
onTechniqueEffects: [],
onRoundEffects: [],
stacks: 1,
cantUpgrade: true,
};
Healing Over Time - Restoring Fragrance
import { Buff } from 'afnm-types';
import icon from '../assets/icons/restoring-fragrance.png';
const restoringFragranceBuff: Buff = {
name: 'Restoring Fragrance',
icon: icon,
canStack: true,
stats: undefined,
type: 'blossom',
afterTechnique: true,
onTechniqueEffects: [
{
kind: 'heal',
amount: { value: 0.25, stat: 'power', upgradeKey: 'power' },
},
],
onRoundEffects: [
{
kind: 'add',
amount: { value: -1, stat: undefined },
},
],
stacks: 1,
};
Stack Management
Buffs use different stacking behaviors:
Standard Stacking
canStack: true
- Multiple instances combine their stacksmaxStacks
- Optional limit to prevent infinite stacking
Non-Stacking
canStack: false
- Only one instance can exist- Applying again refreshes or replaces the existing buff
Conditions
Buffs can have conditional effects that only trigger under specific circumstances:
Buff Conditions
condition: {
kind: 'buff',
buff: targetBuff,
count: 3,
mode: 'more'
}
HP Conditions
condition: {
kind: 'hp',
percentage: 50,
mode: 'less'
}
Custom Conditions
condition: {
kind: 'condition',
condition: 'custom_flag > 0',
tooltip: 'When condition is met'
}
Chance Conditions
condition: {
kind: 'chance',
percentage: 30
}