Text Adventure Combat

Hello Everyone,

I was wondering if anybody has ever tried to make a combat system for a text based adventure game. A simple example would be, the player is given the option to travel North, East or West down the path in the woods or return to town. Upon choosing any option other than return to town a random number between 1 and 10 is generated a 7 or greater creates a combat scenario with one goblin. How would I do a turn for turn combat system between the player and the goblin and track HP on both sides. I’m just starting into the scripting side of unity I’ve created a short simple text adventure but would like to implement a combat system for a little more spice. Eventually maybe a simple leveling system. Any help would be appreciated.

If this is the wrong form for this question let me know and ill move it.

Thanks

That’s a real open-ended question. The simplest combat system is just you have a set of dice, the goblin has a set of dice, maybe they’re the same, maybe one is larger than the other.

When you do combat, you each roll once and the higher one lives, the lower one dies.

That’s not much fun for the human, so combat systems tend to be a lot more complicated.

For instance, you could both have hit points denoting health, and the dice indicating how much attack power you have.

Then you have to iterate and attack and perhaps whoever loses will lose one hitpoint. That gives the player an opportunity to try to run away between blows if it’s going badly. The ability to run away might require beating another dice roll, and if you fail, the goblin gets a free shot at you for that one attack.

Definitely do some reading because there has been a LOT of ink spilled about how to craft this stuff, but I recommend starting out super-simple so you develop an intuitive feel for engineering a combat system.

1 Like

The key to a combat system is to give the player real choices. There has to be some form of strategy that will aid in victory. Different enemies have different strengths and weaknesses. Also, resource management. Do I save a potion for later, or a spell, or use it now. Of course, there has to be some luck too. Sometimes a spell fizzles, or whatever. Basically, the opponents have different stats and you just subtract them as the fight ensues. First one to zero health loses.

Thanks for the advice. You wouldn’t happen to have any code snippets of a random encounter generator and a combat system that implements a dice role sort of like d&d a role for hit and roll for damage kind of set up.

Since you’re just getting into scripting, now is the perfect time to tackle working with data files. (Whenever possible, keep your code and data separate.) You could use ScriptableObjects or something similar, but I think a simple text file is the best way to start.

Two easy text formats are comma-separated values (CSV) and JSON. Your CSV file might look something like this:

#chance, monster, hp, tohit, damage
50,  goblin, 5, 25, 3
80,  orc,   10, 40, 5
100, ogre,  18, 45, 8

The way I chose to format the file above, the random encounter is a goblin if a d100 die roll is 00-50. The goblin has 5 HP, hits 25% of the time, and does 3 damage. If the d100 roll is 51-80, the encounter is an orc who has 10 HP, hits 40% of the time, and does 5 damage.

In Unity, text files in your project are treated as TextAssets, the contents of which are in TextAsset.text. You can use the String.Split method to split it on newlines (“\n”) to break out each line, and then again on commas (“,”) to get each field in the line.

JSON is more awkward to edit unless you use a JSON editor instead of a plain text editor. But Unity’s JsonUtility class makes it easy to read. Your JSON text file might look like this:

{
  encounters : [
    { "chance":50, "monster":"goblin", "hp":5,  "tohit":25, "damage":3 },
    { "chance":50, "monster":"orc",    "hp":10, "tohit":40, "damage":5 },
    { "chance":50, "monster":"ogre",   "hp":18, "tohit":45, "damage":8 },
  ]
}

And you could read it in a class like this:

using UnityEngine;
using System;

[Serializable]
public class Encounter {
    public int chance;
    public string monster;
    public int hp;
    public int tohit;
    public int damage;
}

[Serializable]
public class EncounterList {
    public Encounter[] encounters;
}

public class CombatManager : MonoBehaviour {

    public TextAsset dataFile; // Assign in inspector.

    private EncounterList encounterList;

    void Awake() {
        // Read the data file:
        encounterList = JsonUtility.FromJson<EncounterList>(dataFile.text);
    }

    // Choose a random encounter:
    public Encounter ChooseRandomEncounter() {
        var roll = Random.Range((int)0, (int)100);
        foreach (var encounter in encounterList.encounters) {
            if (roll <= encounter.chance) return encounter;
        }
        return null;
    }
}

The script above shows how to read a data file and choose a random encounter. It doesn’t cover how to run the random encounter, but the previous replies sketched it out pretty well. If you get stuck on that part, post back and I’m sure some of us may have some suggestions.

That sounds exactly like what I need to start playing with, I’ve been looking all morning for an example of any sort of combat in a text adventure. All my work has turned up nothing. Ill insert a copy of sample script I’m using to figure things out, could you point me at a function that would use the CVS you have shown. In my research I have also come across another question, what sort of object would I use to store character data such as hp and attack damage?

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System;

