In this second blog post I’m showing how I implemented objects - things the player can interact with - for a text adventure written in Rust. As usual, the full code is available on GitHub.
The unend engine defines a trait which anything which the player can interact with (objects at present time; people, animals etc in the future) must implement:
Basically, the interact()
method accepts an interaction as a parameter, and returns a result for that.
Among the “planned for the future” things, there’s method like this:
in order to allow interaction between objects, such as use ink with paper. It would also allow the give interaction (as in give book to Alice) to exist. This is, however, absolutely too advanced for current development status. 😆
Interaction
and InteractionRes
are enums defined as follows:
The first object I wanted to implement was a very simple one, that would just show a text message for each interaction: a description if it’s looked at, a nice “no, thank you” message if the player attempted to take it, etc. So, leaving aside trait instantiation and other boilerplate, all the action happens in this code:
The implementation of Interagible
’s interact()
method is just a few lines: the code looks into
an HashMap
(contained in the InfoObject
struct) to see if there is a result for the interaction
requested (we’ll see in a while how possible results are added when creating a new InfoObject
):
if a match is found, we return an Info
value of the InteractionRes
enum, which contains a
simple string to show to the player; with no match, we return the same type, but with a default string.
Now to the main loop code:
The parser is just a regular expression at the moment. Once it matches the type of the target
(the enum in which it’s contained was previously looked up via the object’s tag), the main loop
invokes its interact()
method, passing the (previously looked up) Interaction
.
We use match in order to unwrap the InteractionRes
enum: if the value is
Info
we show the contained string to the player; other result values are not supported for this
specific kind of object, so a panic!()
is probably the best option (=forces somebody to debug
the thing).
Now that we have all of this setup, it is possible to modify the code - shown in previous article - which was used to create the kitchen, so that is becomes:
The code should be self-explanatory enough. The only thing worth noting is that InfoObject
is wrapped
into an UnendObject::Info
enum value: as we did for sections, this allows to have different object
(Interagible
) types inside the same containing HashMap
.
So, let’s see if this works somehow:
And now… a slightly different implementation: a PortalObject
, which we define as an object which
transports the player to another section of the game when used.
Skipping the struct constructor and other service code (which you can find the in the repository),
the interesting part comes (as before) with the interact()
method:
Only two interactions are supported by PortalObject
: the player can only look at it or use it.
Everything else shows a default string. Compared to InfoObject
, the difference here lies in how
the Interaction::Use
is handled, that is by returning an InteractionRes::GotoSection
containing
a string with the tag of the destination section. The main loop code will therefore know how
to use that tag:
Dead easy: either show the description or change the section the player is in and skip to the next loop cycle. Now we only need to actually create the portal object and put it inside a section/room.
A fireplace which happens to be a portal… how interesting! The secretroom, defined elsewhere, is a section for which there is no access from any of the other sections; the secretroom itself, however, has an exit which brings the player back to the hallway.
That’s all! See you next time with something else. I’ll likely try to implement
inventory, and therefore objects which can be taken. This poses some challenges: as of now,
everything in the game has an immutable state; using mutable states, besides the programming aspects
(which may be non-trivial for a Rust newbie like me), will also likely lead to some changes in how
Visitable
s are structures: if an object is taken, it must disappear from the section, and the
section description (which is now a monolithic text block) should change. We’ll see…