Skip to main content

Overview

The L2J Mobius skill system manages all character abilities including active skills, passive buffs, debuffs, and special abilities. Skills are defined by templates and applied through buff effects. Package: org.l2jmobius.gameserver.model.skill

Class Hierarchy

Skill

Core skill definition and properties

BuffInfo

Active buff/debuff instance on a creature

SkillHolder

Lightweight skill reference (ID + level)

AbnormalType

Skill effect category (buffs, debuffs)

Skill Class

Overview

The Skill class represents a complete skill definition with all properties, effects, and targeting information. Source: model/skill/Skill.java:68

Core Properties

_id
int
required
Unique skill ID
_level
int
required
Skill level (1-100+)
_displayId
int
Custom skill ID shown to client (defaults to ID)
_displayLevel
int
Custom skill level shown to client
_name
String
required
Skill name
_operateType
SkillOperateType
required
Operation type: ACTIVE, PASSIVE, TOGGLE

Resource Costs

_mpConsume
int
default:"0"
MP consumed on cast
_mpInitialConsume
int
default:"0"
Initial MP cost (before cast starts)
_mpPerChanneling
int
default:"0"
MP consumed per channeling tick
_hpConsume
int
default:"0"
HP consumed on cast
_itemConsumeId
int
default:"0"
Item ID required for casting
_itemConsumeCount
int
default:"0"
Number of items consumed

Timing Properties

_hitTime
int
Cast time in milliseconds
_coolTime
int
Animation cooldown after cast (ms)
_reuseDelay
int
Reuse delay in milliseconds

Range and Targeting

_castRange
int
Maximum cast range
_effectRange
int
Effect application range
_targetType
TargetType
required
Target type: SELF, TARGET, PARTY, CLAN, AREA, etc.
_affectRange
int
Area of effect radius

Power and Damage

_power
double
Base skill power
_pvpPower
double
Power modifier in PvP
_pvePower
double
Power modifier in PvE
_magicLevel
int
Magic level for resist calculations

Abnormal (Buff/Debuff) Properties

_abnormalType
AbnormalType
required
Abnormal category (determines stacking)
_abnormalLevel
int
Abnormal level (higher level replaces lower)
_abnormalTime
int
Duration in seconds
_isAbnormalInstant
boolean
default:"false"
Whether effect applies instantly (herbs)
_stayAfterDeath
boolean
default:"false"
Whether buff persists after death

Key Methods

Skill Information

getId
int
Returns skill ID
int skillId = skill.getId();
getLevel
int
Returns skill level
int level = skill.getLevel();
getName
String
Returns skill name
String name = skill.getName();
getOperateType
SkillOperateType
Returns operation type
if (skill.getOperateType() == SkillOperateType.TOGGLE) {
    // Handle toggle skill
}

Resource Costs

getMpConsume
int
Returns MP cost
int mpCost = skill.getMpConsume();
if (player.getCurrentMp() < mpCost) {
    player.sendMessage("Not enough MP");
}
getHpConsume
int
Returns HP cost
int hpCost = skill.getHpConsume();

Timing

getHitTime
int
Returns cast time in milliseconds
int castTime = skill.getHitTime();
getReuseDelay
int
Returns reuse delay
int reuse = skill.getReuseDelay();
player.addTimeStamp(skill, reuse);

Range and Targeting

getCastRange
int
Returns cast range
int range = skill.getCastRange();
if (!player.isInsideRadius3D(target, range)) {
    player.sendMessage("Target too far");
}
getTargetType
TargetType
Returns target type
TargetType targetType = skill.getTargetType();
isAOE
boolean
Checks if skill is area of effect
if (skill.isAOE()) {
    // Get targets in area
}

Power and Damage

getPower
double
Returns skill power (with PvP/PvE modifiers)
double power = skill.getPower(caster, target, isPvP, isPvE);
isDamage
boolean
Checks if skill deals damage
if (skill.isDamage()) {
    // Apply damage calculations
}

Abnormal Properties

getAbnormalType
AbnormalType
Returns abnormal type
AbnormalType type = skill.getAbnormalType();
getAbnormalLevel
int
Returns abnormal level
int level = skill.getAbnormalLevel();
getAbnormalTime
int
Returns duration in seconds
int duration = skill.getAbnormalTime();
isDebuff
boolean
Checks if skill is a debuff
if (skill.isDebuff()) {
    // Can be cleansed
}

