Mod Development
What You’ll Learn
In this guide, you’ll learn to use the ModAPI (Mod Application Programming Interface) - think of it as your toolkit for adding content to the game. You’ll create your first items, understand how the game stores data, and see how everything connects together.
By the end of this guide, you’ll have:
- Created your first custom item
- Understood how the game organizes content
- Learned the basic patterns for adding content
How Modding Works in AFNM
The Big Picture
When you create a mod, you’re essentially writing instructions that tell the game:
- “Here’s new content I want to add” (items, NPCs, locations, etc.)
- “Here’s where players can find it” (shops, quests, drops, etc.)
- “Here’s how it should behave” (effects, interactions, etc.)
The game reads your instructions when it starts up and integrates your content seamlessly.
Your Mod’s Entry Point
All mods start from one file: src/modContent/index.ts
This is like the front door to your mod - the game looks here first to find all your content. You can organize your mod across multiple files, but they must all be imported into this main file.
Understanding the ModAPI
The ModAPI is your connection to the game’s systems. It’s available globally as window.modAPI
and has three main sections:
1. gameData
- What’s Already in the Game
window.modAPI.gameData;
This is like a giant catalog of everything that already exists in the game:
items
- All existing items (weapons, consumables, artifacts, etc.)characters
- NPCs you can interact withlocations
- All areas in the game worldtechniques
- Combat and cultivation abilitiesquests
- Available questlines- And much more…
Think of this as read-only information. You can look at what exists, but you shouldn’t modify it directly.
Example - Looking at existing content:
// See all existing items
console.log(window.modAPI.gameData.items);
// Check if a specific item exists
const healingPill = window.modAPI.gameData.items['Healing Pill'];
console.log(healingPill.description); // Shows the item's description
2. actions
- Adding Your Content to the Game
window.modAPI.actions;
These are the functions that actually add your content to the game. Think of them as “registration functions” - they tell the game about your new creations.
Core Content Functions:
addItem(item)
- Register a new itemaddCharacter(character)
- Add an NPCaddLocation(location)
- Create new areasaddTechnique(technique)
- Add cultivation abilitiesaddQuest(quest)
- Create questlines
Integration Functions:
addItemToShop(item, quantity, location, ...)
- Make items purchasableaddItemToAuction(item, chance, condition, ...)
- Add to auction houselinkLocations(existing, newLocation)
- Connect areas togetheraddCalendarEvent(event)
- Add seasonal/timed events
Golden Rule: Always add
your content before using it elsewhere.
For example, to create a purchasable item:
- First:
addItem(myItem)
- Register the item - Then:
addItemToShop(myItem, ...)
- Make it buyable
3. utils
- Helper Functions
window.modAPI.utils;
These are convenience functions that handle common tasks and help with game balance:
Enemy Scaling:
alpha(enemy)
- Create stronger variant (+50% stats)realmbreaker(enemy)
- Create cross-realm variantscorrupted(enemy)
- Add corruption effects
Quest Templates:
createCullingMission(...)
- “Kill X enemies” questcreateDeliveryMission(...)
- “Fetch/deliver item” questcreateHuntQuest(...)
- “Hunt specific enemy” quest
Balance Helpers:
getExpectedPower(realm, progress)
- Recommended combat statsgetExpectedHealth(realm, progress)
- HP scaling by levelgetNumericReward(base, realm, progress)
- Scale rewards properly
Why use these? They ensure your content fits well with the game’s existing balance and difficulty curve.
Your First Item - Step by Step
Let’s create your first mod content: a simple combat pill that restores health and provides a temporary power boost, similar to the healing pills already in the game.
Step 1: Prepare Your Assets
First, you’ll need an image for your item.
Add an image:
- Find or create an image (256x256 pixels recommended, but anything square works) for your combat pill
- Save it as
src/assets/vigor-pill.png
- If you don’t have an image: You can temporarily use any small
.png
file just to test
Step 2: Create the Item Code
Open src/modContent/index.ts
and replace the contents with:
import { CombatPillItem } from 'afnm-types';
// Import your item's image
import pillIcon from '../assets/vigor-pill.png';
// Define your item's properties
const vigorPill: CombatPillItem = {
name: 'Vigor Pill',
description:
'A carefully refined pill that restores health and grants temporary strength. Popular among cultivators entering combat.',
icon: pillIcon,
rarity: 'mundane', // Valid rarities: 'mundane', 'qitouched', 'empowered', 'resplendent', 'incandescent', 'transcendent'
kind: 'pill',
pillKind: 'combat', // Pills need this additional classification
toxicity: 10, // Pills have toxicity (how much they poison you)
realm: 'bodyForging', // What realm this pill is designed for
stacks: 1,
effects: [
{
kind: 'heal', // Direct healing effect
amount: {
value: 50, // Restores 50 health when consumed
stat: undefined,
},
},
],
};
// Register the item with the game
window.modAPI.actions.addItem(vigorPill);
// Make it available for purchase
window.modAPI.actions.addItemToShop(
vigorPill,
12, // 12 items in stock
'Liang Tiao Village', // Location name (this location exists in the base game)
'bodyForging', // Minimum cultivation realm required
);
Understanding the Code
Let’s break down what each part does:
Import Statement:
import pillIcon from '../assets/vigor-pill.png';
This tells the build system to include your image file and gives you a reference to use later.
Pill Item Definition:
const vigorPill: CombatPillItem = {
//...
};
Property Details:
name
: Display name in-gamedescription
: Flavour text shown to playersicon
: Reference to your imagerarity
: Affects text color and power (‘mundane’, ‘qitouched’, ‘empowered’, ‘resplendent’, ‘incandescent’, ‘transcendent’)kind
: Type of item (‘pill’, ‘weapon’, ‘armour’, ‘artefact’, etc.)pillKind
: For pills only - ‘combat’, ‘crafting’, etc.toxicity
: How much toxicity consuming this pill costsrealm
: What cultivation realm the pill is designed foreffects
: What happens when used (see Techniques)
Registration:
window.modAPI.actions.addItem(vigorPill);
This tells the game “this item exists now.”
Shop Integration:
window.modAPI.actions.addItemToShop(...)
This makes your item purchasable in the specified location.
Step 3: Test Your First Item
Let’s test this basic version before adding more complexity:
Build your mod:
npm run build
Expected result: You should see a new ZIP file in your builds/
folder.
If you get errors: Check that:
- Your image file exists at
src/assets/vigor-pill.png
- There are no typos in your code
- VS Code isn’t showing any red error underlines
Working with Assets (Images)
File Organization
src/assets/
├── items/
│ ├── vigor-pill.png
│ ├── strength-pill.png
│ └── healing-pill.png
├── characters/
│ └── pill-master-chen.png
└── locations/
└── ancient-alchemy-hall.jpg
Import Patterns
// Single import
import pillIcon from '../assets/items/vigor-pill.png';
// Multiple imports
import strengthPillIcon from '../assets/items/strength-pill.png';
import pillMasterPortrait from '../assets/characters/pill-master-chen.png';
import alchemyHallBackground from '../assets/locations/ancient-alchemy-hall.jpg';
Image Guidelines
- Size: 256x256 pixels for items, 1536x1536 for characters
- Format: PNG with transparency preferred
- Style: Should match the game’s art style if possible
- Naming: Use kebab-case (my-item.png) for consistency
Organizing Your Mod
As your mod grows beyond a single item, you’ll want to organize your code into separate files.
Recommended Structure
// src/modContent/index.ts (main entry point)
import { initializeItems } from './items';
import { initializeCharacters } from './characters';
import { initializeLocations } from './locations';
// Initialize all content when mod loads
initializeItems();
initializeCharacters();
initializeLocations();
// src/modContent/items.ts
import { CombatPillItem } from 'afnm-types';
import pillIcon from '../assets/items/vigor-pill.png';
import strengthPillIcon from '../assets/items/strength-pill.png';
export function initializeItems() {
// Create your items
const vigorPill: CombatPillItem = {
name: 'Vigor Pill',
kind: 'pill',
pillKind: 'combat',
toxicity: 10,
realm: 'bodyForging',
rarity: 'mundane',
stacks: 1,
icon: pillIcon,
description: 'A healing pill.',
effects: [
{
kind: 'heal',
amount: {
value: 50,
stat: undefined,
},
},
],
};
const strengthPill: CombatPillItem = {
name: 'Strength Pill',
kind: 'pill',
pillKind: 'combat',
toxicity: 15,
realm: 'coreFormation',
rarity: 'qitouched',
stacks: 1,
icon: strengthPillIcon,
description: 'A pill that boosts power temporarily.',
effects: [
{
kind: 'buffSelf',
buff: {
name: 'Strength Boost',
icon: strengthPillIcon,
canStack: false,
buffType: 'Pill',
stats: {
power: {
value: 100,
stat: undefined,
},
},
onTechniqueEffects: [],
onRoundEffects: [
{
kind: 'add',
amount: {
value: -1,
stat: undefined,
},
},
],
stacks: 1,
},
amount: {
value: 10,
stat: undefined,
},
},
],
};
// Register them all
window.modAPI.actions.addItem(vigorPill);
window.modAPI.actions.addItem(strengthPill);
// Add to shops
window.modAPI.actions.addItemToShop(
vigorPill,
15,
'Liang Tiao Village',
'bodyForging',
);
window.modAPI.actions.addItemToShop(
strengthPill,
8,
'Liang Tiao Village',
'coreFormation',
);
}
Why Organize This Way?
Benefits:
- Easier to find and edit specific content
- Multiple people can work on different files
- Reduces merge conflicts when using Git
- Makes debugging easier when things go wrong
When to split:
- More than ~5 items in one category
- Different team members working on different content types
- Content has complex inter-relationships
Scaling and Game Balance
Using Balance Helpers
The game provides utility functions to keep your content balanced:
// Create appropriately scaled rewards
const rewardAmount = window.modAPI.utils.getNumericReward(
100, // Base amount
'coreFormation', // Player's realm
'Middle', // Progress through realm ('Early', 'Middle', 'Late')
);
// Result: automatically scaled number appropriate for core formation cultivators
Next Steps
🎉 Great work! You’ve learned the fundamentals of AFNM modding:
You now understand:
- ✅ How the ModAPI works (
gameData
,actions
,utils
) - ✅ Creating and registering items
- ✅ Working with images and assets
- ✅ Organizing mod code across files
- ✅ Basic game balance concepts
Ready for more? Your mod is functional but needs to be packaged for the game to load it.
Continue to: Packaging & Testing