Controlling Players and Characters(16)

Controlling Players and Characters(16)

 

Creating a Derived Script Class

I’m going to assume that you are comfortable working with action templates and
scripts at this point. The following action template provides an example of using a
derived script class:

“End”
“If flag ~ equals ~ then”
  INT 0 255
  BOOL
“Else”
“EndIf”
“Set flag ~ to ~”
  INT 0 255
  BOOL
“Print ~”
TEXT

Now, using the preceding action template, include the following
script (I list it in text form here to make it easier to understand):

If flag 0 equals TRUE then
  Print “Flag is TRUE”
  Set flag 0 to FALSE
Else
  Print “Flag is FALSE”
  Set flag 0 to TRUE
EndIf

A brief reading shows that the preceding script displays the message “Flag is FALSE”
first (because all script flags are reset to FALSE when initialized); when executed
again, the script displays “Flag is TRUE”.

CAUTION
Remember that the example script shown here is in text form, but when used
as a Mad Lib Script, the format is based on values. For example, the if...then
action is represented by the value 1, whereas the EndIf action uses the value 3.

 

The Derived Class
 

The next step to processing the script is to derive a class from cScript:

class cGameScript : public cScript
{
private:
  BOOL m_Flags[256]; // The internal flags

  // The script function prototypes
  sScript *Script_End(sScript*);
  sScript *Script_IfFlagThen(sScript*);
  sScript *Script_Else(sScript*);

  sScript *Script_EndIf(sScript*);
  sScript *Script_SetFlag(sScript*);
  sScript *Script_Print(sScript*);

  // The overloaded process function
  sScript *Process(sScript *Script);

public:
  cGameScript();
};

The derived class shown here (cGameScript) uses an array of BOOL values that represents
the internal flags the scripts can use. Following the single variable declaration
is a list of function prototypes.


The script function prototypes are the bread and butter of the script processor.
Each script action has an associated function that is called during the Process function.
The Process function is overridden to call upon those script functions, as you
soon see in this section.

Aside from those private function calls, there is the constructor, which clears the
m_Flags array to all FALSE values.

cGameScript::cGameScript()
{
  // Clear all internal flags to FALSE
  for(short i=0;i<256;i++)
    m_Flags[i] = FALSE;
}

Jumping back a bit, take a look at the overridden Process function. As you can see
from the following code, cGameScript::Process takes only the current script action
type and jumps to the appropriate function. Upon the return of each action function,
a pointer to the next script action is returned. If a value of NULL is returned,
script execution halts.

sScript *cGameScript::Process(sScript *Script)
{
  // Jump to function based on action type
  switch(Script->Type) {
    case 0: return Script_End(Script);
    case 1: return Script_IfFlagThen(Script);
    case 2: return Script_Else(Script);
    case 3: return Script_EndIf(Script);
    case 4: return Script_SetFlag(Script);
    case 5: return Script_Print(Script);
  }

  return NULL; // Error executing
}

Now that you’ve overridden the Process function (and filled in the switch statement
with the action’s function calls), you can continue by programming all of the actions’
functions, as follows:

sScript *cGameScript::Script_End(sScript *Script)
{
  return NULL; // Force script to stop processing
}

sScript *cGameScript::Script_IfFlagThen(sScript *Script)
{
  BOOL Skipping; // Flag for if...then condition

  // See if a flag matches second entry
  if(m_Flags[Script->Entries[0].lValue % 256] == Script->Entries[1].bValue)
    Skipping = FALSE; // Don’t skip following actions
  else
    Skipping = TRUE; // Skip following actions

  // At this point, Skipping states if the script actions
  // need to be skipped due to a conditional if..then statement.
  // Actions are further processed if skipped = FALSE, looking
  // for an else to flip the skip mode, or an endif to end
  // the conditional block.

  Script = Script->Next; // Go to next action to process

  while(Script != NULL) {
    // if else, flip skip mode
    if(Script->Type == 2)
      Skipping = (Skipping == TRUE) ? FALSE : TRUE;

    // break on end if
    if(Script->Type == 3)
      return Script->Next;

    // Process script function in conditional block
    // making sure to skip actions when condition not met.
    if(Skipping == TRUE)
      Script = Script->Next;
    else {
      if((Script = Process(Script)) == NULL)
      return NULL;
    }
  }

  return NULL; // End of script reached
}

sScript *cGameScript::Script_Else(sScript *Script)
{
  return Script->Next; // Go to next script action
}

sScript *cGameScript::Script_EndIf(sScript *Script)
{
  return Script->Next; // Go to next script action
}

sScript *cGameScript::Script_SetFlag(sScript *Script)
{
  // Set boolean value
  m_Flags[Script->Entries[0].lValue % 256] = Script->Entries[1].bValue;

  return Script->Next; // Go to next script action
}

sScript *cGameScript::Script_Print(sScript *Script)
{
  // Display some text in a message box
  MessageBox(NULL, Script->Entries[0].Text, “Text”, MB_OK);

  return Script->Next; // Go to next script action
}

 

Using the Derived Class


To test the cGameScript class, instance it and run the example script that I showed
you earlier in the section “Creating a Derived Script Class.” Assuming that you
saved that script to a file named test.mls, the following example shows the script
class functionality:

cGameScript Script;

Script.Execute(“test.mls”); // Prints a Flag is FALSE message

// At this point, the script’s internal flags are maintained,
// so the next call would take the new flag states into account.
Script.Execute(“test.mls”); // Prints a Flag is TRUE message

Although this is a quick and dirty example of the derived cGameScript class, there really
isn’t much difference between this class and a full-fledged script parser that uses a huge
action template. You merely need to add each action-processing function into the class
and call that function via the Parse function.


你可能感兴趣的:(Controlling Players and Characters(16))