Code snippets 
       

                Stratego: RTS and TBS template    
   
First of all, RTS stands for Real Time Strategy and TBS means Turn Based Strategy, but I guess you knew that already. I have created a game demo that will get you started if you plan to create a strategy game. The demo allows you to: 
- move the camera; 
- create two types of buildings; one produces soldiers, the other one produces nothing (can be used for technology upgrades?) 
- move the units using a primitive path finding system. 
 
I have set the screen resolution to 800x600 pixels and I have set mouse_range to 3000 just to make sure that I can touch and click anything on screen (default = 1000). I'm also using 

var walk_speed = 2; // soldier speed 
var animation_speed = 6; // soldier's animation speed. 

My main function is simple (as always): 

function main() 
{ 
     D3D_PANELS = ON; 
     LOAD_LEVEL (<strat1.wmb>); 
     move_camera(); // new stuff here 
} 

Let's take a look at move_camera: 
 

function move_camera() 
{ 
      level_marginx = 200; // scroll the camera from -200 to 200 
      level_marginy = 200; // on X and Y 
      CAMERA.ARC = 70;  
      CAMERA.X = 0; // build your levels with the starting point in the origin 
      CAMERA.Y = 0; // because at game start the camera will point to the origin 
      CAMERA.Z = camera_height; // change camera_height to get closer or farther to the ground 
      CAMERA.PAN = 90; // set the camera to look down. 
      CAMERA.TILT = -90; 
      CAMERA.ROLL = 0; 
  
      MOUSE_MAP = cursor_map; // choose the cursor and make it appear in the center of the screen 
      MOUSE_POS.X = SCREEN_SIZE.X / 2; 
      MOUSE_POS.Y = SCREEN_SIZE.Y / 2;  
      MOUSE_MODE = 2; 

      WHILE (1) 
      { 
               MOUSE_POS.X = POINTER.X; // mouse movement; 
               MOUSE_POS.Y = POINTER.Y; 

               IF (MOUSE_POS.X < 1 && CAMERA.X > level_marginx * (-1)) {CAMERA.X -= 10 * TIME;} 
               IF (MOUSE_POS.X > SCREEN_SIZE.X - 2 && CAMERA.X < level_marginx) {CAMERA.X += 10 * TIME;} 
               IF (MOUSE_POS.Y > SCREEN_SIZE.Y - 2 && CAMERA.Y > level_marginy * (-1)) {CAMERA.Y -= 10 * TIME;} 
               IF (MOUSE_POS.Y < 1 && CAMERA.Y < level_marginy) {CAMERA.Y += 10 * TIME;} 
               WAIT (1);          
       } 
} 
 
The camera movement code works this way:   

IF (MOUSE_POS.X < 1 && CAMERA.X > level_marginx * (-1))  
{ 
       CAMERA.X -= 10 * TIME; 
} 

If the mouse moves to the left and reaches the left edge (MOUSE_POS.X < 1) and we can move the camera (CAMERA.X > -200) then we move the camera to the left (scroll left). The other 3 IF statements look and work in the same way; we're using SCREEN_SIZE.X and SCREEN_SIZE.Y to get the size of the screen in pixels for the right and bottom edge. We could have used numbers because we already know that we're running in 800x600 pixels, but this way our game is prepared for serious strategy games; the code can read the resolutions and adapt the camera movement automatically. Don't forget that the Y axis is reverted, so if we want to scroll upwards we have to reduce CAMERA.Y to make it work. 

We are adding a panel that creates buildings if it is clicked and plays a nice sound when we're over the buttons using the mouse_over function: 

PANEL main800_pan  
{ 
      BMAP main800_map; 
      LAYER 20; 
      POS_X 0; 
      POS_Y 0; 
      BUTTON 4, 4, building1_map, building1_map, building11_map, create_building1, NULL, mouse_over; 
      BUTTON 4, 65, building2_map, building2_map, building22_map, create_building2, NULL, mouse_over; 
      FLAGS OVERLAY, REFRESH, TRANSPARENT, VISIBLE; 
} 

