@reldens/skills Class Hierarchy

Reference of every class in the @reldens/skills package: inheritance, composition, properties, methods, owner requirements and integration patterns.

Inheritance

  • Skill
    • Attack extends Skill
      • PhysicalAttack extends Attack
    • Effect extends Skill
      • PhysicalEffect extends Effect
  • LevelsSet
    • ClassPath extends LevelsSet
  • Level - standalone data class.
  • SkillsServer - standalone wrapper (composition, not inheritance).
  • Sender (server) and Receiver (client) - standalone communication classes.

No class extends the EventsManager: Skill, LevelsSet and ClassPath hold a reference to it in their events property (EventsManagerSingleton by default).

Class Details

Level

Data container for a single level.

  • key (required) - level number; must be numeric and is converted with parseInt().
  • modifiers - array of @reldens/modifiers Modifier instances (a warning is logged when empty).
  • label - display name (defaults to the key).
  • requiredExperience - total experience needed (also read from required_experience, default 0).

Used by LevelsSet and ClassPath.

LevelsSet

Manages the level progression, the experience and the level modifiers.

Constructor props: owner and events (EventsManager, default EventsManagerSingleton).

init(props) props:

  • levels (required) - object of Level instances indexed by level key.
  • currentLevel (default 0), currentExp (default 0).
  • autoFillRanges (default false), autoFillExperienceMultiplier (default 1.5).
  • increaseLevelsWithExperience (default true).
  • levelsByExperience - ordered array of level keys (default: keys sorted by level key).
  • setRequiredExperienceLimit (default false).
  • ownerIdProperty (default id).

Methods:

  • setOwner(props) - validates and sets the owner and the owner ID property.
  • async init(props), async setLevels(levels), async createLevels(levels) (auto-fill).
  • async levelUp(), async levelDown(), async applyLevelModifiers(revert).
  • async addExperience(number), getNextLevelExperience(), getLevelInstance(levelKey).
  • getOwnerId(), getOwnerEventKey(), getOwnerUniqueEventKey(suffix).
  • async fireEvent(eventName, ...args), listenEvent(eventName, callback, removeKey, masterKey), eventFullName(eventName).

ClassPath

Extends LevelsSet with the skill tree and labels by level.

Additional init(props) props:

  • key (required) - class path identifier.
  • label - base display name (defaults to the key).
  • labelsByLevel - {levelKey: 'label'}.
  • currentLabel - defaults to the label of the highest level reached in labelsByLevel.
  • skillsByLevel - {levelKey: [Skill instances]}.
  • currentSkills - optional preset of the current skills.
  • affectedProperty - property affected by the class skills.

Additional properties: skillsByLevelKeys ({levelKey: [skill keys]}) and currentSkills ({skillKey: Skill}).

Methods:

  • async levelUp() - overridden: adds the skills and the label of the next level, then calls the parent level up.
  • async levelDown() - overridden: removes the skills of the current level, sets the previous level label, then calls the parent level down.
  • async addSkills(skills), async removeSkills(skills) (skill instances or keys), async setOwnerSkills(skills).
  • getCurrentLabel(), getSkillsByLevelKeys().

SkillsServer

Server-side wrapper that creates (or receives) a ClassPath and, with a client, a Sender.

  • owner (required) - must implement getPosition().
  • classPath (optional) - an existing ClassPath instance; otherwise new ClassPath(props) is created.
  • client (optional) - must implement send() and broadcast(); a Sender is created and its listeners registered.
  • All ClassPath init props (key, label, levels, skills by level, etc.).

Properties: classPath and client (the Sender). The constructor starts classPath.init(props) without awaiting it; validation problems are logged as critical errors instead of thrown.

Skill

Base class for every skill type.

Constructor props:

  • key (required), owner (required, with getPosition()), ownerIdProperty (default id).
  • target - fixed target, or pass it to execute().
  • range (default 0 = infinite), rangeAutomaticValidation (default false), rangePropertyX, rangePropertyY, rangeTargetPropertyX, rangeTargetPropertyY.
  • allowSelfTarget (default false) - stored for your own validations.
  • skillDelay (cooldown in ms, default 0), castTime (ms, default 0), canActivate (default true).
  • usesLimit (default 0 = unlimited), autoValidation (default false).
  • ownerConditions - Condition instances; ownerEffects - Modifier instances applied to the owner.
  • criticalChance (percentage 0-100, default 0), criticalMultiplier (default 1), criticalFixedValue (default 0).
  • customData, groups, events.

Runtime properties: isReady (false when required props are missing), isValid, uses, lastState, lastAppliedModifiers, skillActivationTimer, type. The constructor also resets owner.isCasting and owner.castingTimer.

Methods:

  • validate(), validateConditions().
  • validateRange(target), isInRange(ownerPosition, targetPosition), async isValidRange(target).
  • async execute(target), async applySkillLogicOnTarget(target), async finishExecution(target).
  • Hooks: onExecuteConditions(), async runSkillLogic(), async onExecuteRewards().
  • Critical: isCritical(), applyCriticalValue(value), getCriticalDiff(value).
  • applyModifiers(modifiersObjectList, target, avoidCritical).
  • Events and owner: getOwnerId(), getOwnerEventKey(), getOwnerUniqueEventKey(suffix), fireEvent(), listenEvent(), eventFullName().

