Creating Your Base Actions

Each action that you can take requires some command to be executed. Since each command will need the same base variables you will create a command class from which each subsequent command can inherit.

Since each command can have a parent command, you will need a variable to store the parent. You will also need a way to execute the commands. It would make sense to make each command execute its own functions instead of having the CCommand() class execute the command functions. Each command could possibly have sub-commands, so you also need the ability to store those. Since you will store sub-commands you need to keep track of which command you are currently on. If anything goes wrong with the command, you need to roll back anything that you have done. After all, you don’t want partial data floating around during the game.

Now that you know what each command needs to do, you can start to plan how you will code the class. The way I see it, each command can have four possible states. The command could error, it could be waiting for input, it could have more sub-commands to process, or it could be done processing. So let’s define some constants.

// define some standard return values for commands.
// Specific commands can return whatever they like.
// Commands that are executed by CGame must return one of these four values.
define(“CMD_ERROR”, 1);       // an error occurred
define(“CMD_CONTINUED”,2);     // the command has more steps to execute before it finishes
define(“CMD_FINISHED”, 3);     // the command has finished executing
define(“CMD_NEEDS_INPUT”, 4); // the command needs human input before it can continue

In the comments you see a reference to a class called CGame(). The CGame() class will handle running the game and will be covered later in this chapter.

Next let’s create the CCommand() class. You know that you have to track the parent object, the current command, the sub-commands of the parent object, and whether the command needs to be rolled back.

class CCommand
{
    // this is a reference to a parent object
    var $m_pParent = null;

    // the index of the current sub-command that is being executed
    var $m_nCurrentCommand;

    // if true, the command needs to be rolled back.  if false,
// the command does not
    var $m_bNeedRollBack;

    // an array to hold the sub-commands
    var $m_arraySubCommands;

    function CCommand( &$parent )
    {
        // set the reference to our parent
        $this->m_pParent = &$parent;

        // set the current command to the first one
        $this->m_nCurrentCommand = 0;

        // initially, commands don’t need to be rolled back
        $this->m_bNeedRollBack = false;
    }
    
    function Execute( $args )
    {
        // each command must deal with handling its own execution
        return CMD_FINISHED;
    }

    /*
    This function handles the return value from a sub-command.
    It is responsible for incrementing the current command index
    and for determining whether this command has anything
    left to execute.
    */
    function HandleSubCommand( $result )
    {
        // did it finish?
        if ($result == CMD_FINISHED)
        {
            // yes, so go to the next one
            $this->m_nCurrentCommand++;
        }
        // was there an error?
        else if ($result == CMD_ERROR)
        {
            // yes, so propagate the error upwards
            return CMD_ERROR;
        }
        // does the command need input?
        else if ($result == CMD_NEEDS_INPUT)
        {
            // yes, so just propagate the need for input upwards
            return CMD_NEEDS_INPUT;
        }

        // have we already executed everything
// we need to for this command?
        if ($this->m_nCurrentCommand >=
count( $this->m_arraySubCommands ))
        {
            return CMD_FINISHED;
        }
        // we still have more to do!
        else
        {
            return CMD_CONTINUED;
        }
    }

    function OnError( $sError )
    {
        // if we have a parent, let him deal with it
        if ($this->m_pParent != null )
        {
            $this->m_pParent->OnError( $sError );
        }
        // otherwise, we *are* the parent, so we have to deal with it
        else
        {
            // print out the error message
            echo “An error occured: $sError<BR>“;

            /*
            We roll us back, which, assuming all our child classes
            implemented OnRollBack correctly, will undo everything.
            */
            $this->OnRollBack();
        }
    }

    // the default implementation of the OnRollBack command
        //assumes that the command
    // performs no processing that affects the roll back.
        //if this does not hold, a
    // command must override this method in order to
        // take the processing into account
    function OnRollBack()
    {
        // since this command doesn’t perform any processing
        //outside of subcommand,
        // we can simply call rollback on each of its
        // sub-commands in reverse order
        for($i = $this->m_nCurrentCommand; $i >= 0; $i--)
        {
            // unroll the sub-command if it’s set.
        // the isset check is
            // to make sure that we don’t try to
        // roll back commands that
            // don’t have sub-commands but don’t override OnRollBack.
            if (isset($this->m_arraySubCommands[$i]))
            {
                $this->m_arraySubCommands[$i]->OnRollBack();
            }
        }
            // reset the current command pointer
            $this->m_nCurrentCommand = 0;
    }

}

When a command object is created, it calls the CCommand() constructor. This constructor initializes all the member variables of the class. In this case the parent object gets set, the number of sub-commands is set to 0, and the rollback required variable is set to false.

Earlier I mentioned that each parent command would handle its own execution. So the only thing that the Execute() function needs to do is to tell the game that it is finished executing.

Now you need a way to handle the sub-commands that need processing. What better name for a function than HandleSubCommand()? The HandleSubCommand() class takes a single argument that tells the handler what state the command is executing in. First you see if the command has finished. If the command finished successfully then you can move on to the next sub-command in the tree. If the command did not finish then it must be in another state. So you check to see if an error in execution has occurred. If an error has occurred, then you need to tell the game to stop its execution. If an error has not occurred, then you need to check to see if the command is awaiting input. If the command is waiting for input, then you need to tell the game to hold up and wait for the user.

If you get past all of that checking you must then check to see if there are more commands to execute. If there are more commands to execute, then you need to tell the game that it needs to continue to the next command. If you have executed all of the possible sub-com-mands then you are finished executing.

What happens if an error occurs during execution? If an error does occur during the exe-cution of a command, you need to call the OnError() function. There are two states where an error can occur: 1) if the command has a parent, and 2) if the command doesn’t have a parent. If the executing command has a parent command, then the parent command should handle the error. But if the executing command has no parent, then the CCommand() class needs to clean itself up by calling the OnRollBack() function. All the OnRollBack() function does is clear any sub-commands that there might be and then resets the count.

You might be thinking that looks like an awful lot of code for a class that really doesn’t do anything. To tell you the truth, this class will allow you to keep consistency between all of your commands. Each time you create a command, that command will inherit all the functions in this class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset