Dungeon Hopper

Dungeon Hopper

  • Docs

›Topics

Getting Started

  • What is this?

Terminology And Basics

  • The lifecycle of the game
  • Chains and Chain Templates
  • Calling Conventions

Topics

  • Game Loop
  • Decorators
  • Action
  • Algorithms
  • Movs Algorithms
  • Sequence
  • Modifiers
  • Dynamic Stats
  • Retouchers
  • Tinkers
  • Status effects
  • Items
  • Weapons
  • Pools
  • World Generation
  • Mods

Decorators

Overview

Decorators are used extensively throughout the project. They act pretty much like individual components that are used to augment your entity classes. They are not dynamic, that is, they cannot be added on already instantiated objects. Nevertheless, the chains they add are dynamic and can be modified at any time.

Method name conventions

  1. be<Something> methods refer to applying that something on the entity after a series of checks that are able to interfere with that, not allowing the effects. For example, beAttacked does the attack after going through a series of handlers, e.g. blocking some of the damage, or stopping the event from propagating completely, e.g. by being invincible.
  2. execute<Something> is the same as the first one, except it is that object that does the action, that is, it is not the subject of the action. Foe example, executeAttack would make the entity try to do an attack.

How to use the decorators

Assume you have an entity class you'd like to decorate. For that, just do the following:

Decorators.Start(MyEntity)                -- create the chain template on that object
decorate(MyEntity, MyDecorator)           -- add stuff to that template, add stuff to your entity
decorate(MyEntity, Decorators.Attackable) -- apply a predefined decorator

You do not need to import anything, since the basic decorators and the decorate function are available globally.

NOTE: You cannot decorate instances! You can apply the decorators only to classes! So, in order to, e.g., stop your entities from taking damage while they are in some phase of their lifecycle, use some other logic, e.g. adding handlers to chains. In our example, this would mean stopping event from propagating in myEntity.chains.defence chain by adding a nullifier handler to it

What do decorators do?

In short, Decorators add particular behaviour to your classes.

They all share these 3 basic stages

Decoration

This is the moment the Decorator.decorate method is called. It works by taking the list of affectedChains from the decorator class and adding them to the chainTemplate of your entity class.

For example, such chains would create a chain named myCheck on the entity class's chainTemplate and add the handlers checkHandler1 and checkHandler2 onto it. After that, it would create the chain myExecute, adding just the executeHandler1 onto it and, finally, it would just create the new myEmpty chain on the chainTemplate, without adding any handler onto it:

MyDecorator.affectedChains = {
    { 'myCheck',   { checkHandler1, checkHandler2 } },
    { 'myExecute', { executeHandler1 }              },
    { 'myEmpty',   {}                               }
}

By default, sorted chains and chain templates are used, which means one can do the following:

MyDecorator.affectedChains = {
    { 'myCheck',   
        { 
            { checkHandler1, Ranks.LOW }, -- using priority rank
            { checkHandler2, 600000    }, -- using priority directly 
            checkHandler3                 -- defaults to MEDIUM
        } 
    },
}

The decorator also gets pushed to the myEntityClass.decoratorsList for the further initialization stage.

Initialization

The initialization stage takes place when your entity class is being instantiated. At this time, all Decorators saved on myEntityClass.decoratorsList are getting instantiated and their myDecorator:__construct() methods called. This is the moment they should instantiate things on the instance, if they need to. The decorator instances are saved on entity.decorators (entity here being your instantiated myEntityClass), which is a table where the keys are the names of the applied decorator classes. For example, to access the Attackable decorator instance, one would do:

entity.decorators.Attackable

This is useful for checking whether a specific decorator has been applied, although you are encouraged to use entity:isDecorated(decorator) if you are just checking.

decorate(EntityClass, Decorators.Attackable)
local entity = EntityClass()
print(entity:isDecorated(Decorators.Attackable)) -- true
print(class.name(entity.decorators.Attackable)) -- Attackable

Activation

Activation as such implies the decorator's method myDecorator:activate() getting called. Typically, it would have the instance as the first parameter.

A fair amount of predefined decorators use the checkApplyCycle as their activation. The idea is straightforward: they would do a pass over their check chain and, if it were successful, that is, if it went through all its handlers without getting interrupted, the do chain is going to be passed too. Otherwise, it wouldn't. See logic.decorators.utils.

For example, take Decorators.Attacking. It adds two chains: getAttack, which is the check (or get) chain, and attack, which is the do chain.

List of basic decorators

Start

Adds the chainTemplate and decoratorsList to your Entity class.

This is technically not a decorator, but just an ordinary function.

You must call this first if you want your next decorators to work at all.

Acting

Enables the entity to apply action that were saved as Entity.nextAction during the computeAction beat stage.

Added chainAutomatically added handlersDescription
checkAction-TODO
action-contains the action algorithm(s)
failAction-traversed if no action succeeded
succeedAction-traversed if an action succeeded

Shorthand activation: Entity:executeAction()

Attackable

This decorator enables the entity to take normal hits.

Added chainAutomatically added handlersDescription
defencesetAttackRes resistSource armorThis chain is traversed when your entity is about to take damage. These methods mild or amplify effects of the attack.
beHittakeHit, dieHandlers of this chain are traversed after a hit has been assured to come through by the defence chain.
attackableness

Attack sources

Each attack effect specifies a source. Any entity can do damage from only one source. The source is a string.

Every entity decorated with DynamicStats has resistance to particular attack sources. The resistSource() handler checks if the level of resistance to the particular attack source is high enough (equal or more than the resistance level) to stop the attack. If it is, the event propagation is stopped.

