Does anyone know how to implement a command line type user interface using twine?

Hi all.

I have been using twine and SugarCube for a little while now and I really like how slick it is to craft a text-based adventure game.
One burning question I have before I write another level is - Does anyone know or have any experience in creating a game using twine that simulates a more command-line user interface for a text-adventure game? Similar to that say from the Zork games of the 80’s?
I think it would be really interesting for players to type their requests in a command line type interface as opposed to clicking on links to action/action further passages.

Thanks in advance.

Like a parser?
It’s been done before. I’ve even done one for the SpringThing this year (source code). Also see Post-Mortem for other examples.
It is doable, but not really recommended. SugarCube (and Twine in general) wasn’t really built for parser/input games. You’ll need to create your own library of verbs/nouns and combo for interaction and everything that comes pre-packaged in parser programs. It’s honestly easier to do with Adventuron/Inform/TADS than Twine…

3 Likes

It’d be interesting. People have wanted this for a long time. If you want a parser interface, it’s probably best to use a legit parser system like Inform or TADS and incorporate occasional choices with an extension that does this, such as Hybrid Choices for Inform 7. This was the strategy I used in Fair and Steph Cherrywell’s Inform games use it for dialog menus.

With Twine, most flavors allow you to accept text input using a macro and store that string as a variable. It’s good for singular variable input like “what is your name?” But then the author is responsible if they want to somehow take that string and try to match what was typed against a list of responses. Since there’s no automatic handling like in parser, you’d come up with situations where the player might type the expected OPEN DOOR but they might also type CHECK THE DOOR TO SEE IF IT’S LOCKED which requires advanced level splicing of commands to see if anything in that command is a keyword that makes sense and has a logical response. (IE manually doing the work of “a parser”)

I’ve seen Twine games that can work if this is limited story wise and technologically - say a robot full of lore that requires you type ONE WORD to look up what are essentially phrase matching dictionary entries. Again you get the problem of predicting how the player will phrase these keywords so you have to predict they will type stuff like CHICKEN/CHICKEN DINNER/MEAL/DINNER/FRIED CHICKEN/BAKED CHICKEN … etc. Which is the type of thing a parser is good at doing.

People have always wanted a hybrid parser/choice system and there’s never been one that functions on the level of Inform or Twine singularly. The closest might be GrueScript which functions as a clickable two-word parser interface with links but the player can also type. I also want to say Quest allows parser or choice games, but I’m not sure how simultaneous that is - their parser interface does involve clicking but it’s “click the name of the thing in your inventory” which will drop down useful verbs “examine/take/open” that you’d normally type. I think if you choose at the beginning to make a choice narrative the parser doesn’t come into play at all.

I am not a TADS user, so it might have functionality I’m unaware of.

There’s also Dialog which is a completely different parser system that leans heavily into allowing clickable links and “play with mouse only” but requires a lot of Author-customization to make sure this happens correctly.

2 Likes

TWO WORDS as with ScottKit works, too.

Adventuron does that imo. It’s a bit finicky to make the choices thing, but it works (see The Familiar).

1 Like

I have a Sugarcube-based parser that I have used in a few games. They are Spanish-only at the moment. However, I translated the prototype into English, and you can try it here.

How it’s done?

Captura de pantalla 2023-10-15 a las 20.10.48

  1. There’s a Javascript object that parses the command matching the 1st token, and returns the passage name with the implementation of the verb (object matching would work likewise: parse 2nd token + find object).
Javascript helper
var parser = {
	"actions": [
		{"id": "ACT_COGER", "vbs": ["get","take"]},
		{"id": "ACT_IR", "vbs": ["n","s","e","w",
		 	"up","down","in", "out"]},
		{"id": "ACT_MIRAR", "vbs": ["l","look","describe"]},
		{"id": "ACT_EXAMINAR", "vbs": ["x","ex","examine"]},
		{"id": "ACT_INVENTARIO", "vbs": ["i","inv","inventory"]},
		{"id": "ACT_SALIR", "vbs": ["quit","exit","end"]},		
		],
	"parse": function (cmd){
		var tokens = cmd.toLowerCase().match(/\S+/gi);
		if (tokens == null) {return;}
		for (var i=0;i<this.actions.length;i++){
			if (this.actions[i].vbs.indexOf(tokens[0])>=0){
				var ret = {};
				ret.action = this.actions[i];
				ret.args = tokens.slice(1);
				return ret;
			}
		}
	}
};
  1. The main UI is a passage printing the game log and the prompt. Notice that entering a command takes the player to the Parser passage.
'Input' passage
$log &gt; <<textbox "$player_input" "" "Parser" autofocus>>
  1. The Parser passage takes the input, passes it to the function and moves the player to the parsed action (or to the error message). Notice that the 1st line echoes the command to the log, so we get a nice transcript.
'Parser' passage
<<set $log = $log + "&gt; " + $player_input + "\n" >>
<<set $command = parser.parse($player_input) >>
<<if $command>>
	<<goto $command.action.id>>
<<else>>
	<<goto ACT_ERROR>>
<</if>>
  1. Action passages work by adding text to the game log (large string stored in $log), and returning the player back to Input.
ACT_COGER passage (take action)
<<set $log = $log + "Taken (not really).\n" >>
<<goto Input>>

And… this is it, really. Execution model is:

InputParser → ‘ACT_SOMETHING’ → Back to Input

with the ACT_SOMETHING passages appending text to the game log, and changing the state of the game world as needed (moving objects from locations to inventory, changing the player location, etc).

Typically I keep track of the player location, inventory and so on into a object variable called $state.

For different games I have implemented a map, directions, inventory, ‘soft’ save/restore, log pruning… Save for the small piece of JS, is all implemented with macros, so the actions have access to the story variables and the game state is properly serialized on save and restore.

If you can handle some Spanish text, my game whoami has a short parser subsection with proper inventory, map (with no directions, just a universal GO TO ) and interaction with an NPC.

Edit: A couple of pro-tips:

  • disable navigation to prevent the text logs cluttering your memory (set maxStates to 1), and
  • add a parser tag in all the parser nodes and use it a to customize the style, and disable the fade in-out effect for these nodes.
4 Likes

I feel like using TADS here would be using a rocket ship just because it had a cup holder. If you want a Twine view of the world, you’ll likely be ignoring much of TADS’ system, just for its parser. Then again you’ll have to patch around/implement a lot of things so the parser doesn’t go astray.

I feel like if it’s memetically nice to type in commands, I would stick with Twine choices, but do a bunch of CSS styling and maybe use special effects to pretend that it’s being typed in when it goes to the relevant passage.

Parsers are a pain. You only want them if they are achieve something larger than what you can hope to achieve otherwise, like a simulated environment or flexibility in actions.

For my IF Comp game I thought a lot about replicating Twine in TADS 3 or vice versa. In the end I split them into the respective systems just to focus on their strengths.

2 Likes

It is not simultaneous. You choose one or the other when you start writing, and the gamebook mode has no parser or world model.

1 Like

This! My use case was having pockets of exploratory, open ended sections in choice games. This way you can’t simply backtrack or comb the links to solve them, as the space of possibilities is not explicit.

1 Like