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
Custom skill ID shown to client (defaults to ID)
Custom skill level shown to client
Operation type: ACTIVE, PASSIVE, TOGGLE
Resource Costs
Initial MP cost (before cast starts)
MP consumed per channeling tick
Item ID required for casting
Timing Properties
Cast time in milliseconds
Animation cooldown after cast (ms)
Reuse delay in milliseconds
Range and Targeting
Target type: SELF, TARGET, PARTY, CLAN, AREA, etc.
Power and Damage
Magic level for resist calculations
Abnormal (Buff/Debuff) Properties
Abnormal category (determines stacking)
Abnormal level (higher level replaces lower)
Whether effect applies instantly (herbs)
Whether buff persists after death
Key Methods
Returns skill ID int skillId = skill . getId ();
Returns skill level int level = skill . getLevel ();
Returns skill name String name = skill . getName ();
Returns operation type if ( skill . getOperateType () == SkillOperateType . TOGGLE ) {
// Handle toggle skill
}
Resource Costs
Returns MP cost int mpCost = skill . getMpConsume ();
if ( player . getCurrentMp () < mpCost) {
player . sendMessage ( "Not enough MP" );
}
Returns HP cost int hpCost = skill . getHpConsume ();
Timing
Returns cast time in milliseconds int castTime = skill . getHitTime ();
Returns reuse delay int reuse = skill . getReuseDelay ();
player . addTimeStamp (skill, reuse);
Range and Targeting
Returns cast range int range = skill . getCastRange ();
if ( ! player . isInsideRadius3D (target, range)) {
player . sendMessage ( "Target too far" );
}
Returns target type TargetType targetType = skill . getTargetType ();
Checks if skill is area of effect if ( skill . isAOE ()) {
// Get targets in area
}
Power and Damage
Returns skill power (with PvP/PvE modifiers) double power = skill . getPower (caster, target, isPvP, isPvE);
Checks if skill deals damage if ( skill . isDamage ()) {
// Apply damage calculations
}
Abnormal Properties
Returns abnormal type AbnormalType type = skill . getAbnormalType ();
Returns abnormal level int level = skill . getAbnormalLevel ();
Returns duration in seconds int duration = skill . getAbnormalTime ();
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
The skill that created this buff
The creature affected by this buff
The creature that cast this buff
Remaining time in seconds
Key Methods
Returns the skill Skill skill = buffInfo . getSkill ();
Returns affected creature Creature target = buffInfo . getEffected ();
Returns caster Creature caster = buffInfo . getEffector ();
Returns remaining time int remaining = buffInfo . getTime ();
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
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
Type Description ACTIVECast once, applies effects PASSIVEAlways active, no casting TOGGLEOn/off state with continuous MP drain
Common Abnormal Types
Type Description 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