Attack

Extends Skill with damage calculation. Type SKILL.TYPE.ATTACK (2).

  • affectedProperty (required) - target property that receives the damage.
  • hitDamage (default 0), applyDirectDamage (default false).
  • attackProperties, defenseProperties, aimProperties, dodgeProperties - arrays of property paths (owner for attack and aim, target for defense and dodge).
  • dodgeFullEnabled (default true), dodgeOverAimSuccess (default 1).
  • damageAffected, criticalAffected (default false).
  • propertiesTotalOperators - {propertyPath: ModifierConst.OPS value} to combine a property with an operation other than addition.
  • allowEffectBelowZero (default false).

Methods: runSkillLogic(), applyDamageTo(target), calculateProportionDamage(), calculateCriticalDamage(), getPropertiesTotal(object, properties), getDiffProportion(total, value), getAffectedPropertyValue(target), setAffectedPropertyValue(target, value).

Effect

Extends Skill to apply modifiers to the target (buffs and debuffs). Type SKILL.TYPE.EFFECT (3).

  • targetEffects (required) - Modifier instances applied to the target, with critical.

PhysicalAttack and PhysicalEffect

Extend Attack (type 4) and Effect (type 5) for skills resolved by a physics system: the skill creates a projectile or area through the owner, and the damage or effects are applied on collision.

  • magnitude, objectWidth, objectHeight (required).
  • validateTargetOnHit (default false) - only apply when the collided object is the skill target.
  • The owner must implement executePhysicalSkill(target, skill); otherwise isReady is false.
  • parentType - the type of the parent class (ATTACK or EFFECT).
  • async executeOnHit(target) - call it from your physics system when the collision happens.

Helpers: PhysicalPropertiesValidator (required props) and PhysicalSkillRunner (shared logic for both types).

Sender

Server-side broadcaster created by SkillsServer.

  • constructor(classPath, client).
  • registerListeners() - listens to the class path init end, level up, experience added, skill before cast and attack apply damage events.
  • sendInitClassPathData(), sendLevelUpData(), sendLevelExperienceAdded(), sendSkillBeforeCastData(), sendSkillAttackApplyDamage().
  • runBehaviors(messageData, actionName, behavior, ownerId) - sends, broadcasts or both, and ignores events from other owners.

Receiver

Client-side message processor.

  • Constructor props: owner, actions (extra action to method mapping), avoidDefaults.
  • processMessage(message) - ignores messages without the rski. prefix and calls the mapped handler.
  • setDefaultMethods(), isValidMessage(message).

Composition

  • LevelsSet
    • contains Level instances, each one with Modifier instances.
    • uses the EventsManager.
  • ClassPath
    • inherits the LevelsSet composition.
    • contains Skill instances by level (Attack, Effect, PhysicalAttack, PhysicalEffect), each one with Condition and Modifier instances.
  • SkillsServer
    • contains a ClassPath.
    • contains a Sender, which listens to the ClassPath events and uses the client send() / broadcast().

Server and Client Architecture

  • Server side
    • SkillsServer creates the ClassPath (progression) and the Sender (broadcaster).
    • The ClassPath and its skills fire events through the EventsManager.
    • The Sender listens to those events and sends messages through the client connection.
  • Network - the messages travel through your transport (for example, a Colyseus room).
  • Client side
    • The Receiver processes each message and calls the handler that updates the UI or the game client.

Message flow example:

  1. Server: skillsServer.classPath.addExperience(500) fires the experience added event.
  2. Server: the Sender handles it in sendLevelExperienceAdded(classPath) and calls client.send() with the action rski.Ea, the owner ID and data.exp set to the current experience.
  3. Network: the message is transmitted.
  4. Client: receiver.processMessage(message) calls onLevelExperienceAdded(message), which updates the experience bar.

External Dependencies

  • @reldens/utils - used by all classes:
    • sc (Shortcuts) - property getters and helpers.
    • EventsManagerSingleton - emit() and onWithKey().
    • InteractionArea - range validation in Skill.
    • Logger.
  • @reldens/modifiers - used by Level, Skill, Attack and Effect:
    • Modifier - level modifiers, owner effects and target effects.
    • Condition - owner conditions.
    • PropertyManager and Calculator - property access and property totals.

Owner Requirements

Minimal owner

class GameEntity
{

    constructor(id)
    {
        this.id = id;
        // recommended, used as the events namespace:
        this.eventsPrefix = 'player.'+id;
        this.position = {x: 0, y: 0};
        this.stats = {hp: 100, maxHp: 100, mp: 50, atk: 10, def: 5, aim: 10, dodge: 5};
    }

    getPosition()
    {
        return this.position;
    }

}

Skills manage owner.isCasting and owner.castingTimer themselves for the cast time.

Physical skills owner

class PhysicalEntity extends GameEntity
{

