Modding Guide: Adding a New Mob
Relevant source files
The following files were used as context for generating this wiki page:
- core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java
- core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/MobSpawner.java
- core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/MobSprite.java
- core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java
- core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/CrystalWisp.java
- core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java
- core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndInfoMob.java
- core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM200.java
This guide provides a comprehensive walkthrough for adding a new monster (mob) to Shattered Pixel Dungeon. Creating a new mob involves defining its characteristics and behavior by extending the base Mob class, creating a visual representation by extending MobSprite, and integrating it into the game's spawning system via the MobSpawner.
The core architecture is designed to be extensible. By overriding methods in the Mob class, you can define everything from basic stats to complex AI and special attacks. The visual component is handled separately in a MobSprite class, which is linked to your mob logic. Finally, the MobSpawner class controls which mobs appear at which dungeon depths, allowing you to place your new creation in the appropriate part of the game.
Core Components
Adding a new mob primarily involves three key Java classes, with the Level class orchestrating their creation.
The Mob Class
The Mob class is the foundation for all non-player characters in the game. It extends com.shatteredpixel.shatteredpixeldungeon.actors.Char and contains all the logic for a mob's stats, behavior, and interactions.
- Key Properties: When creating a new mob, you'll set its base attributes in an initialization block.
- AI States: The mob's behavior is governed by an
AiStateinterface, with default implementations forSLEEPING,HUNTING,WANDERING,FLEEING, andPASSIVE. - Persistence: Custom mob properties that need to be saved and loaded with the game must be handled in
storeInBundle()andrestoreFromBundle().
The following class diagram illustrates the basic inheritance structure for a mob.
The MobSprite Class
The MobSprite class is responsible for the mob's visual representation, including its appearance and animations. Each Mob subclass must be associated with a MobSprite subclass.
- Linking: The link is established in the
Mobsubclass by setting thespriteClassvariable to your new sprite class (e.g.,spriteClass = CrystalWispSprite.class;). - Animations:
MobSpritecontains methods for standard animations likeattack()anddie(). You can also add custom animation methods, such aszap(), which can be called from yourMoblogic.
Sources: sprites/MobSprite.java, actors/mobs/CrystalWisp.java:33
The MobSpawner and Level Classes
The MobSpawner class determines which mobs are eligible to spawn on each floor of the dungeon. The Level class then uses this information to create and place mobs.
MobSpawner.getMobRotation(int depth): This is the central method you will modify. It returns anArrayListof mob classes for a given dungeon depth. You must add your new mob's class to this list for the desired depth range.Level.createMob(): This method callsMobSpawner.getMobRotation()to get the list of valid mobs and then uses reflection to instantiate one.GameScene.add(Mob mob): Once a mob is created, it and its sprite are added to the current game scene to be rendered and processed.
This sequence diagram shows the high-level process of a mob being spawned.
Implementation Steps
1. Create the Mob Class
First, create a new Java class that extends com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob. Inside this class, you'll define the mob's core stats and behavior.
An initialization block is used to set the mob's default attributes.
// core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/CrystalWisp.java
public class CrystalWisp extends Mob{
{
spriteClass = CrystalWispSprite.class;
HP = HT = 30;
defenseSkill = 16;
EXP = 7;
maxLvl = -2;
flying = true;
properties.add(Property.INORGANIC);
}
// ...
}
Sources: actors/mobs/CrystalWisp.java:32-43
You must override several methods to define its combat abilities:
damageRoll(): Returns the damage dealt by a standard attack.attackSkill(Char target): Returns the mob's accuracy against a specific target.drRoll(): Returns the mob's damage reduction value.
2. Create the Mob Sprite
Next, create a class for the mob's visual representation that extends com.shatteredpixel.shatteredpixeldungeon.sprites.MobSprite. In your Mob subclass, set the spriteClass variable to point to your new sprite class.
// In your Mob subclass
spriteClass = YourMobSprite.class;
The sprite class can be very simple if no custom animations are needed. For custom animations, add new methods to your sprite class and call them from your mob's logic. For example, CrystalWisp calls sprite.zap(enemy.pos) to trigger its ranged attack animation.
Sources: actors/mobs/CrystalWisp.java:33, actors/mobs/CrystalWisp.java:87, sprites/MobSprite.java
3. Add to the Mob Spawner
To make your mob appear in the dungeon, you must add it to the spawn lists in MobSpawner.java.
- Open
core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/MobSpawner.java. - Locate the
getMobRotation(int depth)method. - Add your mob's class to the
mobslist within theifblock corresponding to the dungeon depths where you want it to appear.
// core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/MobSpawner.java
public static ArrayList<Class<? extends Mob>> getMobRotation( int depth ) {
// ...
if (depth >= 12 && depth <= 14) {
// ...
mobs.add( DM200.class );
mobs.add( YourNewMob.class ); // Add your mob here
}
// ...
}
Sources: actors/mobs/MobSpawner.java
Advanced Features
Custom AI and Behavior
The Mob class uses a state machine for its AI. The default states are defined in Mob.java as inner classes implementing the AiState interface.
This diagram shows the primary AI state transitions for a typical mob.
Sources: actors/mobs/Mob.java:1158-1358
For simple custom logic, you can override the act() method. For more complex behaviors, you can define your own AiState implementation, as seen in DM200.java, which has a custom Hunting state to handle its gas attack.
Sources: actors/mobs/Mob.java:101-107, actors/mobs/DM200.java:132-169
Special Attacks
To implement a special attack, override the doAttack(Char enemy) method. You can check the distance to the target and execute different logic for melee versus ranged abilities. The CrystalWisp class provides a good example of a ranged "zap" attack.
// core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/CrystalWisp.java:83-96
protected boolean doAttack(Char enemy ) {
if (Dungeon.level.adjacent( pos, enemy.pos )
|| new Ballistica( pos, enemy.pos, Ballistica.MAGIC_BOLT).collisionPos != enemy.pos) {
return super.doAttack( enemy );
} else {
if (sprite != null && (sprite.visible || enemy.sprite.visible)) {
sprite.zap( enemy.pos );
return false;
} else {
zap();
return true;
}
}
}
Sources: actors/mobs/CrystalWisp.java:83-96
Custom Loot Drops
You can define custom loot by setting the loot and lootChance properties in your mob's initialization block.
| Property | Type | Description |
|---|---|---|
loot | Object | Can be a Generator.Category enum or a specific Item class. |
lootChance | float | The base probability (0.0 to 1.0) that this mob will drop its loot upon death. |
For more complex loot logic, such as limiting the total number of drops of a certain item, you can override the createLoot() method. DM200 does this to limit the number of equipment drops.
Sources: actors/mobs/Mob.java:1030-1052, actors/mobs/DM200.java:49-63
Saving and Loading State
If your mob has custom properties that need to persist across game sessions, you must implement storeInBundle() and restoreFromBundle().
CrystalWisp uses this to save its color, which is determined randomly on creation.
// core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/CrystalWisp.java:122-128
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(SPRITE, spriteClass);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
spriteClass = bundle.getClass(SPRITE);
}
Sources: actors/mobs/CrystalWisp.java:122-128
UI Display
Information about your mob is displayed in a WndInfoMob window when the player examines it.
- Name: The mob's name is taken from the
name()method. - Description: The descriptive text is provided by the
info()method, which defaults to callingdescription(). You can override either of these to provide custom text. - Health and Buffs: The window automatically includes a
HealthBarand aBuffIndicatorto show the mob's current status.
Sources: windows/WndInfoMob.java, actors/mobs/Mob.java:1130-1142
Summary
Adding a new mob is a structured process of class extension and integration. By creating Mob and MobSprite subclasses, you define the mob's logic and appearance. You then register it with the MobSpawner to make it appear in the game. The system is highly flexible, allowing for custom AI, special attacks, unique loot tables, and persistent state, giving modders powerful tools to expand the dungeon's bestiary.