Game Programming Language
Phase 8: Animation Blocks



Overview:

If you have made it this far, you are pretty close to completing the entire interpretor.

(1) Add animation blocks to the parser you created in program 7.  Since your program should already be able to parse a statement block, all you have to do is:

(a) write the action for animation blocks in gpl.y
(b) implement the game_object parameter for animation blocks

(2) Implement the touches and near operators.  This will require small additions to your expression class.

(3) Implement initialization blocks (this is trivial).


Requirements:

By the end of p8 your program must do everything specified in the gpl manual

Your program should run the game you turned in for Phase 2




Hints:

There are two tricky parts to this assignment
  1. Matching the Animation_block created in a forward statement to the Animation_block in an animation declaration
  2. Implementing animation block parameters.
Animation block

Recall in p7 that when parsing a statement block you created a new Statement_block and pushed it onto the statement block stack when you parsed the "{" and when you parsed the "}" you popped it off the stack.  This way each statement you parsed (while inside the block) could be inserted into the Statement_block at the top of the stack.

When parsing the forward statement you created an empty Animation_block and put in into the symbol table.

When you parse the "{" of an animation block, instead of creating a new Statement_block, you need to get the current animation block's Animation_block out of the symbol table and push it on top of the stack.  At the end of an animation block, you pop the Animation_block off the stack.

Since Animation_block inherits Statement_block, there is no problem pushing it onto the stack and using it when you are inserting statements.  In other words, you can code as if you were using a Statement_block * instead of an Animation_block *.

The easiest way to do this is to add an action in the middle of the rule for animation_block:
animation_block:
T_ANIMATION T_ID T_LPAREN check_animation_parameter T_RPAREN
T_LBRACE {put action here} statement_list T_RBRACE end_of_statement_block

In this action you will lookup the animation block in the symbol table (its name is $2).  Then you push it on the statement block stack.  Thus you will be filling in the animation block that was created in the forward statement instead of creating a new one.


Getting parameters to work

Building a mechanism that works like parameter passing in C++ would be difficult, so I came up with a hack.  If you can come up with a cleaner way to implement this, I would like to hear about it.

When an animation block is created by a forward statement, the formal parameter variable is created and placed into the symbol table as if it were a global variable (except it is not drawn or animated).

When an animation block is executed, the actual parameter must replace the formal parameter.

Consider the following gpl code:

forward animation go(rectangle cur_rect);

rectangle my_rect(animation_block = go);

animation go(rectangle cur_rect)
{
cur_rect.x += 10;

}

The formal parameter is cur_rect.  The actual parameter is my_rect.  cur_rect is created and placed in the symbol table when the forward statement is parsed.  When the statements in the animation block are parsed, they reference cur_rect.  Since cur_rect is in the symbol table, you can parse code that contains cur_rect just like the code in on blocks.

When the animation block go is executed for the rectangle my_rect, cur_rect needs to be "replaced" with my_rect.  Ok, here comes the hack:  Before executing the statement block, modify cur_rect's symbol so that it points to my_rect's Game_object.  It sounds like a bad hack, but it is very easy to implement.

In order to implement this:

(1) Make sure you are creating the formal argument when you parse the forward statement and passing it to Animation_block's constructor.

(2) Implement Animation_block::execute(Game_object *argument).  This function should do the following:

(a) modify the symbol for the formal parameter so it points to argument
(b) call Statement_block::execute()
(c) undo the modification you did in step (a)

(3) Implement bool Statement_block::empty() which returns true if there are no statements in the statement block and false otherwise.

Near and touches

near and touches are binary operators that take two Game_objects and return an INT (0 or 1).

Game_object implements the actual functionality, all you have to do is call near() or touches() when you evaluate a binary expression where the operator is near or touches.

You will have to add actions in gpl.y to handle these operators.  Since the operands are always variables, there are minimal changes (i.e. being able to detect the GAME_OBJECT type variable) to the construction of your Expression.  You will have to change Expression::eval_int() to be able to handle these operators.

Initialization blocks

The window manager (window.cpp) makes the following call right before program execution starts:
   event_manager->execute_handlers(Window::INITIALIZE);
This means that all you have to do to implement initialization blocks is to insert the block into the Event_manager when you parse it (in the action for initialization_block).  Just like event blocks, there can be many initialization blocks.

You should not need to change Event_manager::insert() because INITIALIZATION is a keystroke just like SPACE.


Turning in and Testing:

See docs/turnin.html for a description of how to turn in assignments.

Testing works just like in p7 (see p7/p8 section of docs/testing.html) however, you have to wait for the animations to stop before comparing your results to those in the test directory.  

The animation blocks in the p8 tests run for a finite time.  In other words, the animations don't go on forever.