Oh, the Little Things - Test Adventure Dev 02

posted in A4L
Published January 02, 2019
Advertisement

G'day....

So the new year is upon us and after taking a few days off to do family things over the holidays I managed to get some time into my Test Adventure Game Project. As mentioned in the last DevDiary I wanted to build a base skeleton for the game so I could work on the meat of the project, which is the Command Interrupter. My goal for this phase was simply to implement classes for Player, Level, Room, Exits and Items. That way I could sit in a test room, with a few items lying about so I can build an test my command processing code by interacting with that room and those items.

That is all up and running now. So the game loop is running, and I get to enter commands and stuff, and there is a room to look at and items to interact with, but at this moment there is no logic to "understand" anything or "do" anything.

To do this though I also had to build my DataReader class, as I have always wanted the meat of the "game design" to be done externally form the coding. The goal here was to build data files for rooms and items that the game reads from. Allowing me to quickly edit descriptions and even set boolean flags without ever having to touch the code. In addition my plan for the command processor will be using dynamically built word lists and the DataReader will be used for this as well to construct those lists from files. (more on the actual command interpreter when I get around it it)

Building the DataReader was a little more fiddly than I thought.

I went through a few iterations of methods and towards the end of the coding phase (a phase is what I am calling a short term project goal) decided that "bracketed" data is probably the best way to do it. Unfortunately I was so deep into using line by line reading that I didn't want to redo everything. I may still redo things, but as I said the testing environment is up, so I will probably just move onto the command interpreter.

Examples of DataFiles

Room DataFile Example @PasteBin

  • Name
  • Room_Description
    • Default room description that always prints on on any given "Look-Room" command. The idea is for this text to have appended onto it any other "Look-Room" information that may be required by cycling through the objects in the scene.
  • Exit Brackets
    • These are two text lines that can be searched for to find the given index number for the start and end of a individual item information.
      • Exit-Name
        • Contains the name for the individual Exit.. like North... or "Road into Forest".
      • Open
        • This is a Boolean value that tells me if the exit is closed or not at initialisation.
      • Look-Room-Closed
        • text description that is to be appended to the default "Look-Room" result. If the exit is CLOSED.
      • Look-Room-Open
        • text description that is to be appended to the default "Look-Room" result. If the exit is OPEN.
      • Look-Exit-Open
        • This is the description of the "Look-Exit". So when you actually examine the exit itself. If the exit is currently OPENED.
      • Look-Exit-Closed
        • This is the description of the "Look-Exit". So when you actually examine the exit itself. If the exit is currently CLOSED.
      • Use-Open
        • Text response if the "Open-Exit" command is successful.
      • Use-Closed
        • Text response if the "Open-Exit" command is a failure.
  • Item List Brackets
    • List as many items here to fill room. Each item name corresponds to a physical item data file to be read and imported into the room object.

Item DataFile Example @PasteBin

  • Name
  • Get-able
    • a booleen value to determine if the object is able to have the "Get-Inventory" command run on it.
  • Description_Room
    • Text description that is to be appended to the default "Look-Room" result. If the object is in the scene and not picked up.
  • Description_Dropped
    • Text description that is to be appended to the default "Look-Room" result. If the object has been placed in the room by the player.
  • Description_Gone
    • Text description that is to be appended to the default "Look-Room" result. If the object has been removed from the room.
  • Get-Item-Success
    • Text to be printed when "Get-Item" is SUCCESSFUL.
  • Get-Item-Never
    • Text to be printed when "Get-Item" is done on an object that is never allowed to be gotten.

uc?export=download&id=15lzc_29R5Bt-8uL29

This is what the data files look basically look like. I use NotePad++ and set up some custom colouring so it is a lot easier to read while editing.

ReadData-Room

  • At this time I have 2 Data Files in the project. ROOMS and ITEMS.
  • Each individual room or item is a separate file in either the room or item folder
  • Each DataFile is read into a List<string> using File.ReadLines().
  • I then read those lines looking for keywords at the start of each line using .StartsWith()
    • or I read a section of the file into lines between two brackets
  • Those found lines are then cleaned and added to the room's object as data.
  • Also the room DataFiles contains a names of items, which match the file names item data files. These datafiles are in turn processed to build the items that are then added into the Room.
  • This entire process of adding a room to the level map is boiled down to a single command "LevelMap[x, y] = DataReader.ImportRoomData(string name);"

I am reading the DataLine by line, as you can see in the text files each line has its own "keyword" at the start of the line. Even if it is a long string. The entire text file is read into the application, ignoring any empty lines. It then processes each line looking for the keywords at the start of the line. If the keyword is found, it removes that keyword and then uses the rest of the line as data by plugging it into various variables for objects in the game.

This is all working pretty well. Though I came to a issue with adding multiple exits.