If we click the building1 button, it will trigger the create_building1 function: 

function create_building1()  
{ 
     WHILE (MOUSE_LEFT == 1 || MOUSE_RIGHT == 1) {WAIT (1);} // wait for mb release 
     WHILE (1) 
     {  
           IF (MOUSE_LEFT == 1) 
           { 
                  build1_pos.X = MOUSE_POS.X; 
                  build1_pos.Y = MOUSE_POS.Y; 
                  build1_pos.Z = camera_height; // place buildings at ground position 
                  vec_for_screen (build1_pos,CAMERA); 
                  CREATE (<build1.wmb>, build1_pos, build1_action); 
                  END; 
           }  
           IF (MOUSE_RIGHT == 1) {END;} // abort if right mouse button is pressed 
           WAIT (1); 
    } 
} 

If you click the building1 button, you could trigger several events, so this line 

WHILE (MOUSE_LEFT == 1 || MOUSE_RIGHT == 1) {WAIT (1);}  

waits for the left mouse button release, then all the stuff goes on. If the lmb is clicked again, we're getting the mouse pointer coordinates in build_pos, converting them to world coordinates with vec_for_screen. We create the building1 (build1.wmb) at pointer position and make it run the build1_action: 

function build1_action() 
{ 
      MY.PUSH = 20; // units (soldiers) have PUSH = 10, building have PUSH = 20 
      WHILE (MOUSE_LEFT == 1) {WAIT (2);} // don't create units by mistake 
      MY.ENABLE_SCAN = ON; // SCAN - sensitive 
      MY.ENABLE_CLICK = ON; 
      MY.EVENT = build_stuff1; // create units when clicked 
      scan_area.PAN = 360; 
      scan_area.TILT = 60; 
      scan_area.Z = 900; 
      vec_set (MY_POS, build1_pos); 
      SCAN MY_POS, MY_ANGLE, scan_area; 
      IF (RESULT > 0 && RESULT < 200) // buildings must be separated by 200 quants or more 
      { 
            PLAY_SOUND cantplacehere_snd, 100;  
            REMOVE ME; 
      }   
      ELSE 
      { 
             PLAY_SOUND buildingplaced_snd, 100;    
      } 
} 

Building1 is sensitive to SCAN and CLICK. When it is placed on the ground, it will start scanning around it. If another building was found nearby (RESULT > 0 means that SCAN has found another building) and it is closer than 200 quants, the newly created building is instantly removed. If there isn't any buiding in the area, the building succedes and a buildingplaced_snd sound is played.  

What happens if any existing building is clicked? It's build_stuff1 event is triggered: 

