Text game v1

Our game world file contains basic navigation information, which means we are now able to write a script capable of walking around the world and seeing what is there. At this time there are no objects to pick up or use, but you can add them later.

Here is the game navigation script - save this as game.php:

<?php
    $World = simplexml_load_file("gameworld.xml");
    $CurrentPos = 0;
    $Done = 0;
    print "\n";
    printplace() ;

    function printplace() {
        GLOBAL $World, $CurrentPos;
        $Room = $World->ROOM[$CurrentPos];
        $Name = $Room->NAME;
        $Desc = wordwrap((string)$Room->DESC);
        print "$Name\n";
        print str_repeat('-', strlen($Name));
        print "\n$Desc\n\n";

        if ((string)$Room->NORTH != '-') {
            $index = (int)$Room->NORTH;
            print "North: {$World->ROOM[$index]->NAME}\n";
        }

        if ((string)$Room->SOUTH != '-') {
            $index = (int)$Room->SOUTH;
            print "South: {$World->ROOM[$index]->NAME}\n";
        }

        if ((string)$World->ROOM[$CurrentPos]->WEST != '-') {
            $index = (int)$Room->WEST;
            print "West: {$World->ROOM[$index]->NAME}\n";
        }

        if ((string)$World->ROOM[$CurrentPos]->EAST != '-') {
            $index = (int)$Room->EAST;
            print "East: {$World->ROOM[$index]->NAME}\n";
        }

        print "\n";
    }

    while (!$Done) {
        $input = fgets(STDIN);
        print "\n"; // add another line break after the user input

        $input = split(' ', $input);

        switch(trim($input[0])) {
            case 'north':
                if ((string)$World->ROOM[$CurrentPos]->NORTH != '-') {
                    $CurrentPos = (int)$World->ROOM[$CurrentPos]->NORTH;
                    printplace() ;
                } else {
                    print "You cannot go north!\n";
                }
                break;
            case 'south':
                if ((string)$World->ROOM[$CurrentPos]->SOUTH != '-') {
                    $CurrentPos = (int)$World->ROOM[$CurrentPos]->SOUTH;
                    printplace() ;
                } else {
                    print "You cannot go south!\n";
                }
                break;
            case 'west':
                if ((string)$World->ROOM[$CurrentPos]->WEST != '-') {
                    $CurrentPos = (int)$World->ROOM[$CurrentPos]->WEST;
                    printplace() ;
                } else {
                    print "You cannot go west!\n";
                }
                break;
            case 'east':
                if ((string)$World->ROOM[$CurrentPos]->EAST != '-') {
                    $CurrentPos = (int)$World->ROOM[$CurrentPos]->EAST;
                    printplace() ;
                } else {
                    print "You cannot go east!\n";
                }
                break;
            case 'look':
                printplace() ;
                break;
            case 'quit':
                $Done = 1;
                break;
        }
    }

    print "\nThanks for playing!\n\n";
?>

Before we go through that script line by line, first try running it. The commands are "north", "south", "west", and "east" for directions, "look" to repeat the description of the current place, and "quit" to exit the game. Run the game using the CLI SAPI, like this:

// Windows
copy game.php c:\php\cli
copy gameworld.xml c:\php\cli
cd c:\php\cli
php game.php

// Unix
php game.php

The extra instructions for Windows are there because you probably do not have the PHP CLI executable in your path, so we copy game.php and gameworld.xml into the same directory as the PHP CLI and run it from there. You should, of course, change "c:\php" to wherever you have PHP installed.

If all has worked well, you should be able to play the game - or at least as much of the game as we have so far!

Now, onto explaining how the script actually works. There are three distinct parts to the script, and these are the initial setup, the printplace() function, and the game loop. The initial set up, lines one to five, is easiest - it loads the XML file using SimpleXML, sets the current player position to 0 (the first element in our rooms array), sets $Done to 0, which is used in the game loop to say "we are currently playing", and prints out the initial room information using printplace().

The printplace() function is where we print out the name and description of the player's current location, as well as tell them which places lie in the compass directions. The first two lines make $CurrentPos and $World, created outside the function, available for use inside the function, and also sets up the $Room to point to the current room - this saves having to repeatedly say $World->ROOM[$CurrentPos], which is much slower as it has to search the array each time.

