Basic Roleplaying system in Inform

After a bit of off-and-on work, I’ve got what I’d call the MVP of an implementation of Chaosium’s ORC-licensed Basic Roleplaying SRD in Inform 7 as an extension, including a tiny demo scene (which I’ve also published in the repo as a gblorb if you want to give it a play without going through the I7 editor) making use of skills and characteristics in various ways: GitHub - maxsond/i7-BRP · GitHub It was fun to play with and see how interactions between the various possibilities emerged the deeper I got into fleshing out the demo.

So far, this implements most of sections 1-4 of the SRD (so no combat yet, but most IF doesn’t lean much on combat anyway). Notable exceptions are character creation, professions, and skills with a base value based on characteristics, although it does support games adding their own skills and assigning skill values to characters and granting skill experience, all of which the demo demonstrates.

The demo works around the lack of character creation by giving the player pills they can eat to increase their skills and stats as they wish to attempt their chosen path to victory, and this also serves as an example of the experience roll implementation.

One particular part I’d like feedback on is that as it currently stands, if you want to manipulate a skill or a characteristic value for a character, you must author its initial value for that character (because these are tracked in tables, and we can’t add rows at runtime or dynamically compute the necessary number of rows at compile time as far as I know). Failing to do so will generate a big obvious error message when the attempt to manipulate the value occurs. I could see this possibly being annoying when authoring a larger-scale game. Initially I had intended to automatically generate all pairings which would remove this bit of manual bookkeeping but would probably also result in a majority of the data being irrelevant for any particular game. So maybe it’s actually good as it is?

Another is that because I’m aiming to keep the implementation close to the SRD I’ve adopted its semantic version number for the version of the SRD I’m implementing (which is 1.0.2), but I also need a separate semantic version for the implementation. Since Inform doesn’t support version levels beyond the debug level, I just extended the debug level’s digits to also encompass a 5-digit semantic version for the implementation (e.g. 1.0.200001 for the 0.00.01 implementation). This feels dirty and is probably not compliant with the “Friends of i7” repo. It’s entirely plausible that there could be an implementation API break that has nothing to do with the SRD, and this approach can’t handle that. I’m very open to hearing a better approach to this. There’s probably something easy that’s just evading me.

Anyway, I hope y’all at least get a little enjoyment from the tiny demo even if you’re not looking to add a TTRPG system to your interactive fiction. :smiley: And if you are, I’d be interested to hear how it works for you!

8 Likes

I haven’t had a chance to look at this yet, but a technical side note: Inform does have a way to create a table with a blank row for every (kind of your choice) in the game, though it’s very rarely used.

Congratulations on what you have so far! TTRPG-style implementation is one of those things that pops up from time to time here and it seems like most people who try it eventually give up or at least settle on a less ambitious implementation than the one they originally intended. (Although, based on certain posts, I’m pretty sure there is at least one other one in active development.) I myself have been working on such a project on and off for quite some time.