function build_stuff1() 
{ 
    IF (EVENT_TYPE == EVENT_CLICK) // make sure it's a click 
    { 
         IF (MY.SKILL10 > 2)  
         { 
               END; // 8 positions filled, so stop creating units 
         } 
         IF (MY.SKILL9 == 1 && MY.SKILL10 == 1) // exclude center of the building 
         { 
               MY.SKILL9 += 1; 
         } 
         temp.X = unitstartxpositions[MY.SKILL9] + MY.X; 
         temp.Y = unitstartxpositions[MY.SKILL10] + MY.Y; 
         temp.Z = MY.Z; 
         CREATE (<guard.mdl>, temp, unit1_action); 
         PLAY_SOUND unitcreated_snd, 50; 
         MY.SKILL9 += 1; 
         IF (MY.SKILL9 > 2)  
         { 
               MY.SKILL9 = 0; // next row 
               MY.SKILL10 += 1;  
         } 
    } 
 

Every building can create up to 8 units (soldiers). We have a few arrays to help us do that: 

var unitstartxpositions[3] = -65, 0, 65; // stores unit offsets related to the wmb center 
var unitstartypositions[3] = -65, 0, 65; // we aren't using 0,0 (building center) 

The idea is to place the building1 wmb on the ground; these arrays will help us place the units - look at this picture 
 

 
 
We can place 8 units around the building (3 rows and 3 columns, but we're excluding the center). The figures in the picture are the offsets of the units related to the building. I'm using SKILL9 and 10 for these offsets (the first 8 skills can be accessed in Wed and I thought I should keep them free for your use). When the building is clicked for the first time, SKILL9 and SKILL10 are 0, so the first soldier will have an offset given by unitstartxpositions[0] = -65 and unitstartypositions[0] = -65. The building has the SKILL9 increased by 1 at the second click (second soldier), so the unit will have the offset (0, -65). These things go on and on (excluding (0,0)) until all the units (8 soldiers) have been created. Finally, the most complex function - move_unit - is going to be exposed: 

function move_unit () 
{ 
      MY.PUSH = 10; // lower push for units 
      IF (EVENT_TYPE == EVENT_ENTITY)  // collision with buildings or other units 
      { 
            // caveman's path finding code  
           EXCLUSIVE_ENTITY;  
           MY.PAN = RANDOM (360); 
           WAITT (10); // wait before setting the target again 
           temp.X = MY.SKILL12; // stored mouse pointer coordinates 
           temp.Y = MY.SKILL13; 
           temp.Z = 0; 
           vec_sub (temp, MY.X); 
           vec_to_angle (MY.PAN, temp); // rotate unit towards the target again 
           // remove the lines above if you want to add your path finding code  
       } 
       IF (EVENT_TYPE == EVENT_CLICK) 
       { 
           MY.SKILL11 = 0; 
           WHILE (MOUSE_LEFT == 1) {WAIT (2);} // wait for lmb release 
           WHILE (MY.SKILL11 == 0) // wait for the target to be set (lmb click) 
           { 
                 IF (MOUSE_LEFT == 1) 
                 { 
                       EXCLUSIVE_ENTITY; // stop parallel running actions for this unit 
                       MY.SKILL11 = 1; // target has been set  
                       temp.X = MOUSE_POS.X; 
                       temp.Y = MOUSE_POS.Y; 
                       temp.Z = camera_height; 
                       vec_for_screen (temp, CAMERA); // temp holds pointer's coords now 
                       MY.SKILL12 = temp.X; // store pointer's coords before they get lost 
                       MY.SKILL13 = temp.Y; 
                       vec_sub (temp, MY.X); 
                       vec_to_angle (MY.PAN, temp); // rotate unit towards mouse 
                       MY.SKILL15 = 500; // maximum path length = fuel :)  
                       WHILE (abs(MY.X - MY.SKILL12) + abs(MY.Y - MY.SKILL13) > 3 && MY.SKILL15 > 0) // stops near the mouse pointer  
                       {  
                             move_mode = ignore_you + ignore_passable;  
                             ent_move (walk_speed, nullvector);  
                             ent_cycle("walk", MY.SKILL14);  
                             MY.SKILL14 += animation_speed * TIME; 
                             IF (MY.SKILL14 > 100) {MY.SKILL14 = 0;}  
                             MY.SKILL15 -= TIME; // burn fuel 
                             WAIT (1); 
                       } 
                       ent_cycle("stand", 0); // set the idle frame  
                } 
                WAIT (1); 
          } 
     } 
} 

We'll ignore IF (EVENT_TYPE == EVENT_ENTITY) for now; this happens only if the soldier collides with one of the buildings or other soldiers. To move the soldiers, we click on them (producing an EVENT_CLICK), then we want to click somewhere on the map and we expect to unit to move there. I'm using soldier's SKILL11...SKILL15 in this function; I could have used SKILL9 and 10 because they aren't connected with SKILL9 and 10 used in the building functions, but I wanted to make the things as clear as possible.     
 
When we click the unit, its SKILL11 is set to zero and it will stay this way until we click on the map to set the target. If we have set the target, temp is used to get the mouse pointer coordinates; we're converting them to world positions with vec_for_screen and we're safely storing them in SKILL12 and 13 because temp can suddenly change its value; we already know unit's Z coordinate, so we don't need to store it. 

The unit must rotate towards the mouse then it will start moving, trying to reach its target. My path finding code is really simple, so the unit can "think" too much before finding the right path; this is why I have added "fuel". If a unit can't find its correct path, it will move towards the target until it burns all its fuel 

WHILE (abs(MY.X - MY.SKILL12) + abs(MY.Y - MY.SKILL13) > 3 && MY.SKILL15 > 0) 

will make the unit stop when it reaches the target or when SKILL15 = 0 (it ran out of fuel). I'm using a simple method to check if the unit has reached the target; please remember that SKILL12 and SKILL13 are holding target's coordinates. 
 

 
 
The unit moves towards the target and the "blue" differences become smaller and smaller. When their sum is smaller then 3, the unit stops. 
I have used the new guard.mdl file; it walks with walk_speed set at the beginning and is animated using these lines: 

ent_cycle("walk", MY.SKILL14);  
set the animation frames to the first walk frame (MY.SKILL14 = 0 for now, so the guard is set at the first walking frame) 

MY.SKILL14 += animation_speed * TIME;  
go through all the walk animation frames; SKILL14 contains the current walk frame 

IF (MY.SKILL14 > 100) {MY.SKILL14 = 0;}  
loop the animation 

If the unit has stopped (reached the target or ran out of fuel), it switches to stand mode 

ent_cycle("stand", 0); // set the idle frame = first stand frame 

Did I say this was the latest function? We've got the path finding code to discuss, but it is really simple: 

IF (EVENT_TYPE == EVENT_ENTITY)  // collision with buildings or other units 
      { 
            // caveman's path finding code  
           EXCLUSIVE_ENTITY;  
           MY.PAN = RANDOM (360); 
           WAITT (10); // wait before setting the target again 
           temp.X = MY.SKILL12; // stored mouse pointer coordinates 
           temp.Y = MY.SKILL13; 
           temp.Z = 0; 
           vec_sub (temp, MY.X); 
           vec_to_angle (MY.PAN, temp); // rotate unit towards the target again 
           // remove the lines above if you want to add your path finding code  
       } 
................................................................................. 

If the unit collides with something, it picks a random direction and moves for a little more than 1/2 second, then it "reads" the right direction from SKILL12 and 13 and tries to reach the target again. 

There are many things that you should add to this code to make it perfect ;) 
- Better path finding code; 
- The buildings can't be placed one over the other, but you can place buidings on top of isolated units and that's wrong. I wouldn't use SCAN for every unit because it would slow down the game in theory (I did that with the buildings, but there aren't too many of them in a level). I would store all the unit positions in an array and I would compare building's coordinates with all the unit coordinates stored in the array. The array could be used to improve the path finding algorithm, too. 
- Etc. 

Please note that I have used a regular model with 644 polygons for the soldier. I wouldn't do that if I were you - with 100 models visible on screen at the same time, you'll slow down the engine and you don't want that. 
 
 

          SCRAP - SCReen cAPture code     

If you are like me, you have looked at the METAL movie in "Hot features" and you're wondering: how did he do that movie? He must have used two PCs and a great capture card... This is a good method and I have successfully used it for my first Takeover movies, but then I thought that I should use a single PC and I came up with this simple scrap code. 

First of all, I'd like to say that I heard somebody at the forum speaking about this and it inspired me (I can't find the post anymore, maybe it got deleted by mistake). The idea was to use SCREENSHOT every time the player was moving and save the shots on the hard drive, then assemble a movie using the screenshots. Scrap works in a different way, but uses the same idea. 
 
You'll have to INCLUDE scrap.wdl in your game file; don't forget to insert scrap_movie(); in main to call the scrap function, 

var Count = 11000; // just a number bigger than 10000 
var Count1 = 0; 
var Count2 = 0; 
var Count3 = 0; 
var Count4 = 0; 
var Count5 = 0; 
var Count6 = 0; 
var Count7 = 0; 
var Count8 = 0; 
var Count9 = 0; 

function scrap_movie () 
{ 
    WHILE (1) 
    { 
          IF (Count == 11000 && KEY_M == 1) {WAIT (2); Count = 0;} 
          WHILE (KEY_M == 1) {WAIT (1);} 
          WHILE (count < 10000) 
          { 
                  Count1 = Count - 1000; 
                  Count2 = Count - 2000; 
                  Count3 = Count - 3000; 
                  Count4 = Count - 4000; 
                  Count5 = Count - 5000; 
                  Count6 = Count - 6000; 
                  Count7 = Count - 7000; 
                  Count8 = Count - 8000; 
                  Count9 = Count - 9000; 

                  IF (Count < 10) {screenshot "Mv000",Count;} 
                  IF (Count >= 10 && Count < 100) {screenshot "Mv00",Count;} 
                  IF (Count >= 100 && Count < 1000) {screenshot "Mv0", Count;} 
                  IF (Count >= 1000 && Count < 1010) {screenshot "Mv100", Count1;} 
                  IF (Count >= 1010 && Count < 1100) {screenshot "Mv10", Count1;} 
                  IF (Count >= 1100 && Count < 2000) {screenshot "Mv1", Count1;} 

                  IF (Count >= 2000 && Count < 2010) {screenshot "Mv200", Count2;} 
                  IF (Count >= 2010 && Count < 2100) {screenshot "Mv20", Count2;} 
                  IF (Count >= 2100 && Count < 3000) {screenshot "Mv2", Count2;} 

                 .......................................................................... 
                 // repeats for Count = 3000...8000  

                 IF (Count >= 9000 && Count < 9010) {screenshot "Mv900", Count9;} 
                 IF (Count >= 9010 && Count < 9100) {screenshot "Mv90", Count9;} 
                 IF (Count >= 9100 && Count < 10000) {screenshot "Mv9", Count9;} 

                Count += 1;  
                IF (KEY_M == 1) {WAIT (2); Count = 11000;} 
                WHILE (KEY_M == 1) {WAIT (1);} 
                WAIT (1); 
                } 
     WAIT (1); 
     } 
} 