public class TextController : MonoBehaviour
{

  

    public Text text;
    private enum States { cell, eyes, closed, Stand };
    private States myState;

    // Use this for initialization
    void Start()
    {
        myState = States.cell;
    }

    // Update is called once per frame
    void Update()
    {
        print(myState);
        if (myState == States.cell)
        {
            state_cell();
        }
        else if (myState == States.eyes)
        {
            state_eyes();
        }
    }
    void state_cell()
    {
        text.text = "Light fills your vision, you hear the cherping of crickets " +
                    "what could that be you think to yourself. I was just in bed " +
                    "why in the world am I outside?\n\n" +
                    "Press E to open your eyes, Press Q to keep .";
        if (Input.GetKeyDown(KeyCode.E))
        {
            myState = States.eyes;
        }
    }

        void state_eyes()
        {

            text.text = "You open your eyes to find a forest. Tall oak trees " +
                        "thick brambles, and the ever present bird sounds. " +
                        "What should I do now?\n\n" +
                        "Press G to Stand Up, Press C to return to Cell";
            if (Input.GetKeyDown(KeyCode.C))
            {
                myState = States.cell;
            }
        }
    }

I thought I’d mention (/recommend) that if you would like to share code for some feedback, you should do so by posting the code here. :slight_smile:
Check out this link for how to do it nicely, if you don’t know, already: Using code tags properly

Few people want to download scripts to look them over :slight_smile:

1 Like

thanks, I thought there might be a better way. ill take care of it

In TextController.cs, notice how the data and code are mixed together? For example:

        void state_eyes()
        {

            text.text = "You open your eyes to find a forest. Tall oak trees " +
                        "thick brambles, and the ever present bird sounds. " +
                        "What should I do now?\n\n" +
                        "Press G to Stand Up, Press C to return to Cell";
            if (Input.GetKeyDown(KeyCode.C))
            {
                myState = States.cell;
            }
        }

This will quickly become a beast to maintain, which is why my first recommendation was to separate code and data. Since CSV is a spreadsheet format, it’s good for the encounter table. You could read the CSV above like this:

var rows = dataFile.text.Split("\n");
encounterList = new EncounterList();
encounterList.encounters = new Encounter[rows.Length - 1]; // Skip first row, which is a comment row.
for (int i = 1; i < rows.Length; i++) {
    var encounter = new Encounter;
    encounterList.encounters[i] = encounter;
    var fields = rows[i].Split(",");
    encounter.chance = Int32.Parse(fields[0]);
    encounter.monster = fields[1];
    encounter.hp = Int32.Parse(fields[2]);
    encounter.tohit = Int32.Parse(fields[3]);
    encounter.damage = Int32.Parse(fields[4]);
}

Technically, the CSV format can be a little more complicated, but as long as you keep your data file like the example above, then this code will work fine. To keep the example clear and simple, the code doesn’t do any error checking. (You can also find open source CSV readers on github and codeproject.)

Rooms, on the other hand, aren’t ideal for CSV because their text is so freeform. JSON would be a good choice, or you could establish your own simple format such as:

Cell
Light fills your vision, you hear the chirping of crickets. What could that be you think to yourself.
I was just in bed.  Why in the world am I outside?
\n
Press E to open your eyes.
- E: Eyes

Eyes
You open your eyes to find a forest. Tall oak trees thick brambles, and the ever present bird sounds.
What should I do now?
\n
Press G to Stand Up, Press C to return to Cell.
- G: Standing
- C: Cell

In this case, you’d read the first line (“Cell”) as the room name, subsequent lines as the room description, lines starting with “-” as keypresses that go to other rooms, and a blank line denotes the start of a new room. Personally I think JSON would be easier, since you don’t have to parse it yourself.

Ok, I will check it out and attempt a test run then report back.

I did! I enjoyed EAMON as a lad but found it far too limiting, so I wrote my own similar (but more powerful) system called The Shire, which included armor, weapons, classes, and turn-based combat. (And 80-column text!) Unfortunately I can’t show you any sites about it, because it never gained a wide following, and this was about a decade before the Internet. :wink:

Also, that was all in AppleSoft BASIC, so may not be too relevant to what you’re asking. I just got excited.

Well, the player and the goblin are both objects that can have various stats, including HP.

If you already have a simple text adventure, then it sounds like you already have a turn system — each time the player inputs a command, you run a turn (right?). So that’s when you give all your objects a chance to do something, which in the case of a goblin, might be attacking.

Unity’s not really designed for this sort of game, but it certainly can be done.

Best,

  • Joe

But JSON isn’t especially easy to write by hand. I’d recommend GRFON, which is designed for exactly this sort of thing.

1 Like