I see that you’ve come to the same conclusion that I did a while back – using simple properties to describe ability scores (or characteristics in your system) doesn’t really cut it. Everyone’s first instinct is to go “A person has a number called strength,” etc. Then, trying to write code to check these different properties results in code duplication. It would be easier if we could write phrases with “property” or even “arithmetic value valued property” as parameters, but there’s a bug in the current version of Inform that won’t let us do that. (It seem to have been fixed for the next release, see: https://inform7.atlassian.net/browse/I7-2437)

The obvious solution is to use kinds of value – as you’ve done – which is neater anyway. You can easily repeat through them, print their names, etc. But then the question becomes, “how do I pair each one with the right score?” Your use of tables could work for your purposes; as @Draconis points out, one can create tables with blank rows and then fill them in.

My original solution was to use pairs of lists (one of characteristics and one of numbers). Lists are nice because we don’t have to worry about predetermining their lengths. I wrote helper phrases that kept the lists in sync, so if you wanted to change a certain characteristic’s score, the code would find which entry that score was in list A and change the corresponding entry in list B. I lost all that code a while back, but you could probably put something like that together pretty easily yourself.

However, what I’m doing now is much better for my purposes and more extensible. I’m using Dannii Willis’ “Collections” extension to create “maps” for these (and other) sets of paired values. The extension’s documentation is a little sparse and seems to presume some basic understanding of “collection references” (which I didn’t have), but once you start messing with it, the power is amazing.

In my implementation, each character has a map, which has keys that are abilities pointing to a number which is the score. It makes more sense if you see a very basic implementation:

"Scratch Pad" by Michael Tarbert
	
Include Collections by Dannii Willis.

Lab is a room. Sam is a man in the Lab.

An ability is a kind of value. 
Strongness, Nimbility, Hardiness, Smarts, Savy, and Looks are abilities.

A person has a collection reference called ability map.

To set --/a/the (AB - an ability) score of --/a/the (P - a person) to (N - a number):
	let M be the ability map of P;
	unless kind of the ability map of P is map:
		now the ability map of P is a new map;
	set key AB of the ability map of P to N.
	
To assign (AS - a list of numbers) to --/a/the ability scores of --/a/the (P - a person):
	if the number of entries in AS is not six:
		say "Warning: attempt to apply invalid ability score list to [P] ignored.";
		stop;
	let n be 1;
	repeat with ab running through abilities:
		let score be entry n in AS;
		set ab score of P to score;
		increment n.

To decide what number is --/a/the (AB - an ability) score of --/a/the (P - a person):
	let M be the ability map of P;
	if kind of M is map:
		if M has key AB:
			let score be M => AB as a number;
			decide on score;
		otherwise:
			say "(No value for [AB] found.) ";
	otherwise:
		say "(No ability scores found.) ";
	decide on 0.
	
Instead of examining a person:
	say scores of the noun.
	
To say scores of (P - a person):
	say "[if P is the player]Your[else][P]'s[end if] scores:[line break]";
	repeat with AB running through abilities:
		say "[AB]: [AB score of P].".
		
Instead of jumping:
	repeat with AB running through abilities:
		set AB score of the player to a random number from 3 to 18;
	say scores of the player.		
		
When play begins:
	assign {12, 13, 15, 14, 9, 18} to the ability scores of the player;
	assign {10, 7, 12, 13, 4, 6} to the ability scores of Sam.
	
test me with "x me / x sam / jump / g".

Maps are really useful, especially anywhere you need pairs of values of different kinds. For example, instead of just giving characters a class and a level number properties, I’m using a map so I can handle multiclass characters.

Anyway, sorry for rambling. I don’t know if any of that could help you, but sometimes it helps me to see what other people have tried. Even if you don’t use it for this, you should try “Collections.” It’s awesome! (Thanks to @Dannii )

6 Likes

Haha, I did indeed go through that “wait, these can’t really just be numbers” journey myself. :sweat_smile: Once I landed on values and tables, the rest was fairly straightforward (although there’s still some code duplication - it’d be neat if I could merge the skill and characteristic roll code together because the mechanics are mostly identical, but one uses the new skill values and the other uses the characteristic values. Perhaps at a certain point I could have some shared code that operates on raw numbers but the “public API” if you will begins from the skill/characteristic values).

That map thing looks pretty neat, I’ll have to give it a spin. Also I wasn’t aware of the --/a/the thing. I might swipe that, too.

1 Like

Inform does have a way to create a table with a blank row for every (kind of your choice) in the game, though it’s very rarely used.

Hm, I might look into that. The number of rows in this case would be (number of persons) x (number of skills), but maybe it’d still work. But that Collections extension looks pretty interesting, too.

As someone who loves tabletop RPG’s of all sorts, I hope this becomes more fleshed out, with combat and the like. I wonder how hard it would be to rewrite Inform ATTACK to use this BRP system as its base, instead of using the D20-derived rules that were intended for Kerkerkruip. Also, the thought of using this for lovecraftian horror games (E.G. the King of Shreds and Patches, Anchorhead) is almost irresistable.

1 Like

I’m definitely willing to look into adapting ATTACK after the SRD core is done. Using a flow that people interested in combat in their parser IF are probably already familiar with would be a boon for adoption, I think.