5e D&D game mechanics library

5e.dg (7.0 KB) 5e Library, Version 0.1.
SignedMath.dg (6.8 KB) Signed Math Library, Version 0.3.

I am trying to make a library for emulating some 5th edition Dungeons and Dragons game mechanics in Dialog games. The first thing I have coded is ability and skill checks, optionally with (dis)advantage. Because ability score modifiers can be negative I use my Signed Math Library. Here is a simple test:

The hero is level 5, and she has stats (STR = 10, DEX = 10, CON = 10, WIS = 10,
CHA = 10), proficiency in [athletics arcana] and expertise in [acrobatics].

Making an acrobatics check with advantage: [+ 22]

Making an intimidation check with strength as the ability score: [+ 19]

Here is the test code:

#dungeon
(room *)

#hero
(name *)
        hero
(female *)
(singleton *)
(* is #in #dungeon)
(* is level 5)
(* has @strength score 10)
(* has @dexterity score 10)
(* has @constitution score 10)
(* has @intelligence score 10)
(* has @wisdom score 10)
(* has @charisma score 10)
(* has proficiency in @athletics)
(* has proficiency in @arcana)
(* has expertise in @acrobatics)

(intro)
        (banner)
        (#hero is level $Level)
        (#hero has @strength score $Str)
        (#hero has @dexterity score $Dex)
        (#hero has @constitution score $Con)
        (#hero has @intelligence score $Int)
        (#hero has @wisdom score $Wis)
        (#hero has @charisma score $Cha)
        (The #hero) (is #hero) level $Level ,
        and (it #hero) (has #hero) stats 
        \( STR = $Str , DEX = $Dex , CON = $Con ,
        WIS = $Wis , CHA = $Cha \),
        proficiency in
        (collect $C)
                *(#hero has proficiency in $C)
        (into $Proficiencies)
        $Proficiencies and expertise in
        (collect $C)
                *(#hero has expertise in $C)
        (into $Expertises)
        $Expertises .
        (par)
        Making an acrobatics check with advantage:
        (check skill @acrobatics with @advantage for #hero into $Check1)
        $Check1
        (par)
        Making an intimidation check with strength as the ability score:
        (check skill @intimidation using @strength for #hero into $Check2)
        $Check2

My next plan is to tie in perception and stealth checks to the Dialog world model, allowing you to try to hide from monsters and monsters to try to hide from you. I’ll want to add tool checks for thieves’ tools lockpicking, and then move on to character hit points and armor class and such. Ultimately I’d like to have simplified combat, traps and spellcasting, though that’s clearly a bit ambitious.

One thing that I found I can’t do according to the 5e Systems Resource Document is experience based leveling: Dialog doesn’t support numbers large enough. I’m not bothered by this because I prefer milestone leveling anyway, if experience based leveling is needed you’ll need to make a Dialog Large Numbers Library, I suppose.

11 Likes

5e.dg (12.0 KB) 5e Library, Version 0.2.
SignedMath.dg (6.8 KB) Signed Math Library, Version 0.4.

I think I finally have a prototype of hiding and perceiving general to player, NPCs and traps, taking hiding things you don’t see out of scope. First the annotated transcript showing it works (with limitations: first, currently characters that move out of the room and then back in don’t lose their watching hidden list; second, I don’t yet handle the player or NPCs not being able to see in the dark):

* An active perception check happens after every [look]; [look] is called by the standard intro.
You make an active perception check:
[[+ 1] = [+ 1] [+ 0] [+ 0] : #wisdom #perception check].

* Waiting around idly doesn't perform an active perception at the moment, maybe it should?
* Here each character can see themselves, the kobold can see the player because the player has the same visibility ceiling as the kobold and the player isn't hiding, but the player can't see the kobold because the kobold is hiding and the player hasn't made a good enough perception check.
> wait
A moment slips away.
You can see yourself!
The skulking kobold (hidden up to perception 12) can see yourself!
You is oblivious to the presence of the skulking kobold (hidden up to perception 12)!
The skulking kobold (hidden up to perception 12) can see the skulking kobold (hidden up to perception 12)!

> look
Dungeon
You are here.
*Here we have made an active perception check good enough to see the hiding kobold.
You make an active perception check: [[+ 13] = [+ 13] [+ 0] [+ 0] : #wisdom #perception check].
You can see yourself!
The skulking kobold (hidden up to perception 12) can see yourself!
You can see the skulking kobold (hidden up to perception 12)!
The skulking kobold (hidden up to perception 12) can see the skulking kobold (hidden up to perception 12)!

> look
Dungeon
You are here.
*Because the kobold was spotted earlier, even though the active perception and passive perception of the player is not greater than the kobold's stealth, the watching player keeps seeing the kobold.
You make an active perception check: [[+ 6] = [+ 6] [+ 0] [+ 0] : #wisdom #perception check].
You can see yourself!
The skulking kobold (hidden up to perception 12) can see yourself!
You can see the skulking kobold (hidden up to perception 12)!
The skulking kobold (hidden up to perception 12) can see the skulking kobold (hidden up to perception 12)!

Here is the implementation (sans dice related predicates):

(late on every tick)
	(exhaust)
	{
		*(animate $Character)
		(now) ~($Character made active perception check $)
	}

(after [look])
	(current player $Player)
	($Player check skill #perception into $ActivePerception)
	(now) ($Player made active perception check $ActivePerception)
	(line) You make an active perception check: $ActivePerception. (line)

($Object can be seen by $Viewers out of $Candidates)
	*(room $Room)
	($Object has ancestor $Room)
	(visibility ceiling of $Object is $Ceil)
	(collect $C)
		*($C has ancestor $Room)
		(animate $C)
		(visibility ceiling of $C is $Ceil)
	(into $Candidates)
	(if) ($Object is hidden up to perception $DC) (then)
		(collect $C)
			*($C is one of $Candidates)
			{
				($C = $Object)
			}
			(or)
			{
				($C is watching hidden $Objects)
				($Object is one of $Objects)
			}
			(or)
			{
				($C check passive #perception into [$PassivePerception | $])
				(if) ($C made active perception check [$ActivePerception | $]) (then)
					(signed max [$ActivePerception $PassivePerception] into $Perception)
				(else)
					($Perception = $PassivePerception)
				(endif)
				(signed $Perception > $DC)
				(if) ($C is watching hidden $Objects) (then)
					(if) ~($Object is one of $Objects) (then)
						($NewObjects = [$Object | $Objects])
						(now) ($C is watching hidden $NewObjects)
					(endif)
				(else)
					(now) ($C is watching hidden [$Object])
				(endif)
			}
		(into $Viewers)
	(else)
		($Viewers = $Candidates)
	(endif)

~($Object is in scope)
	(current player $Player)
	($Object can be seen by $Viewers out of $)
	~($Player is one of $Viewers)

And here is the testbed:

#dungeon
(name *)
	Dungeon
(room *)
(singleton *)

#hero
(current player *)
(name *)
	hero
(female *)
(singleton *)
(* is #in #dungeon)
(* is level 5)
(* has #strength score 10)
(* has #dexterity score 10)
(* has #constitution score 10)
(* has #intelligence score 10)
(* has #wisdom score 10)
(* has #charisma score 10)
(* has proficiency in #athletics)
(* has proficiency in #arcana)
(* has expertise in #acrobatics)
(on every tick)
	(* can be seen by $Viewers out of $Candidates)
	(exhaust)
	{
		*($Candidate is one of $Candidates)
		(if) ($Candidate is one of $Viewers) (then)
			(line) (The $Candidate) can see (the *)! (line)
		(else)
			(line) (The $Candidate) (is *) oblivious to the presence of (the *)! (line)
		(endif)	
	}

#skulking-kobold
(name *)
	skulking kobold \(hidden up to perception 12\)
(animate *)
(* is #in #dungeon)
(* is level 1)
(* has #strength score 10)
(* has #dexterity score 10)
(* has #constitution score 10)
(* has #intelligence score 10)
(* has #wisdom score 10)
(* has #charisma score 10)
(* is hidden up to perception 12)
(on every tick)
	(* can be seen by $Viewers out of $Candidates)
	(exhaust)
	{
		*($Candidate is one of $Candidates)
		(if) ($Candidate is one of $Viewers) (then)
			(line) (The $Candidate) can see (the *)! (line)
		(else)
			(line) (The $Candidate) (is *) oblivious to the presence of (the *)! (line)
		(endif)	
	}

Feedback is very welcome!

1 Like

EDIT: Characters now need light or ($ has darkvision) to be considered viewers of an object, and leaving a room has you lose track of animates and items, but not objects that are fixed in place.

5e.dg (13.4 KB) 5e Library, Version 0.3.
SignedMath.dg (6.8 KB) Signed Math Library, Version 0.4.

The final small task for stealth is going to be adding a (perform [hide]) action, and (let $NPC hide) and (let $NPC look) NPC behaviors. I’m also thinking of adding (perform [point out $Object]) and (let $NPC point out $Object) so the player and allied NPCs can point out hiding characters they are watching or hidden traps and secret doors they know the location of.

1 Like

You and NPCs can hide now.

I generalized perception of mobile and fixed things, so that the player and NPCs remember where they last saw people and things and succeed automatically at seeing them if they are in that location even if they are hidden. I had to add one exception: when you are hidden and move into a room, others that last saw your location as [$Player #in $CurrentRoom] forget that and don’t spot you, so you can at least attempt to sneak back into a room you were in previously. I’m not completely happy with this exception but I haven’t come up with a better approach. Maybe I could allow something like >sneak north to behind the curtains and that moves you directly to #behind #curtains in the room to the north? Would that be too fiddly?

5e.dg (14.6 KB) 5e Library, Version 0.4.
SignedMath.dg (6.8 KB) Signed Math Library, Version 0.4.

2 Likes

This looks really interesting. I’m curious how difficult it would be to port this to TADS, which has better mathematical capabilities built into the system library.

1 Like

I don’t know TADS but it probably would be straightforward, the most complex thing so far is dealing with who can see what, the rest is just simple random numbers, basic arithmetic, and manipulation of state.