BuffInfo Class

Overview

Represents an active buff or debuff effect on a creature. Source: model/skill/BuffInfo.java:43

Properties

_skill
Skill
required
The skill that created this buff
_effected
Creature
required
The creature affected by this buff
_effector
Creature
The creature that cast this buff
_abnormalTime
int
Remaining time in seconds

Key Methods

getSkill
Skill
Returns the skill
Skill skill = buffInfo.getSkill();
getEffected
Creature
Returns affected creature
Creature target = buffInfo.getEffected();
getEffector
Creature
Returns caster
Creature caster = buffInfo.getEffector();
getTime
int
Returns remaining time
int remaining = buffInfo.getTime();
resetAbnormalTime
void
Resets buff duration
buffInfo.resetAbnormalTime(skill.getAbnormalTime());

SkillHolder Class

Overview

Lightweight skill reference containing only ID and level. Source: model/skill/holders/SkillHolder.java

Usage

getSkill
Skill
Retrieves the actual skill object
SkillHolder holder = new SkillHolder(1234, 5);
Skill skill = holder.getSkill();

Usage Examples

Casting Skills

public void castSkill(Creature caster, WorldObject target, Skill skill) {
    // Check MP
    if (caster.getCurrentMp() < skill.getMpConsume()) {
        caster.sendPacket(SystemMessageId.NOT_ENOUGH_MP);
        return;
    }
    
    // Check range
    if (!caster.isInsideRadius3D(target, skill.getCastRange())) {
        caster.sendPacket(SystemMessageId.TARGET_IS_OUT_OF_RANGE);
        return;
    }
    
    // Check reuse
    if (caster.isSkillDisabled(skill)) {
        caster.sendPacket(SystemMessageId.SKILL_NOT_AVAILABLE);
        return;
    }
    
    // Cast skill
    caster.doCast(skill);
}

Buff Management

// Add buff to player
public void applyBuff(Player player, Skill skill) {
    // Create buff info
    BuffInfo buffInfo = new BuffInfo(player, player, skill);
    
    // Check for existing buff of same type
    BuffInfo existing = player.getEffectList()
        .getBuffInfoByAbnormalType(skill.getAbnormalType());
    
    if (existing != null) {
        // Compare levels
        if (skill.getAbnormalLevel() > existing.getSkill().getAbnormalLevel()) {
            // Remove old buff
            existing.getEffected().getEffectList().remove(existing);
        } else {
            player.sendMessage("A better buff is already active.");
            return;
        }
    }
    
    // Apply new buff
    buffInfo.initializeEffects();
}

// Remove specific buff
public void removeBuff(Creature creature, AbnormalType type) {
    BuffInfo buff = creature.getEffectList().getBuffInfoByAbnormalType(type);
    if (buff != null) {
        creature.getEffectList().remove(buff);
    }
}

// Remove all buffs
public void removeAllBuffs(Creature creature) {
    creature.getEffectList().stopAllEffects();
}

Skill Learning

// Learn skill
public boolean learnSkill(Player player, int skillId, int skillLevel) {
    // Get skill
    Skill skill = SkillData.getInstance().getSkill(skillId, skillLevel);
    if (skill == null) {
        return false;
    }
    
    // Check requirements
    SkillLearn learn = SkillTreeData.getInstance()
        .getSkillLearn(player.getClassId(), skillId, skillLevel);
    
    if (learn == null) {
        player.sendMessage("You cannot learn this skill.");
        return false;
    }
    
    // Check level requirement
    if (player.getLevel() < learn.getGetLevel()) {
        player.sendMessage("Level too low.");
        return false;
    }
    
    // Check SP cost
    if (player.getSp() < learn.getLevelUpSp()) {
        player.sendMessage("Not enough SP.");
        return false;
    }
    
    // Learn skill
    player.addSkill(skill, true);
    player.getStat().addSp(-learn.getLevelUpSp());
    
    return true;
}

Custom Skill Effects

// Damage skill
public void applyDamageSkill(Creature attacker, Creature target, Skill skill) {
    // Calculate damage
    boolean isPvP = attacker.isPlayer() && target.isPlayer();
    boolean isPvE = !isPvP;
    double power = skill.getPower(attacker, target, isPvP, isPvE);
    
    // Apply formulas
    double damage = Formulas.calcMagicDam(attacker, target, skill, power);
    
    // Apply damage
    target.reduceCurrentHp(damage, attacker, skill);
    
    // Apply effects
    skill.applyEffects(attacker, target);
}

