TGB/Tutorials/Fill Battle/Creating The Game

From TDN

This page is a Work In Progress.

Contents

Defining the Game Flow, Part 1

To make the game, we’ll need to start thinking about what happens. When the user selects a color, we need to change the upper left-hand corner to that color and all other tiles adjacent to it that also match the color. This logic is a little tricky. What we are going to use here is a stack.

What Is A Stack?

(You can skip this section if you don’t care about what a stack is and does.)

A stack is a data structure where the last thing put on it is the first thing that comes off. (You can go to Wikipedia to learn gory details.)

Imagine it as a stack of note cards. We can write on a note card and put it on the stack. We can also take a note card off the stack and read it. In TorqueScript, you can write it like this:

function Stack::onAdd( %this )
{
  %this.len = 0;
}

function Stack::push( %this, %val )
{
  %this.v[%this.len] = %val;
  %this.len++;
}

function Stack::pop( %this )
{
  if( %this.len == 0 )
  {
    error( "Stack Underflow." );
    return "";
  }
  %this.len--;
  return %this.v[%this.len];
}

Whenever we create a new stack, the “::onAdd” function gets called. So if we did this:

%locations = new Stack();

Then “::onAdd” would get called where “%this” would be equal to “%locations”. It would then set the length of the stack (or height) to zero.

We can then “push” a card onto the top of the stack or we can “pop” the top-most card off of the stack and read what’s on it. So if we did this:

%locations = new Stack();
%locations.push( 1 );
%locations.push( 2 );
%a = %locations.pop();
%b = %locations.pop();

Then “%a” would equal “2” and “%b” would equal “1”.

We’re using some TorqueScript magic here by dynamically creating variables. “len” is the size of the stack. “v” is an array that has the cards. So “v[0]” is the bottom card, “v[1]” is on top of that, “v[2]” is on top of “v[1]”, and so on.

When we “push” a card onto the stack, we put it at “v[0]” and then increase the length/height of the stack by 1. The next push will then put the card at “v[1]” and then increase the length/height by 1.

“pop” does the opposite. It reduces the length/height by 1 and returns the top-most card. So if our height was 1 and we called “pop”, we’d reduce the length/height to 0 and return “v[0]”.

We do a little error checking in “pop”. If our length/height is zero, we return a blank string and print out an error message into the console.

Defining the Game Flow, Part 2

Here’s the general flow we’re trying to create:

  • The user clicks on a color which we call “newColor”.
  • We remember the original upper left-hand corner’s color and call it “oldColor”.
  • We create a string, “0 0” to represent the upper left-hand tile. The first word of the string is the x-coordinate; the second, the y-coordinate.
  • We push this string onto the stack.
  • While our stack has any size...
    • Remove and read the top-most card.
    • Get the first word on that card and call it “xCoord”.
    • Get the second word on that card and call it “yCoord”.
    • Change <xCoord,yCoord> to the “newColor”.
      • NOTE: It should equal the oldColor, but that check is done in the next step.
    • Check all of the tiles adjacent to <xCoord,yCoord>
      • If any are equal to the “oldColor”, push it onto the stack.
  • Our stack should be empty now, so we are done!

Scripting the Game Flow

So how does that look in code? Look at the modified Button code below (read the comments carefully):

function Stack::onAdd( %this )
{
  %this.len = 0;
}

function Stack::push( %this, %val )
{
  %this.v[%this.len] = %val;
  %this.len++;
}

function Stack::pop( %this )
{
  if( %this.len == 0 )
  {
    error( "Stack Underflow." );
    return "";
  }
  %this.len--;
  return %this.v[%this.len];
}

function Button::onMouseUp( %this, %modifier, %worldPosition, %clicks )
{
  // Used to check the boundaries of the tile map
  %width = TileBoard.getTileCountX();
  %height = TileBoard.getTileCountY();
  
  // Read the button to get the color that the user clicked on.
  %newColor = %this.getFrame(); // or use %this.color;

  // This returns the string "static IMAGEMAP FRAME#".
  // Word 0 is "static".
  // Word 1 is the ImageMap.
  // Word 2 is the frame number.  
  %upperLeftTileType = TileBoard.getTileType( 0, 0 );
  %oldColor = getWord( %upperLeftTileType, 2 );

  // Create the stack.
  %locStack = new ScriptObject() { class = Stack; };
  
  // Put the upper left-hand corner onto the stack.
  %locStack.push( "0 0" );
  
  while( %locStack.len > 0 ) // While there are ANY cards on the stack
  {
    // Remove and read the top card.
    %topCard = %locStack.pop();
    
    // Get the x-coordinate and y-coordinate off the text on that card.
    %xCoord = getWord( %topCard, 0 );
    %yCoord = getWord( %topCard, 1 );
    
    // Change that location to the new color.
    TileBoard.setStaticTile( %xCoord, %yCoord, SixColorsImageMap, %newColor );
    
    // If we aren't on the left-hand side of the board
    if( %xCoord > 0 )
    {
      // Look at the tile to the left of us.
      %leftTileType = TileBoard.getTileType( %xCoord - 1, %yCoord );
      %leftColor = getWord( %leftTileType, 2 );
      
      // If it matches the old color, put it's location on the stack.
      if( %leftColor == %oldColor )
      {
        // If %xCoord were 5 and %yCoord were 7, then we'd create a new
        // string by putting together the number 4 with a space (SPC) and
        // the number 7.  This would result in "4 7" to be written on the card.
        %locStack.push( (%xCoord - 1) SPC %yCoord );
      }
    }
    
    // If we aren't on the right-hand side of the board
    if( %xCoord < %width - 1 )
    {
      // Look at the tile to the right of us.
      %rightTileType = TileBoard.getTileType( %xCoord + 1, %yCoord );
      %rightColor = getWord( %rightTileType, 2 );
      
      // If it matches the old color, put it's location on the stack.
      if( %rightColor == %oldColor )
      {
        %locStack.push( (%xCoord + 1) SPC %yCoord );
      }
    }
    
    // If we aren't at the top of the board...
    if( %yCoord > 0 )
    {
      %upTileType = TileBoard.getTileType( %xCoord, %yCoord - 1 );
      %upColor = getWord( %upTileType, 2 );
      if( %upColor == %oldColor )
      {
        %locStack.push( %xCoord SPC (%yCoord - 1) );
      }
    }
    
    // If we aren't at the bottom of the board...
    if( %yCoord < %height - 1 )
    {
      %downTileType = TileBoard.getTileType( %xCoord, %yCoord + 1 );
      %downColor = getWord( %downTileType, 2 );
      if( %downColor == %oldColor )
      {
        %locStack.push( %xCoord SPC (%yCoord + 1) );
      }
    }
  }

  %locStack.delete();
}

That’s it! Save, run, and play! The game’s complete!

Complete!

Return to the Fill Battle Home!