Skip to main content

Overview

The Npc class represents all Non-Player Characters in the game world, including monsters, friendly NPCs, merchants, guards, and quest NPCs. It extends Creature and uses templates to define static properties. Package: org.l2jmobius.gameserver.model.actor Hierarchy:
WorldObject
  └── Creature
      └── Npc
          ├── Attackable (Monsters)
          ├── Monster
          ├── Guard
          ├── Merchant
          ├── Teleporter
          └── ... (50+ specialized types)
Source: model/actor/Npc.java:126

Class Hierarchy

Npc (Base)

Core NPC functionality for all non-player creatures

Attackable

Monsters and hostile creatures with AI

Guard

Town guards and defenders

Merchant

Shop NPCs and traders

Key Properties

Core Identity

_spawn
Spawn
The spawn object that manages this NPC’s respawn behavior
_isBusy
boolean
default:"false"
Whether this NPC is currently busy (in dialogue, etc.)
_busyMessage
String
default:"\"\""
Message to display when NPC is busy
_isDecayed
boolean
default:"false"
Whether the decay task has been called (for corpses)

Behavior Flags

_isAutoAttackable
boolean
default:"false"
Whether this NPC can be auto-attacked
_isRandomAnimationEnabled
boolean
default:"true"
Enables random idle animations
_isRandomWalkingEnabled
boolean
default:"true"
Enables random walking behavior
_isTalkable
boolean
Whether players can talk to this NPC (from template)
_isQuestMonster
boolean
Whether this is a quest-related monster (from template)
_isFakePlayer
boolean
Whether this NPC appears as a fake player (from template)

Visual Properties

_currentLHandId
int
Current left-hand weapon/item ID
_currentRHandId
int
Current right-hand weapon/item ID
_currentEnchant
int
Current weapon enchant level (visual)
_currentCollisionHeight
double
Current collision height (used for grow effects)
_currentCollisionRadius
double
Current collision radius (used for grow effects)

Combat Properties

_soulshotamount
int
default:"0"
Soulshot count for this NPC
_spiritshotamount
int
default:"0"
Spiritshot count for this NPC
_shotsMask
int
default:"0"
Bitmask of active shot types

Core Methods

Template and Configuration

getTemplate
NpcTemplate
Returns the NPC’s template with base stats and configuration
NpcTemplate template = npc.getTemplate();
int npcId = template.getId();
String name = template.getName();
getId
int
Returns the NPC’s template ID
int npcId = npc.getId();
if (npcId == 30001) {
    // Specific NPC logic
}

Spawn Management

getSpawn
Spawn
Returns the spawn object managing this NPC
Spawn spawn = npc.getSpawn();
if (spawn != null) {
    int respawnDelay = spawn.getRespawnDelay();
}
setSpawn
void
Sets the spawn object for this NPC
npc.setSpawn(spawnObject);

Behavior Control

isBusy
boolean
Checks if NPC is busy
if (npc.isBusy()) {
    player.sendMessage(npc.getBusyMessage());
    return;
}
setBusy
void
Sets the busy state and message
npc.setBusy(true);
npc.setBusyMessage("I'm currently helping someone else.");
isAutoAttackable
boolean
Checks if NPC can be auto-attacked
boolean canAttack = npc.isAutoAttackable(player);
setIsAutoAttackable
void
Sets auto-attackable state
npc.setIsAutoAttackable(true);

Animation and Movement

setRandomAnimationEnabled
void
Enables or disables random animations
npc.setRandomAnimationEnabled(false); // Disable idle animations
setRandomWalkingEnabled
void
Enables or disables random walking
npc.setRandomWalkingEnabled(true);

Dialogue and Interaction

showChatWindow
void
Shows HTML dialogue window to player
npc.showChatWindow(player);
// Or with custom HTML file
npc.showChatWindow(player, "data/html/merchant/30001.htm");
onBypassFeedback
void
Handles bypass commands from HTML buttons
@Override
public void onBypassFeedback(Player player, String command) {
    if (command.startsWith("Quest")) {
        // Handle quest bypass
    }
}
isTalkable
boolean
Checks if NPC can be talked to
if (npc.isTalkable()) {
    npc.showChatWindow(player);
}

Decay and Cleanup

isDecayed
boolean
Checks if NPC corpse has decayed
if (npc.isDead() && !npc.isDecayed()) {
    // Corpse still visible
}
deleteMe
void
Removes NPC from the world
npc.deleteMe();

Script Integration

getScriptValue
int
Gets script-specific value
int value = npc.getScriptValue();
setScriptValue
void
Sets script-specific value
npc.setScriptValue(1); // Use for script state

Summoning

getSummonedNpcs
Map<Integer, Npc>
Gets all NPCs summoned by this NPC
Map<Integer, Npc> summons = npc.getSummonedNpcs();
if (summons != null) {
    for (Npc summon : summons.values()) {
        // Process summoned NPCs
    }
}

Common Subclasses

Attackable (Monsters)

Extends Npc with combat AI and aggression mechanics.
Attackable monster = (Attackable) npc;
monster.addDamageHate(player, 0, 1000); // Add hate
boolean isAggressive = monster.isAggressive();