// Heal skill
public void applyHealSkill(Creature caster, Creature target, Skill skill) {
    // Calculate heal amount
    double power = skill.getPower();
    double heal = power + (caster.getPAtk() * 0.05);
    
    // Apply heal
    double newHp = Math.min(target.getMaxHp(), 
                           target.getCurrentHp() + heal);
    target.setCurrentHp(newHp);
    
    // Visual effect
    target.broadcastPacket(new MagicSkillUse(caster, target, 
                          skill.getId(), skill.getLevel(), 0, 0));
}

Skill Reuse Management

// Disable skill
public void disableSkill(Player player, Skill skill) {
    int reuseDelay = skill.getReuseDelay();
    if (reuseDelay > 0) {
        player.addTimeStamp(skill, reuseDelay);
        player.disableSkill(skill, reuseDelay);
    }
}

// Check if skill is disabled
public boolean isSkillDisabled(Player player, Skill skill) {
    return player.isSkillDisabled(skill);
}

// Get remaining reuse time
public long getRemainingReuse(Player player, Skill skill) {
    TimeStamp ts = player.getSkillReuseTimeStamp(skill.getReuseHashCode());
    if (ts == null) {
        return 0;
    }
    return ts.getRemaining();
}

// Reset skill reuse
public void resetSkillReuse(Player player, Skill skill) {
    player.removeTimeStamp(skill);
    player.enableSkill(skill);
}

Skill Conditions

// Check skill conditions
public boolean checkConditions(Creature caster, WorldObject target, Skill skill) {
    // Check if skill can be used
    if (skill.getOperateType() == SkillOperateType.PASSIVE) {
        return false;
    }
    
    // Check target
    if (target == null && skill.getTargetType() == TargetType.TARGET) {
        caster.sendPacket(SystemMessageId.TARGET_CANT_FOUND);
        return false;
    }
    
    // Check HP cost
    if (caster.getCurrentHp() <= skill.getHpConsume()) {
        caster.sendPacket(SystemMessageId.NOT_ENOUGH_HP);
        return false;
    }
    
    // Check items
    if (skill.getItemConsumeId() > 0) {
        if (caster.isPlayer()) {
            Player player = caster.getActingPlayer();
            if (player.getInventory().getInventoryItemCount(
                    skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()) {
                player.sendPacket(SystemMessageId.THERE_ARE_NOT_ENOUGH_NECESSARY_ITEMS_TO_USE_THE_SKILL);
                return false;
            }
        }
    }
    
    return true;
}

Toggle Skills

// Toggle skill on/off
public void toggleSkill(Player player, Skill skill) {
    if (skill.getOperateType() != SkillOperateType.TOGGLE) {
        return;
    }
    
    // Check if already active
    BuffInfo existing = player.getEffectList()
        .getBuffInfoBySkillId(skill.getId());
    
    if (existing != null) {
        // Deactivate
        existing.getEffected().getEffectList().remove(existing);
        player.sendMessage(skill.getName() + " deactivated.");
    } else {
        // Activate
        if (checkConditions(player, player, skill)) {
            skill.applyEffects(player, player);
            player.sendMessage(skill.getName() + " activated.");
        }
    }
}

Skill Operation Types

TypeDescription
ACTIVECast once, applies effects
PASSIVEAlways active, no casting
TOGGLEOn/off state with continuous MP drain

Common Abnormal Types

TypeDescription
BUFFGeneric beneficial effect
DEBUFFGeneric negative effect
HP_RECOVERHP regeneration
MP_RECOVERMP regeneration
SPEED_UPMovement speed increase
SLOWMovement speed decrease
STUNCannot move or act
ROOTCannot move
POISONDamage over time

SkillData

Skill data manager and loader

SkillTreeData

Manages skill learning requirements

EffectList

Manages active buffs on a creature

Formulas

Damage and effect calculations

Notes

  • Skills with same AbnormalType don’t stack (highest level wins)
  • Toggle skills require continuous MP consumption
  • Passive skills are always active and cannot be cast
  • Debuffs can be cleansed unless flagged as unremovable
  • Skill reuse is shared by hash code for skill groups

Build docs developers (and LLMs) love