< games

Critmas 2020 December 25, 2020

2020 was the year that we all learned to play D&D over the Internet. Here is a sketch of my awesome players.

IonCreation January 01, 1992

A platformer game written using Microsoft QuickBasic 4.5 for MS-DOS. Play it in your browser!

No More Initiative

Screenshot of extension in use

Dungeons and Dragons uses a system called initiative to determine turn order. Players roll dice at the start of combat to determine the turn order, after which it is fixed. This extension lets you run Fantasy Grounds and Fantasy Grounds Unity games with no initiative.

Download (23kb)

The extension creates a note that the DM can optionally share by checking the "Public" option. When die rolls are made, they are added to the list, which is kept sorted. It also displays whether the roll was sucessful or not for 5E.

Place it in your FG or FGU data/extensions folder.

How do you use it?

  • The DM announces a new round.
  • Players roll their attacks whenever they are ready but do not roll damage.
  • DM announces that the attack phase is complete and moves on to the resolution phase
  • DM reads down the list in the tracker, narrating what happens highest die roll to lowest
  • As the DM reads down the list, they ask the player in question to roll damage

That's it! *

* there is one issue, which is that you still need to forward through the turns to trigger any "at the start of your turn" type events.

Wait... what?

No initiative? What? Are you crazy?

No initiative might sound crazy, but my players straight out prefer it. Try it out, you might like it. The idea is that when you roll dice it actually includes an intrinsic sense of initiave. I originally came across this idea from this video:

YouTube thumbnail

More details on No Initiative

There are several edge cases that can arise from a no initiative system. You can solve them however you want, but here are some ways that I deal with them:

Movement

I allow players to move before they do their action if they want. If a player specifically wants to move after they've done something then they just tell me. Unless there is a good reason not to allow it, I allow it. If there is ever any doubt, I ask them to do a contest check, such as an athletics contest against an enemy that's trying to cut them off. You can also just have them throw a d20 to see if they can get there in time.

Opportunity attacks

If a player runs up to an opponent and the opponent runs away at the same time, no opportunity attack is triggered. If they start the round next to each other, then it is triggered. It should be intuitive when an opportunity attack is appropriate, but if you're running into issues, you can separate the movement and actions into separate phases. Movement first, actions second.

Fallen Targets

If a player's target is taken down before it gets to their action, I allow them to instead strike a similar target. For melee, it needs to be adjacent to them. For ranged it needs to be in the same general vicinity.

If you don't like that, another option is to allow them to roll at disadvantage for a replacement action. The disadvantage represents that they were intending to do something and had to hastily switch.

Why would you do this?

  • It makes combat fast and furious
  • Combat actually becomes more intense and interesting
  • You can get through more play in less time
  • Players aren't bored waiting for their turn
  • Best of all, narrating the scene is better because it all connects

This extension makes it easy to do. At first I was trying to read back through the log and I don't recommend that, too easy to miss actions or get the order wrong.

Technical notes

The extension passes die rolls to the host through out of bounds (OOB) messages. The host then parses them into:

  • nValue: the roll total
  • nHit: whether it hit or missed
  • sSource: the legible name of the actor doing the action
  • sTarget: the legible name of the actor being targeted
  • sActivity: the legible name of the weapon or spell

The host then adds those values to a table, which is sorted by nValue. It uses this table to generate the tracker note with formatting, such as bold and italics.

Spells are interesting because they are done as forced saving throws. In Fantasy Grounds this is achieved using two different actions: first a "powersave" action, which then forces the target to take a "save" action. We need both of these to figure out a spell.

The "powersave" action doesn't have the roll but gives us the sSource field, while the save action tells us the roll but doesn't have the sSource information any more.

Another thing about saves is that they are defensive. Therefore we calculate a proxy value by taking the DC, and adding the DC minus the roll. This anchors the value to the DC number while making a target that rolls low take damage earlier.

nValue = DC + DC - roll

For example, someone that rolls a 2 on a DC 14 has the hit against them recorded as 26:

26 = 14 + 14 - 2

This sytem definitely needs refinement and I'd welcome any input.

Hooks in Fantasy Grounds (and Fantasy Grounds Unity)

I wasn't able to find an appropriate event listener. There is registerResultHandler, which tells us about the roll that was made. However, we're missing the source and target information.

Instead, we intercept the ActionsManager.resolveAction function from the CORE ruleset. This is very easy in lua:

function onInit()
orig_resolveAction = ActionsManager.resolveAction
ActionsManager.resolveAction = my_resolveAction
end
function my_resolveAction(rSource, rTarget, rRoll)
... -- do our logic here, with access to rSource, rTarget, rRoll
orig_resolveAction(rSource, rTarget, rRoll);
end

5E versus CORE

While I wanted this extension to be widely useful, I happen to use it for D&D 5E. I tried to make the 5E ruleset optional, but I haven't tested with an actual non-5E game.

In 5E there is advantage and disadvantage. This means you roll two dice and take either the higher or lower of the two. CORE doesn't understand this, so I had to add extra logic to decode the dice result, adapted from the 5E ruleset code:

function getRollTotal(rRoll)
local bADV = string.match(rRoll.sDesc, "%[ADV%]");
local bDIS = string.match(rRoll.sDesc, "%[DIS%]");
if (bADV and not bDIS) or (bDIS and not bADV) then
if #(rRoll.aDice) > 1 then
local nDecodeDie;
if (bADV and not bDIS) then
nDecodeDie = math.max(rRoll.aDice[1].result, rRoll.aDice[2].result);
else
nDecodeDie = math.min(rRoll.aDice[1].result, rRoll.aDice[2].result);
end
return nDecodeDie + rRoll.nMod;
end
end
return ActionsManager.total(rRoll);
end

Spell casting in 5E is done in reverse. Rather than rolling to see how your spell did, the opponent rolls to see if they saved. To figure out if the spell hit, we need to figure out if they made their roll or not, and reverse it.

function getSaveDetermination(nValue, rRoll)
if getRollTotal(rRoll) >= rRoll.nTarget then
-- actor made their saving throw, therefore action missed
return HIT_MISS;
else
return HIT_HIT;
end
end

possible future enhancements

The biggest enhancement I would make would be to handle the "on your next turn" type events automatically. For example, regeneration is only applied when the actor's turn starts.

Other ideas:

  • User options to control how the tracker is formatted
  • Support for other rulesets
  • 5E: highlight crit and fumble rolls, for extra narrative flavor

My overall thoughts on FGU and lua

Lua is a beautifully simple and clean language. It's amazing that SmiteWorks made so much, both CORE and 5E rulesets, entirely with Lua. Fantasy Grounds is a cool system with the database and OOB delivery systems being straight forward and easy to work with.

However, the OOB message system only supports text. I ended up writing my own trivial JSON codec so that I could transfer the logic to the server. It's possible that I missed a built in way to serialize tables.

Documentation would be nice. Also it would be nice if they ruleset was designed to be more reusable. For example, getting the AC or DC would be nice. A logical flow of action processing would be nice, rather than "powersave" hack for example.

Copyright © 2021