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

Action

Overview

In short, action is the object that statically describes the logic involved in a particular action in the game. For example, and AttackAction would kind of describe how to do an attack.

Note: Action instances are actually not completely static. One may add a direction and effects like attack, push, dig and move onto it.

When an entity decides on an action, it is said to have computed it. When this happens, it sets its entity.nextAction to this computed action. For enemies, this functionality is provided by the Sequential decorator, while for players, it's the PlayerControl decorator.

At the time of computation, the fresh action does not include a direction for enemies. In this case the direction is computed separately by the action algorithm (e.g. GeneralAlgo)

Action Execution

The action execution process is pretty convoluted. Let's break it down into components to clarify how it works.

Prerequisites for becoming an Acting

Acting is a decorator for entities. Entities decorated with Acting have the function executeAction() (which is just a shorthand for entity.decorators.Acting:activation()) actually doing something interesting. If the instance class has not been decorated with Acting, executeAction would just set Acting.didAction to true.

Acting.something in this and subsequent cases will refer to the something field on an entity class decorated with Acting.

The Acting decorator can be applied to both player and non-player entities. Once done, though, you'll still have to provide the algorithm for action, that is, put an Algo handler into the new action chain received from the Acting decorator. Technically, you may add more than one algorithm.

Structure

Before starting on this, some new terminology has to be introduced:

  1. The event at the highest level, set as Acting.enclosingEvent, which is generated by Acting.executeAction() is the enclosingEvent.
  2. The succeeded event nested inside it that is generated by the action algorithm, will be called the algoEvent.
  3. The event produced by the functions Attacking.executeAttack(), Moving.executeMove() and so on will be called resultEvent.
  4. The events that come and go without being saved anywhere are internalEvent's.

Each Acting NonPlayerReal (Acting is a decorator) has a set of fields that reflect the action execution state:

  1. Acting.didAction is set true once the action has been completely executed. The game loop, naturally, ignores them, so that the action is not repeated for many times over (remember, the Acting entities may make others act).
  2. Acting.doingAction is set true once the Acting.executeAction() has been called, and false once exited.

Now we'll examine the final event structure once it comes out of Acting.executeAction() (assume GeneralAlgo). It does not 'come out' as such, the function always returns nothing. The final event is saved as Acting.enclosingEvent. The standart fields of any event, which are event.actor, event.action and event.propagate, exist on each of the event structures examined, so they will be consequently omitted.

This event has a special structure, and consequently will be called EnclosingEvent:

  1. EnclosingEvent.checkSuccess - (boolean) same as event.propagate, except not inherited.
  2. EnclosingEvent.success - (boolean) comes paired with the next field.
  3. EnclosingEvent.algoEvent - The one event that actually occured, with the action and the direction:
    1. algoEvent.success
    2. A nested resultEvent with a wild diversity of more fields depending on the action type. E.g. for an AttackAction, these would be:
      1. resultEvent.attack - the attack object applied
      2. resultEvent.push - the push object applied
      3. resultEvent.status - the status object applied
      4. resultEvent.targets - a list of the Target objects, containing the reals actually hit. This list is formed by the weapon's spec or by taking the cell the actor is facing and getting the real out of it.
      5. resultEvent.attackEvents - list of events generated as a result of reals being attacked
      6. resultEvent.pushEvents - similarly, pushed
      7. resultEvent.statusEvents - similarly, statused
      8. resultEvent.success - whether the check or get (getAttack in this case) chain has been passed entirely through

You can find the one direction that succeeded (assuming GeneralAlgo) in enclosingEvent.action.direction.

The algorithms

Most common algorithms, which act by doing something in a direction, are the GeneralAlgo for enemies and SimpleAlgo for the player and for non-enemy entities.

The difference between these two is that the GeneralAlgo tests out a couple of desirable actions, which it would get from entity.sequence.getMovs() function (this function is set for each step of the sequence individually, see Sequence), gets the most desirable one out of them, and executes that single one, while the SimpleAlgo just does the selected action in the only direction provided (which came from user input).

As a result, these algorithms support just one action of one type at a time. That is, with the GeneralAlgo it is impossible to program an enemy that e.g. would attack to the left, while spitting out a projectile to the right (it is possible, but hacky), which is also true for the SimpleAlgo.

Action chains of both enemies and players typically have a verification stage before deciding on which action to start. These are set on the get (or check) chain from the corresponding decorator. These chains are incorporated in the chainTemplate directly on the entity class. For example, for an attack, the decorator would be Attacking and the chain would be named getAttack. So you would do:

MyEntity.chainTemplate:addHandler('getAttack', myHandler)

There will be plenty of predefined handlers, but these are in fact just normal chain handlers that work with events. You can write ones yourself easily.

The repercussions of this design is that the actions resulting in no avail, e.g. attacking empty space, must be foreseen and prevented manually for both player and non-player entities.

Both of these algorithms also save the action that was relevant and set success to true. See Structure.

An action 'succeeding'

Actions may be multi-step. That is, they may have multiple components to them. For example, there is the AttackMoveAction which indicates first attacking, then moving. So, attacking and moving are both action components in this case.

An action component is considered to have succeeded, if its resultEvent went through every single handler in the action component check (or get) chain. For Attack, this would mean it went through every single handler of the getAttack chain. This is why for checking purposes one should add handlers that would stop the event to the check (or get) chains.

In the case of an action component succeeding, the action as a whole would return, then the resultEvent would be saved on algoEvent and algoEvent.succeed would be set to true. Otherwise, algoEvent.succeed would be false, while resultEvent would be nil.

← DecoratorsAlgorithms →
  • Overview
  • Action Execution
    • Prerequisites for becoming an Acting
    • Structure
    • The algorithms
    • An action 'succeeding'
Dungeon Hopper
Docs
Getting Started
Community
User ShowcaseStack OverflowProject ChatTwitter
More
BlogGitHub RepoStar
Facebook Open Source
Copyright © 2020 Your Name or Your Company Name