Enemy Structure
The EnemyEntity interface defines the complete structure for all enemies in AFNM. Understanding this structure is essential for creating balanced and engaging combat encounters.
Core Properties
Basic Information
interface EnemyEntity {
name: string; // Internal identifier (used in conditions)
displayName?: Translatable; // Optional player-facing name (overrides name in UI)
image: string; // Path to enemy sprite/image
imageScale: number; // Visual scaling factor (typically 0.5-3)
imageOffset?: {
// Optional position adjustment
x: number;
y: number;
};
disableBreathing?: boolean; // Disable idle animation
}
Stance Images
You can provide alternate images that display during specific technique types. Each accepts an optional scale and imageOffset:
{
supportImage?: { image: string; scale?: number; imageOffset?: { x: number; y: number } };
defensiveImage?: { image: string; scale?: number; imageOffset?: { x: number; y: number } };
utilityImage?: { image: string; scale?: number; imageOffset?: { x: number; y: number } };
aggressiveImage?: { image: string; scale?: number; imageOffset?: { x: number; y: number } };
offensiveImage?: { image: string; scale?: number; imageOffset?: { x: number; y: number } };
hitImage?: { image: string; scale?: number; imageOffset?: { x: number; y: number } };
}
These swap in automatically when the enemy uses a technique of the matching type. Omit any you do not need.
Realm & Progression
{
realm: Realm; // Cultivation realm (qiCondensation, meridianOpening, etc.)
realmProgress: RealmProgress; // Early, Middle, or Late stage
difficulty: EnemyDifficulty; // Difficulty tier (see below)
battleLength: BattleLength; // Expected combat duration
}
Difficulty Tiers
How much damage the enemy will do over its lifetime. Damage per round is damage / battle length.
| Tier | Description | Use Case |
|---|---|---|
veryeasy | Trivial opponents | Tutorial, farming |
easy | Below player level | Warm-up encounters |
mediumEasy | Slightly below level | Standard mobs |
medium | Equal to player | Regular encounters |
medium+ | Slightly challenging | Elite variants |
mediumhard | Moderately challenging | Mini-bosses |
hard | Significant challenge | Bosses |
hard+ | Very challenging | Elite bosses |
veryhard to veryhard++++ | Extreme challenges | Legendary encounters |
Battle Length
How long it should survive.
| Length | Rounds | Description |
|---|---|---|
halfround | <1 | Instant kill enemies |
1round | 1 | Single exchange |
veryshort | 2-3 | Quick encounters |
short | 4-6 | Standard fights |
medium | 7-10 | Tactical battles |
long | 11-15 | Endurance tests |
verylong+ | 16+ | Marathon battles |
Note on stats
You cannot set stats directly. They are derived from the difficulty and battle length to give roughly that level of danger. You can then modify these after the fact with spawnCondition to add flat multipliers on top if necessary.
Combat Configuration
Stances
Stances define technique sequences that enemies cycle through:
{
stances: Stance[]; // Array of available stances
stanceRotation: StanceRule[]; // Rules for stance switching (single or random)
rotationOverrides: SingleStance[]; // Conditional overrides — only SingleStance allowed here
}
Stance Definition
interface Stance {
name: string; // Unique identifier
techniques: Technique[]; // Ordered technique sequence
}
Stance Rotation Rules
stanceRotation accepts both SingleStance and RandomStance. rotationOverrides only accepts SingleStance.
type StanceRule = SingleStance | RandomStance;
interface SingleStance {
kind: 'single';
stance: string; // Stance name to use
condition?: string; // Mathematical condition
repeatable?: boolean; // Can be triggered multiple times
alternatives?: StanceRule[]; // Fallback options
}
interface RandomStance {
kind: 'random';
stances: string[]; // Pool of stances to choose from
condition?: string;
repeatable?: boolean;
alternatives?: StanceRule[];
}
Equipment & Pills
{
clothing?: ItemDesc; // Worn clothing/armor
talismans?: ItemDesc[]; // Equipped talismans
artefacts?: ItemDesc[]; // Equipped artefacts
affinities?: Partial<Record<TechniqueElement, number>>; // Elemental affinities
pillsPerRound?: number; // Pills consumption limit per round
pills?: { // Conditional pill usage
condition: string; // When to use (e.g., "hp < 0.5 * maxhp")
pill: CombatPillItem | ConcoctionItem | CombatItem;
}[];
}
Special Properties
Spawn Conditions
{
spawnCondition?: {
hpMult: number; // HP multiplier when spawned (e.g. 0.5 to spawn at half health)
buffs: Buff[]; // Pre-applied buffs
};
spawnRoar?: SoundEffectName; // Sound effect on spawn
}
Stat Multipliers
{
statMultipliers?: {
hp?: number; // Health multiplier
power?: number; // Damage multiplier
};
}
Character Flag
{
isCharacter?: boolean; // True for NPC combatants. Rescales HP to player range and adds defense to compensate
}
Multi-Phase & Party Configuration
Phases
Enemies can move through distinct combat phases:
{
phases?: EnemyEntity[]; // Additional phases; only processed on the root enemy
}
When a phase ends, the game swaps in the next EnemyEntity from the phases array. Use rotationOverrides with HP conditions inside each phase to drive stance flow.
Party Members
Enemies can fight alongside allies:
{
party?: PartyMemberConfig[]; // Allies that fight on the enemy's side
preservePartyMembers?: boolean; // Keep party members alive through phase transitions
}
PartyMemberConfig is an alias for EnemyEntity.
Rewards Configuration
Drop System
{
drops: {
item: Item; // Item to drop
amount: number; // Quantity
chance: number; // Drop rate (0-1)
condition?: string; // Optional condition
}[];
shardMult?: number; // Pillar shard multiplier
qiMult?: number; // Qi reward multiplier
}
Special Flags
{
hideFromCompendium?: boolean; // Hide from bestiary
qiDroplets?: number; // Qi droplet rewards
}
Advanced Configuration
Preconfigured Combat Entity
For complex enemies, you can provide a complete CombatEntity configuration:
{
preconfiguredCombatEntity?: CombatEntity;
}
This allows precise control over initial combat state, including custom stat distributions, pre-applied buffs, and rendering configuration.
Enemy Variant Utilities
The ModAPI provides helpers on ModAPI.utils to generate stronger variants of any enemy:
alpha
const alphaBandit = ModAPI.utils.alpha(bandit);
- Name gains ` Alpha` suffix
imageScalex1.2statMultipliers.hpandstatMultipliers.powerboth +0.3shardMultx1.5- Non-core drop quantities doubled (Spirit Cores and trophies unchanged)
- All
phasesentries recursively alpha-ified
alphaPlus
const eliteBandit = ModAPI.utils.alphaPlus(bandit);
- Name gains ` Alpha+` suffix
imageScalex1.35statMultipliers.hpandstatMultipliers.powerboth +0.6shardMultx2- Non-core drop quantities tripled
- All
phasesentries recursively alpha-ified
realmbreaker
Returns an array of enhanced enemies for realmbreaker hunt missions:
const horde = ModAPI.utils.realmbreaker(demon); // returns EnemyEntity[]
Realmbreaker enemies have escalating power buff stacks and per-round barrier generation.
corrupted
const corruptedBeast = ModAPI.utils.corrupted(beast);
- Name set to
'Corrupted Noble' statMultipliers.hpandstatMultipliers.powerboth +0.35- All
phasesentries recursively corrupted
Example Implementation
const exampleEnemy: EnemyEntity = {
name: 'Corrupted Spirit Beast',
image: 'path/to/sprite.png',
imageScale: 1.5,
realm: 'qiCondensation',
realmProgress: 'Middle',
difficulty: 'medium',
battleLength: 'short',
stances: [
{
name: 'aggressive',
techniques: [clawStrike, bite, clawStrike, roar],
},
{
name: 'defensive',
techniques: [harden, regenerate, clawStrike],
},
],
stanceRotation: [
{ kind: 'single', stance: 'aggressive' },
{
kind: 'single',
stance: 'defensive',
condition: 'hp < 0.3 * maxhp',
},
],
rotationOverrides: [],
drops: [
{ item: spiritCore, amount: 1, chance: 0.5 },
{ item: beastFang, amount: 2, chance: 0.8 },
],
};
Condition Expressions
Conditions use mathematical expressions with available variables:
Self variables:
hp,maxhp— Current and maximum healthpower,defense— Combat statsbarrier,maxbarrier— Barrier valuestoxicity,maxtoxicity— Toxicity valuesBuffName— Total stack count of a named buff (e.g.,Ragefor Rage stacks; evaluates to0if the buff is absent)
Target variables (the opponent — player from the enemy’s perspective):
target.hp,target.maxhp— Target’s current and maximum healthtarget.power,target.defense— Target’s combat statstarget.<stat>— Any combat statistic on the target
Examples:
"hp < 0.5 * maxhp"— Below 50% health"Rage >= 3"— 3+ Rage stacks"Weakened > 0"— Has the Weakened debuff"target.hp < 0.3 * target.maxhp"— Target below 30% health"target.power > power * 1.2"— Target has significantly more power"barrier > 0 || Shielded > 0"— Has barrier or Shielded buff