    async executePhysicalSkill(target, skill)
    {
        // create a body using skill.objectWidth, skill.objectHeight and skill.magnitude,
        // then, when your physics engine detects the collision:
        // await skill.executeOnHit(collidedEntity);
    }

}

Client for SkillsServer

let client = {
    send: (message) => {
        // send the message to this owner client only
    },
    broadcast: (message) => {
        // send the message to every client in the room
    }
};

Integration Patterns

1. Basic single class setup

const { ClassPath, Level, Skill } = require('@reldens/skills');

let levels = {
    1: new Level({key: 1, modifiers: [], requiredExperience: 0}),
    5: new Level({key: 5, modifiers: [], requiredExperience: 1000})
};
let basicSkill = new Skill({key: 'basic', owner: player});
let advancedSkill = new Skill({key: 'advanced', owner: player});
let classPath = new ClassPath({owner: player});
await classPath.init({
    key: 'warrior',
    levels: levels,
    currentLevel: 1,
    skillsByLevel: {
        1: [basicSkill],
        5: [advancedSkill]
    }
});

2. Server-side class manager

const SkillsServer = require('@reldens/skills/lib/server');

class PlayerClassManager
{

    constructor(player, client)
    {
        this.player = player;
        this.client = client;
        this.classes = {};
    }

    createClass(key, config)
    {
        this.classes[key] = new SkillsServer({
            owner: this.player,
            client: this.client,
            key: key,
            label: config.label,
            levels: config.levels,
            currentLevel: 1,
            skillsByLevel: config.skillsByLevel
        });
        return this.classes[key];
    }

    async addExperience(classKey, amount)
    {
        if(!this.classes[classKey]){
            return false;
        }
        await this.classes[classKey].classPath.addExperience(amount);
        return true;
    }

}

3. Client-side integration

const { Receiver } = require('@reldens/skills');

class SkillsUi extends Receiver
{

    onInitClassPathEnd(message)
    {
        this.updateLevel(message.data.lvl, message.data.lab);
        this.updateSkills(message.data.skl);
    }

    onLevelUp(message)
    {
        this.updateLevel(message.data.lvl, message.data.lab);
    }

    onLevelExperienceAdded(message)
    {
        this.updateExperience(message.data.exp);
    }

}

let skillsUi = new SkillsUi({owner: player});
// call skillsUi.processMessage(message) for every message received from the server

4. Custom skill type

const { Skill } = require('@reldens/skills');

class HealSkill extends Skill
{

    constructor(props)
    {
        super(props);
        this.healAmount = props.healAmount || 50;
        this.affectedProperty = props.affectedProperty || 'stats/hp';
        this.maxProperty = props.maxProperty || 'stats/maxHp';
    }

    async runSkillLogic()
    {
        if(!this.target){
            return false;
        }
        let healAmount = this.healAmount + this.getCriticalDiff(this.healAmount);
        let currentValue = this.propertyManager.getPropertyValue(this.target, this.affectedProperty);
        let maxValue = this.propertyManager.getPropertyValue(this.target, this.maxProperty);
        let newValue = Math.min(currentValue + healAmount, maxValue);
        this.propertyManager.setOwnerProperty(this.target, this.affectedProperty, newValue);
        await this.fireEvent('custom.skills.healApplied', this, this.target, healAmount, newValue);
        return true;
    }

}

5. Persistent class path

const { ClassPath, SkillsEvents } = require('@reldens/skills');

class PersistentClassPath extends ClassPath
{

    async init(props)
    {
        let savedData = await database.loadClassPath(this.owner.id, props.key);
        if(savedData){
            props.currentLevel = savedData.currentLevel;
            props.currentExp = savedData.currentExp;
        }
        await super.init(props);
        this.listenEvent(
            SkillsEvents.LEVEL_UP,
            async () => await this.save(),
            this.getOwnerUniqueEventKey('persistLevelUp'),
            this.getOwnerEventKey()
        );
        this.listenEvent(
            SkillsEvents.LEVEL_EXPERIENCE_ADDED,
            async () => await this.save(),
            this.getOwnerUniqueEventKey('persistExperience'),
            this.getOwnerEventKey()
        );
    }

    async save()
    {
        await database.saveClassPath(this.owner.id, this.key, {
            currentLevel: this.currentLevel,
            currentExp: this.currentExp
        });
    }

}

Class Responsibility Summary

  • Level - stores the level data. No events.
  • LevelsSet - level progression and experience (init, levelUp, addExperience). Events: level up / down, experience added, level set init.
  • ClassPath - skill trees on top of the progression (setOwnerSkills, addSkills, removeSkills). Events: add / remove skills, set skills, init end.
  • SkillsServer - server to client synchronization setup (constructor only).
  • Skill - base execution (validate, execute, applyModifiers). Events: validate, execute, cast, run logic.
  • Attack - damage calculation. Event: attack apply damage.
  • Effect - target modifiers. Event: effect target modifiers.
  • PhysicalAttack / PhysicalEffect - physics-based execution. Events: physical attack / effect hit.
  • Sender - broadcasts server events to clients.
  • Receiver - processes client messages through on* handlers.

Related Documentation

Go Up