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

Weapons

Overview

Weapons offer a way of incorporating patterns into normal attacks.

They achieve any behavior you want using these 2 (3) things:

  1. The pattern. Specifies kind of the form of the attack;
  2. The check function and the (algorithm) chain. Describes the algorithm of filtering the targets acquired by means of pattern;

Pattern

Pattern contains relative to the weapon holder coordinates and some other attributes. All of these are wrapped in Piece objects. The Piece attributes are as follows:

AttributeTypeDescription
posVecCoordinate relative to the attacker
dirVecKnockback direction
reachBool or an array of indicesWhether to keep the target only if all previous attacks (if given a table of indices, check those indices) were not blocked by e.g. a wall

The default orientation, against which the pieces are rotated, is Vec(1, 0) (straight to the right), so build all your patterns as though your character looks to the right.

For example, consider the spear attack pattern:

local pattern = Pattern()
pattern:add( Vec(1, 0), Vec(1, 0), false ) -- { pos = (1, 0), dir = (1, 0), reach = false }
pattern:add( Vec(2, 0), Vec(1, 0), true  ) -- { pos = (2, 0), dir = (1, 0), reach = true  }

This represents the following attack pattern. Note that in the case when the first spot is blocked, the second is not targeted, since reach of the second piece is true.

Spear attack pattern

For a more specific behavior of reach, that is, checking attacks at specific indices, just provide a list of indices instead of true in the pattern definition, like so:

local pattern = Pattern()
pattern:add( Vec(1, 0),  Vec(1, 0),  false )
pattern:add( Vec(1, 1),  Vec(0, 1),  false )
pattern:add( Vec(1, -1), Vec(0, -1), false )
pattern:add( Vec(1, 2),  Vec(0, 1),  { 2 } )
pattern:add( Vec(1, -2), Vec(0, -1), { 3 } )

This happens to be the pattern of a whip in the original Necrodancer.

In case you are using the general weapon (algorithm) chain, the pieces of your pattern will be tested in order and only the first match will make it into the targets list.

The chains

The ability to define custom weapon chains gives you precise control over the algorithm of your specific weapon. For most weapons, though, the general chain and the hit all chain will cover the needs.

These custom chains should be set as Weapon.chain for the algorithm chain and Weapon.check for the check function.

The general (algorithm) chain

The steps that the general (algorithm) chain follows are the following:

  1. Filter out targets without entities;
  2. Check if hitting only those entities that are attackable only when you are next to them (e.g. crates) without being next to any (return an empty list in this case);
  3. Check unreachableness (eliminate unreachable ones), which is what Piece.reach is responsible for;
  4. Filter out the targets with Attackableness.NO;
  5. Eliminate targets with Attackableness.IS_CLOSE that aren't close, that is, prioritize normal enemies over e.g. crates. TODO: Targets with Attackablesness.SKIP are not taken into account;
  6. Take the first target in the list, unless it has Attackableness.SKIP. In this case, take the first available after it.

The general check function stops propagation if event.propagate is false or if the list of targets is empty.

Hitting all targets

There is another standart chain you can use, called hitAll. Its steps are as follows:

  1. Filter out targets without entities;
  2. Check if hitting only those entities that are attackable only when you are next to them (e.g. crates) without being next to any (return nothing in this case);
  3. Filter the list, leaving only Attackable entities.

Be careful as this algorithm ignores reach in the pattern.

Now I'm actually realizing that this is not how it should work. For example, the hammer from the original game does not do the attack if there is an unattackable block in front of the character, while in my code as it is it would.

It also provides a check function, which is actually just Chain.checkPropagate.

Weapon:getTargets(actor, action)

This function returns the list of targets for the current actor and action. Targets are objects of the form:

AttributeTypeDescription
entityEntityThe entity targeted by the attack
piecePieceThe rotated piece object from the pattern
indexNumberWhich step in the pattern this piece belongs
attackablenessAttackableness: NO, YES, IF_CLOSE, SKIPreturned by Entity:getAttackableness(attacker, action) on the targeted entity

In case no targets have been found, it returns an empty list.

Creating a weapon

Weapon is a subclass of Item. Here is all the code needed to create a spear:

local Weapon = require '@items.weapons.weapon'
local Pattern = require '@items.weapons.pattern'

local pattern = Pattern()
pattern:add( Vec(1, 0), Vec(1, 0), false )
pattern:add( Vec(2, 0), Vec(1, 0), true  )

 -- the constructor is the same as that of Item
 -- so you may optionally pass a tinker here
local spear = Weapon() -- Weapon(myTinker)

-- set the pattern
spear.pattern = pattern

-- if you have custom chains, or a custom check
-- just set them here
-- spear.chain = myChain
-- spear.check = myCheck

-- if you are willing to use the `hitAll` chain
local hitAll = require '@items.weapons.chains.hitall'
spear.chain = hitAll.chain
spear.check = hitAll.check
← ItemsPools →
  • Overview
  • Pattern
  • The chains
    • The general (algorithm) chain
    • Hitting all targets
  • Weapon:getTargets(actor, action)
  • Creating a weapon
Dungeon Hopper
Docs
Getting Started
Community
User ShowcaseStack OverflowProject ChatTwitter
More
BlogGitHub RepoStar
Facebook Open Source
Copyright © 2020 Your Name or Your Company Name