I wanted to be able to add as many exits as I liked and I didn't want to have to code each exit keyword to be unique. This lead me to working out that the Line by Line reading is probably not the best. As you can see I now have a EXIT_START and EXIT_END keyword, with the data for that unique exit inside. What the code dose now is look for EXIT STARTs and tally the total exists in the file. At the same time is it tallying and recording the indexVale in the List<string> for those "start and end" bracket lines. I can then feed those into a method and do the keyword search to extract the data on a targeted "section" of the file instead of the entire file. It can even loop by the total found count to automatically add things as I edit the text file.

I think this idea is a far better way to handle line reading when trying to extract data. I will be using "brackets" from now on until I work out something better.

Forward Ho!

Right.... so the test environment is up. I may have to look at adding more or changing the actual data being extracted but I think the extractor is robust enough for my needs as well. So now the fun part starts! Building my Command Interpret.

See Ya Next Time

--A4L

uc?export=download&id=1SoiX0una9I8b3EvAG

Test Print of a testRoom with Three exits and 4 Items (I have not printed out all the item properties)

1 likes 4 comments

Comments

duke_meister

G'day, hope it's going well. I'd say your ideas overall are quite good. Consider using a standard format such XML or JSON (or other structured text) however. This will save you the time of designing the format of the 'design language' itself and you can concentrate on the data format (the schema in XML). Plus you get many ready-to-go reader/writers.

I know you've said (in a forum post) that this is a coding exercise, so if that's the case then fair enough and just ignore.

8<> --------------------------------------

Follows the post I was going to put in the forum, auto-merged here with my previous post.

8<> --------------------------------------

I guess you wanted something like below. There's no need for casting/conversion methods. 

But back to your requirements. 

7 hours ago, A4L said:

I will be using the input phraser to spit out commands and targets. Like "get,vase" for example. Each command is boiled down form a thesaurus of words. So... "take the vase from the table" turns into "get,vase". Meaning that there should be a number of base commands like "get" "push" "talk" "ask" etc etc.

Do you mean parser? Many books have been written just on this subject. But obviously you'd be wanting to keep it simple to start.

7 hours ago, A4L said:

These strings are used to trigger unique code for that individual object, or trigger default code. so basically "get()" on one object should trigger a different "get()" on another, unless it is triggering the base get() that everyone that allows get in the first place has as default.. even though the command processor has sent the exact same command.

So I can see why you were experimenting with that test code. But I'm not sure this is the right approach.  Each object is not going to 'get' itself. But it might be useful perhaps as a 'reaction' to being taken animal.react(take) { say( "it struggles and squirms")}, person.react(take) { say ("I don't think so") }. Your game engine might be the 'thing' that does the 'getting', being passed the object in question.

 

7 hours ago, A4L said:

The only real question I have now is how I can build the objects. At some point I need to insert unique code.

Not sure what you mean here.

7 hours ago, A4L said:

I can place code strings in text files to be loaded at the time of initialization, (in the same way I can populate rooms with items) but then I am not sure I am able to use the visual studio's debugging and popup helpers.

Not sure what you mean here either. With the highlighted part, do mean you need with visual studio's debugging features?

----------------------


using System;
using System.Collections.Generic;

namespace atestadventure
{
    class ClassName
    {
        public string Name { get; } = "defaultName";
        public virtual void RunAction()
        {
            Console.WriteLine( Name);
        }
    }

    class Unique : ClassName
    {
        public string UName { get; } = "UniqueName";

        public override void RunAction()
        {
            Console.WriteLine( UName);
        }
    }

    class YAunique : ClassName
    {
        public string YaName { get; } = "YetAnotherName";

        public override void RunAction()
        {
            Console.WriteLine( YaName);
        }
    }

    class TestingGround
    {
        static void Main(string[] args)
        {
            var doesThisWork = new Dictionary<string, ClassName>
            {
                {"test0", new ClassName()}, {"test1", new Unique()}, {"test2", new YAunique()}
            };

            RunAction(doesThisWork["test0"]);
            RunAction(doesThisWork["test1"]);
            RunAction(doesThisWork["test2"]);

            Console.ReadKey();
        }

        static void RunAction(ClassName behaviour)
        {
            behaviour.RunAction();
        }
    }
}

 

PS: Is it ok to post this kind of thing here or is the forum preferred?

February 14, 2019 10:35 AM
A4L

I'll need time to go through this, as I said in latter blogs and in that other thread I am currently moving house, and renovating.. so my "hobby" time is limited. I intend to get back into this late February, early March...

February 15, 2019 02:00 AM
jbadams
19 hours ago, duke_meister said:

PS: Is it ok to post this kind of thing here or is the forum preferred?

Either is fine. :)

February 15, 2019 06:11 AM
duke_meister
5 hours ago, A4L said:

I'll need time to go through this, as I said in latter blogs and in that other thread I am currently moving house, and renovating.. so my "hobby" time is limited. I intend to get back into this late February, early March...

Yep I realise that, just putting it here while I think of it. See you then.

February 15, 2019 07:02 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement