4

so for the past several months, I've been working on a personal project. It's a comically large, highly open, handcrafted text adventure game packed with inside jokes and fun little surprises for my friends to play around with and experience. I've got a few barebones prototypes working, and they've gotten a lot of "oh cools" and laughs.

I've been coding it from the ground up in Python. I recognize that using some engine or framework or language specifically built for this purpose would be far easier, but I'm dead-set on homebaking this myself. This is mainly for learning purposes, but also so that I can do increasingly cool things that nobody expects a simple little text adventure game to be able to do (such as send desktop notifications, or read the current OS username, or play music, or understand synonyms in commands, or type things out to the console gradually like a human would.)

Recently, I've abandoned the Python version and started rewriting it in C# because its purpose as a learning tool has run out, and also because I coded it terribly. I've been using giant JSON dictionaries as essentially glorified piles of global variables, storing things like what doors the player has entered and what choices they make, so that the main script (as well as a module I created for some shortcuts involving typing and asking questions) can access them.

What's more, the main file is one huge mess of branching if statements containing scenarios every possible way that the story could have unfolded. This quickly became unreadable and unmaintainable and it completely killed my motivation to work on the project for a long time.

My question is this: In general terms, what's a more elegant and structured way of doing these things, particularly the if statement part? Doing it the way that I am now feels like such a naive and primitive workaround, and I feel like there has to be a more sensible and scalable way to organize this kind of project.

EDIT: Some code samples to demonstrate the problem. Be warned, it's bad!

while True:
  choices.askchoice("1-entrance", ["yes=enteredCave","go=enteredCave"], speed = 3)
  if save["enteredCave"]:
        break
  else:
        choices.askchoice("2-rejectCave", ["yes=ending-abandonment", "no=enteredCave"])
        ..etc

As you can see, I'm using an "askchoice" method (static of a "choices" custom object created at the start of the file) to clean up the process of printing the prompt text prettily, waiting for input, and then storing the appropriate boolean to the JSON dictionary. *If you're curious, that first parameter is for the key of the prompt for the question (stored in yet another JSON dictionary), and the string list is for all of the possible outcomes. It parses it using that = sign. For example, "yes=enteredCave" means that an input of anything defined as a synonym of "yes" will set the enteredCave boolean in the JSON to true. These booleans then get accessed again in my if forests:

while True:
        choices.askchoice("2-interiordescription", ["go&1=gotoDoor1","go&2=gotoSpirits","go&3=gotoDoor3","go&4=gotoDoor4","go&5=gotoDoor5","exit=hasLeftCave"], speed=3)
        if save["gotoDoor1"]:
            doStuff()
        elif save["gotoDoor2"]:
            doStuff()# and then more branching if statements inside this one!
        ...etc

...Which, as you might be able to tell, creates a total mess.

6
  • 4
    A code example could go a long way in identifying the appropriate solution. It doesn't have to be the entire codebase, but just something that is indicative of the design you used.
    – Flater
    Aug 25 '21 at 7:00
  • The finite state machine may fit your game requirements, especially to remove the forest of nested if statements. Every time the character does one action, the game changes its state, which has another set of available actions.
    – Hieu Le
    Aug 25 '21 at 13:55
  • Added code samples as per @Flater's suggestion. Here be dragons. As for the finite state machine, that'd definitely be cleaner, but wouldn't deciding things based on individual choices (such as, unlock this door in room 3 if the player picked up the red orb and has full HP) eventually have to rely on elaborate if statement tomfoolery anyway? Aug 25 '21 at 15:52
  • Even if you don't intend to use an existing IF-authoring framework, I'd recommend looking through some introductory documentation / examples for a few of them, to get some ideas about how they have organized data and logic and dealt with some of the issues. One resource: firthworks.com/roger/cloak/index.html
    – aschepler
    Aug 26 '21 at 3:51
  • Minor quibble: you say "JSON" dictionary but I don't see any JSON, that looks like a Python dictionary?
    – pjc50
    Aug 26 '21 at 7:46
9

You're not the first person to write an adventure game; they're an extremely old category of game, and there's plenty of examples around. While the Usborne Book from 1983 might be a bit outdated, it does have some great art. The original FORTRAN for Colossal Cave is around as well. Then there is a whole community building games, mostly in the INFORM language.

An outline of possible approaches:

  • table-driven. You are in room N and you go north, so you look in the rooms table row N for the "north" value M, then set current room to M. Can be big arrays of text or, in a more modern program, a database.

  • object-orientated. You map verbs fairly directly to methods on nouns. So "READ BOOK" becomes finding the object with name "BOOK" and calling its "READ" method.

  • entity-based. Kind of a hybrid: rather than just having a lot of methods, an object has "behaviors" attached to it. So if an object has the "readable" behavior, you can read it (invoking its readable behavior object, which may be shared with other objects). If it doesn't, then you get a generic "can't do that" error.

  • metalanguage. INFORM is a language designed specifically for writing adventure games, and there are implementations of it for many platforms.

2
  • 2
    PS you may enjoy ifcomp.org
    – pjc50
    Aug 25 '21 at 13:46
  • Note that Inform 7 uses a rules engine, which is kind of like function overloading based on specific classes or even specific objects. Kind of similar to the entity-based approach? Aug 26 '21 at 2:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.