Guard

Town guards that protect areas and attack criminals.
Guard guard = new Guard(template);
guard.setIsAutoAttackable(false);

Merchant

NPCs that can buy and sell items.
Merchant merchant = new Merchant(template);
merchant.showBuyWindow(player, 0);

Teleporter

NPCs that provide teleportation services.
Teleporter teleporter = new Teleporter(template);
teleporter.showTeleportList(player);

Usage Examples

Creating and Spawning NPCs

// Get NPC template
NpcTemplate template = NpcData.getInstance().getTemplate(30001);

// Create spawn
Spawn spawn = new Spawn(template);
spawn.setLocation(x, y, z, heading);
spawn.setRespawnDelay(60); // 60 seconds

// Spawn the NPC
Npc npc = spawn.doSpawn();

Custom NPC Dialogue

public class CustomNpc extends Npc {
    public CustomNpc(NpcTemplate template) {
        super(template);
    }
    
    @Override
    public void showChatWindow(Player player) {
        NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
        html.setFile(player, "data/html/custom/welcome.htm");
        html.replace("%name%", player.getName());
        player.sendPacket(html);
    }
    
    @Override
    public void onBypassFeedback(Player player, String command) {
        if (command.equals("givereward")) {
            player.addItem("Reward", 57, 10000, this, true);
        }
    }
}

Quest NPC Implementation

// In quest script
public class MyQuest extends Quest {
    private static final int QUEST_NPC = 30001;
    
    @Override
    public String onTalk(Npc npc, Player player) {
        QuestState qs = getQuestState(player, true);
        
        if (npc.getId() == QUEST_NPC) {
            if (qs.isStarted()) {
                return "quest_running.htm";
            } else {
                return "quest_start.htm";
            }
        }
        return null;
    }
    
    @Override
    public String onKill(Npc npc, Player killer, boolean isSummon) {
        QuestState qs = getQuestState(killer, false);
        if (qs != null && qs.isCond(1)) {
            qs.setCond(2, true);
        }
        return null;
    }
}

Boss Mechanics

public class RaidBoss extends GrandBoss {
    private static final int MINION_ID = 20001;
    
    public RaidBoss(NpcTemplate template) {
        super(template);
    }
    
    @Override
    protected void onDeath(Creature killer) {
        // Despawn all minions
        Map<Integer, Npc> summons = getSummonedNpcs();
        if (summons != null) {
            for (Npc minion : summons.values()) {
                minion.deleteMe();
            }
        }
        
        // Handle rewards
        if (killer.getActingPlayer() != null) {
            Player player = killer.getActingPlayer();
            // Give rewards to party/raid
        }
        
        super.onDeath(killer);
    }
    
    // Spawn minions when HP drops
    @Override
    public void reduceCurrentHp(double damage, Creature attacker, 
                                Skill skill, boolean isDOT) {
        double hpRatio = getCurrentHp() / getMaxHp();
        
        if (hpRatio < 0.5 && getScriptValue() == 0) {
            setScriptValue(1); // Mark as triggered
            spawnMinions();
        }
        
        super.reduceCurrentHp(damage, attacker, skill, isDOT);
    }
    
    private void spawnMinions() {
        for (int i = 0; i < 5; i++) {
            addMinion(MINION_ID, false);
        }
    }
}

NPC with Custom AI

// Set custom AI
Npc npc = spawn.doSpawn();
npc.setAI(new CustomAI(npc));

// Custom AI class
public class CustomAI extends NpcAI {
    public CustomAI(Npc npc) {
        super(npc);
    }
    
    @Override
    protected void onEvtAttacked(Creature attacker, int damage) {
        // Custom attack response
        if (getRandom(100) < 30) {
            // 30% chance to teleport when attacked
            Npc npc = (Npc) _actor;
            npc.teleToLocation(npc.getSpawn().getLocation());
        }
        super.onEvtAttacked(attacker, damage);
    }
}

Constants

ConstantValueDescription
INTERACTION_DISTANCE250Interaction distance offset
RANDOM_ITEM_DROP_LIMIT70Max distance for item drops
MINIMUM_SOCIAL_INTERVAL6000Min time between social actions (ms)

Events

The NPC class triggers various events that can be listened to:
  • OnNpcSpawn - When NPC spawns
  • OnNpcTeleport - When NPC teleports
  • OnNpcSkillFinished - When NPC finishes casting
  • OnNpcCanBeSeen - Visibility check event
  • OnNpcEventReceived - Custom event handling

NpcTemplate

Template defining NPC base stats and behavior

Spawn

Manages NPC spawning and respawning

NpcAI

AI controller for NPC behavior

Attackable

Monster subclass with combat AI

Notes

  • NPC templates are loaded once and shared across all instances
  • Use getScriptValue() for temporary NPC state in scripts
  • NPCs auto-cleanup via DecayTaskManager after death
  • Fake player NPCs appear as players in the client
  • Quest monsters have special loot mechanics

Build docs developers (and LLMs) love