There is also the pierce stat, which applies to all attack sources. This stat is checked in the armor() handler. If the level of this stat on the attack effect is less or equal to the pierce protection level, the damage is set to 0.

The armor reduces the damage up to point of minDamage.

Actually being hit

takeHit does damage to you (without applying status effects and pushing, see Pushable and Statused for that).

Entity:takeHit() is the shorthand for Entity.decorators.WithHP:activate()

die checks if the health is 0 and calls the Entity:die() if it is.

Entity:die() is the shorthand for Entity.decorators.Killable:activate()

Shorthand activation: Entity:beAttacked(action)

Also has a function, Attackable.getAttackableness(actor, attacker), which traverses the attackableness chain and return the Attackableness of this entity, which can be NO, YES, IF_CLOSE or SKIP (think ghosts). Default return value: Attackableness.YES.

This function also has a shorthand activation, Entity:getAttackableness(attacker), which returns Attackableness.NO if the entity has not been decorated with Attackable.

Attacking

This decorator enables the entity to do normal hits.

Added chainAutomatically added handlersDescription
getAttacksetBase, getTargetsUsed for creating the Attack object and modifying it with e.g. more damage
attackapplyAttack, applyPush, applyStatusUsed for doing the Attack and applying push and the related status effects

You may pass the list of entities you want to attack as a parameter to the activation. This would skip the process of getting targets.

Shorthand activation: Entity:executeAttack(action)

The do Interactors

These are loosely speaking methods that affect one or more entities in the world. The ones we are concerned with here are the do<Something> interactors. do<Something> methods refer to applying the action on a previously unknown object. For example, the doAttack interactor calculates which entity is going to be targeted and calls beAttacked on those entities.

The apply<Something> handlers added by this decorator just call these interactors.

Interacting

Adds the ability to interact with objects decorated with Interactable.

Added chainAutomatically added handlersDescription
checkInteractgetTarget, checkIsInteractable
interactinteract

Interactable

Added chainAutomatically added handlersDescription
checkInteracted-
beInteracted-

Inventory

See items.

Diggable

Enables the entity to be dug. The wall would take damage on dig equal to dig damage.

Added chainAutomatically added handlersDescription
checkDigcheckPower
beDugtakeDigDamage, die

Digging

This decorator enables the entity to dig.

Added chainAutomatically added handlersDescription
getDigsetBase, getTargets
digapplyDig

Currently, the logic by which getTargets works is very similar to that of normal attacking. Thus, these might be merged in some way in the future. This would mean shovels will be of a subclass of Weapon.

DynamicStats

This decorator adds the possibility to easily initialize with fallbacks to default values, retrieve and modify any stats of your entity.

This decorator doesn't add any chains, but has a number of useful methods. See Dynamic Stats.

Killable

Added chainAutomatically added handlersDescription
checkDie-
diediesets entity.dead to true and calls world:removeDead()

Shorthand activation: Entity:die()

Moving

Enables entity to displace.

Added chainAutomatically added handlersDescription
getMovegetBaseMove
movedisplace

Shorthand activation: Entity:executeMove(action)

PlayerControl

Converts direction to an action for the player

Shorthand activation: Player:generateAction(), just for players

Pushable

Enables the entity to be pushed

Added chainAutomatically added handlersDescription
checkPushcheckPush
executePushexecutePush

Shorthand activation: Entity:bePushed(action)

Sequential

Enables the entity to calculate their next action. Uses a Sequence object to keep track of the current step.

The Sequence is instantiated and set on the entity as Entity.sequence.

Shorthand activation: Entity:calculateAction()

Statused

Makes the entity vulnerable to status effects. Status effects are being frozen, stunned, on fire, poisoned and so on.

Added chainAutomatically added handlersDescription
statusstatusapplies statuses
Modified chainAutomatically added handlersDescription
ticktickdecreases all statuses
checkActionfreecalls free() on statuses

You may optionally pass a configuration parameter to the activation. See status effects.

Shorthand activation: Entity:beStatused(action)

Ticking

Allows the entity to reset some fields at the tick phase.

Added chainAutomatically added handlersDescription
tick-

This:

actor.didAction = false
actor.doingAction = false
actor.nextAction = nil
actor.enclosingEvent = nil

is done directly by world.

Shorthand activation: Entity:tick()

WithHP

Adds an hp object to the player. Makes them takeDamage on activation.

Shorthand activation: Entity:takeDamage(damage)

Copying decorators

copyChains(from, to)

Subclasses of subclasses of Entity can use this function to reapply all previously applied decorators and retouchers of the superclass.

local MyEntitySubclass = require 'wherever.your.subclass.is'
local MySubclass = class('MySubclass', MyEntitySubclass)
copyChains(MyEntitySubclass, MySubclass)
-- now you can apply other decorators without affecting the superclass
decorate(MySubclass, Decorators.Whatever)

Entity.reapplyDecorators(from, to)

This function works the same way but ignores the retouchers.

← Game LoopAction →
  • Overview
    • Method name conventions
    • How to use the decorators
    • What do decorators do?
  • List of basic decorators
    • Start
    • Acting
    • Attackable
    • Attacking
    • Interacting
    • Interactable
    • Inventory
    • Diggable
    • Digging
    • DynamicStats
    • Killable
    • Moving
    • PlayerControl
    • Pushable
    • Sequential
    • Statused
    • Ticking
    • WithHP
  • Copying decorators
    • copyChains(from, to)
    • Entity.reapplyDecorators(from, to)
Dungeon Hopper
Docs
Getting Started
Community
User ShowcaseStack OverflowProject ChatTwitter
More
BlogGitHub RepoStar
Facebook Open Source
Copyright © 2020 Your Name or Your Company Name