Moving on, the next two lines that might confuse you are the calls to wordwrap() and str_repeat(). The wordwrap() function is called so that the description is automatically wrapped onto more lines if it is long - this stops it from being displayed all on one line, which would not be very easy to read. Note that the input to wordwrap() is typecasted to a string - it is a string in the XML file, but SimpleXML is likely to convert it to an object, so we need to make sure and typecast all variables that come in to what they should be. The str_repeat() call is there in conjunction with a call to strlen() so that we get a string back that has a "-" for each letter in the place name. As command line text is usually displayed in a fixed-width font, this will be precisely long enough to underline the place name, so it is printed out immediately after the name.

The final part of the function is made up of four very similar if statement blocks that check each of the four possible directions. This part of the code is designed to print out the list of directions the player can travel and what is there. Naturally not every room allows the player to go in every direction, which was why the XML contained hyphens ("-") wherever a direction was blocked. What these if statements do is check whether the direction is marked as a -, and, if it is not, print out the contents. Again, note the typecasting - this is very important.

In printing out the direction information, the two key lines are (for the south direction):

$index = (int)$Room->SOUTH;
print "South: {$World->ROOM[$index]->NAME}\n";

What this does is grab the array index out of the SOUTH element, typecast it as an integer to make sure the conversion takes place, then looks up the name of the matching element in our ROOM array. This is done once for each of the four directions, and finally we print out a new line just to make things nicely spaced on-screen.

The last part of the script is the game loop, which starts with while (!$Done). The $Done variable was set to 0 near the beginning of the script, which means this loop will keep going until we set $Done to 1.

The first three instructions inside the loop read one line of text from the user, print a new line after it, then split it into an array. Reading text from the command line was covered previously, and this is pretty much exactly the same code we used back then. The user input is split into an array using a space as the separator - this is irrelevant right now, because we only have one-word commands. However, later on we would like to be able to say "use axe", "get rock", etc, and this splits the input elements up quite nicely.

The key action inside the input will always be the first element, so we use that in a switch/case check to decide what the player wants to do. The "north", "south", "west", and "east" direction actions are basically identical, so we will look at the "west" action:

case 'west':
    if ((string)$World->ROOM[$CurrentPos]->WEST != '-') {
        $CurrentPos = (int)$World->ROOM[$CurrentPos]->WEST;
        printplace();
    } else {
        print "You cannot go west!\n";
    }
    break;

This action executes if the player has asked to travel west, so the first thing that is done is to check to make sure the player can go west by comparing the (typecasted!) WEST value of the current position against our no-go sign of "-". If WEST contains anything other than a -, we set the $CurrentPos variable that holds the player's current position to the (typecasted!) value of WEST, then call printplace() to update the player on the new location. Finally, "break;" is used to jump to the end of the switch/case. As $Done hasn't been changed, the loop will repeat and read another line from the user.

The "look" action simply calls printplace() without changing any values, as it is designed to give players a refresh of the current place information in case they forgot.

Finally, there is "quit", which sets $Done to 1 and thus terminates the loop and the game.

That is it - the first version of the game is complete, and it was not that hard, was it? We have only really scraped the surface on what can be done with text-based games in PHP, because, for the most part, adding functionality is just re-using the same functions we've looked at already. To give you a head start in adding items, here's how I would do it:

  • Create a new XML file, gameitems.xml, with a list of items

  • Add <ITEM> elements to rooms where items are, e.g. <ITEM>3</ITEM>.

  • Alter printplace() to check for items in the room.

  • Add a "get" action that allows people to pick up room items, and "drop" to drop room items

  • Add a "user" action that allows people to try using items they have picked up. By default this should say "You cannot use your <item name> here.".

  • Edit gameitems.xml and add <USE> elements to each item that specifies the room numbers where the item can be used and what should be printed out, e.g.: <ITEM> <NAME>Rock</NAME> <USE> <ID>5</ID> <ACTION>You throw the rock at the dangerous-looking man and knock him out.</ACTION> </USE> </ITEM>

There is a lot you can do with text-based adventuring, and it is mostly quite easy because PHP handles text-processing so smoothly.

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Getting graphical >>

Previous chapter: Text-based world planning

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.