We're using Count to start or stop the movie when the "M" key is pressed; if Count is smaller than 10000, the shots are saved on your hard drive; to stop this process, we're setting Count to 11000 (any number greater than 10000 is a valid Count number).  

SCREENSHOT can save up to 999 shots so if your movie is going to have 25 fps, you're limited to 999 / 25 = 40 seconds. I have added a few variables (Count1...Count9) that are used to expand the screenshots to 9999 shots = 7 minutes with 25 fps. 

When you press M, you'll notice a slowdown - that's normal because the shots are saved on your hard drive. When you want to finish the capture, press M again. Exit the game and move to the shots folder - you can create one like this:  

SAVEDIR "C:\\SHOTS";  

If you don't use a line like this in your main file, the shots will be saved in your working folder, mixing with the rest of your files. 

Please take a look at the shots; their names start with mv0000.pcx and can continue to mv3201.pcx for example. Use any program that can convert pcx files to movies (Animation Shop that comes with Paint Shop Pro is a good example), then save the movie as avi, mpg, etc. 

Use small resolutions for your movies; they'll look great anyway because they're ran in small windows, not in full screen. I use 320x200 all the time and this is the way I've created the METAL movie, too. Just change the screen resolution to 320x200 in your game, then press M to start the capture. If you've seen some game preview movies, you've noticed they all come in small sized windows.  

I ran a test on two of my computers: Athlon 900 MHz, 60 GB Quantum Fireball Plus AS 7200 rpm and Athlon 750 MHz, 13 GB Quantum Fireball 4400 rpm. Scrap saves around 25 fps on the system with the fast hard drive and 15 fps for the second PC. If you get more than 8 fps, you can create movies - the movies on my site have 15 fps and a decent size.  

If you have a good processor with a fast hard drive, you can create 400x300 pixels movies, but their size is too big unless you make a 2 second movie. Intel PIII 1000 MHz with 60GB QFB Plus AS 7200 rpm will deliver 15 fps in 400x300.