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.

TierDescriptionUse Case
veryeasyTrivial opponentsTutorial, farming
easyBelow player levelWarm-up encounters
mediumEasySlightly below levelStandard mobs
mediumEqual to playerRegular encounters
medium+Slightly challengingElite variants
mediumhardModerately challengingMini-bosses
hardSignificant challengeBosses
hard+Very challengingElite bosses
veryhard to veryhard++++Extreme challengesLegendary encounters

Battle Length

How long it should survive.

LengthRoundsDescription
halfround<1Instant kill enemies
1round1Single exchange
veryshort2-3Quick encounters
short4-6Standard fights
medium7-10Tactical battles
long11-15Endurance 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
  • imageScale x1.2
  • statMultipliers.hp and statMultipliers.power both +0.3
  • shardMult x1.5
  • Non-core drop quantities doubled (Spirit Cores and trophies unchanged)
  • All phases entries recursively alpha-ified

alphaPlus

const eliteBandit = ModAPI.utils.alphaPlus(bandit);
  • Name gains ` Alpha+` suffix
  • imageScale x1.35
  • statMultipliers.hp and statMultipliers.power both +0.6
  • shardMult x2
  • Non-core drop quantities tripled
  • All phases entries 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.hp and statMultipliers.power both +0.35
  • All phases entries 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 health
  • power, defense — Combat stats
  • barrier, maxbarrier — Barrier values
  • toxicity, maxtoxicity — Toxicity values
  • BuffName — Total stack count of a named buff (e.g., Rage for Rage stacks; evaluates to 0 if the buff is absent)

Target variables (the opponent — player from the enemy’s perspective):

  • target.hp, target.maxhp — Target’s current and maximum health
  • target.power, target.defense — Target’s combat stats
  • target.<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