1. /*
  2.  Author: Jeff Yaskus
  3.  Adapated heavily from below ...
  4.  
  5.  Beginning Game Programming, 2nd Edition
  6.  Chapter 8
  7.  Tiled_Sprite program source code file
  8. */
  9.  
  10. #include "game.h"
  11.  
  12. // default background map
  13. int MAPDATA[MAPWIDTH*MAPHEIGHT] =
  14. {
  15.      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  16.      0,1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,0,
  17.      0,1,0,1,1,1,0,1,1,1,0,0,1,0,1,0,0,0,1,0,
  18.      0,1,0,0,0,0,0,1,0,1,0,0,1,1,1,1,0,0,1,0,
  19.      0,1,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,0,
  20.      0,1,0,0,1,0,0,1,0,0,0,0,1,1,1,1,0,0,1,0,
  21.      0,1,0,0,1,0,1,1,1,1,1,1,1,0,0,1,0,0,1,0,
  22.      0,1,1,1,1,1,1,0,0,1,0,0,0,0,0,1,0,1,1,0,
  23.      0,0,1,0,1,0,0,0,1,1,1,1,0,1,1,1,1,1,0,0,
  24.      0,0,1,0,1,1,1,1,1,2,3,1,1,1,0,0,0,1,0,0,
  25.      0,0,1,0,0,0,0,0,1,4,5,1,0,0,0,1,1,1,1,0,
  26.      0,1,1,1,1,1,1,0,1,1,1,1,0,1,1,1,0,0,1,0,
  27.      0,1,0,0,0,0,1,0,1,0,1,0,0,1,0,1,1,1,1,0,
  28.      0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,0,0,1,0,
  29.      0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1,0,1,1,0,
  30.      0,0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,1,1,0,0,
  31.      0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,0,0,1,1,0,
  32.      0,1,0,0,0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,0,
  33.      0,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,1,1,0,
  34.      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  35. };
  36.  
  37. // used for displaying the sprites
  38. LPD3DXSPRITE sprite_handler;
  39.  
  40. // score
  41. SCOREtype score;
  42.  
  43. // overlay tiles
  44. LPDIRECT3DTEXTURE9 tiles;
  45.  
  46. // DX font
  47. LPD3DXFONT dxfont;
  48. string tStr; // for text output
  49.  
  50. //timing variable
  51. const int FRAMES_PER_SECOND = 30;
  52. int Frames;
  53. int Death_Delay;
  54. HRESULT result;
  55. long start;
  56.  
  57. // used for tracking the "dots"
  58. int dotList[MAPWIDTH][MAPHEIGHT];
  59.  
  60. // arrays used for pathfinding
  61. bool inList[MAPWIDTH][MAPHEIGHT];
  62. listType Queue[MAX_PATHS];
  63.  
  64. // these are used after path found, to determine which to take
  65. int goN = MAX_PATHS;
  66. int goS = MAX_PATHS;
  67. int goE = MAX_PATHS;
  68. int goW = MAX_PATHS;
  69.  
  70. // these are used during the path finding routines
  71. int cnt = 0;
  72. int tCnt = 0;
  73.  
  74. // UNITs
  75. CSprite *enemy[NUM_ENEMY];
  76. CSprite *player;
  77. CSprite *eyeballs;
  78. CSprite *eatable;
  79.  
  80. // "dots" for bunny to eat
  81. CSprite *carrot;
  82. CSprite *apple;
  83.  
  84. // power pill timer
  85. unsigned long long ppStart=0;
  86. unsigned long long ppEnd=0;
  87. bool ppActive = false;
  88.  
  89. int LIVES = 3;
  90.  
  91. // open file to log errors
  92. fstream errorLog("errorLog.txt",ios::out);
  93.  
  94. POINT corners[4] = { {1,1}, {18,1}, {1,18}, {18,18} };
  95.  
  96. // ----------------------- GAME FUNCTIONS ----------------------- //
  97.  
  98. //initializes the game
  99. int Game_Init(HWND hwnd)
  100. {
  101.      //set random number seed
  102.      srand((unsigned int)time(NULL));
  103.  
  104.      //initialize mouse
  105.      if (!Init_Mouse(hwnd))
  106.      {
  107.           MessageBox(hwnd, "Error initializing the mouse", "Error", MB_OK);
  108.           return 0;
  109.      }
  110.  
  111.      //initialize keyboard
  112.      if (!Init_Keyboard(hwnd))
  113.      {
  114.           MessageBox(hwnd, "Error initializing the keyboard", "Error", MB_OK);
  115.           return 0;
  116.      }
  117.  
  118.      //create sprite handler object
  119.      result = D3DXCreateSprite(d3ddev, &sprite_handler);
  120.      if (result != D3D_OK)
  121.           return 0;
  122.  
  123.      // load the font
  124.      D3DXCreateFont( d3ddev, 14, 0, FW_NORMAL, 1, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Comic Sans MS", &dxfont);
  125.  
  126.      // load units
  127.      enemy[0] =  new CSprite(0,0,32,32,"avatar1.bmp");
  128.      assert(enemy[0] != NULL);
  129.      enemy[0]->setTileXY(1,1); // starting location
  130.      enemy[0]->setHome(9,9);
  131.      enemy[0]->setSTATE(SPAWN); // move towards target
  132.      enemy[0]->setFacing(SOUTH);
  133.      enemy[0]->setHasBody(false);
  134.  
  135.      if (NUM_ENEMY > 1)
  136.      {
  137.           enemy[1] =  new CSprite(0,0,32,32,"avatar2.bmp");
  138.           assert(enemy[1] != NULL);
  139.           enemy[1]->setTileXY(18,1);
  140.           enemy[1]->setHome(10,9);
  141.           enemy[1]->setSTATE(SPAWN); // move towards target
  142.           enemy[1]->setFacing(SOUTH);
  143.           enemy[1]->setHasBody(false);
  144.      }
  145.  
  146.      if (NUM_ENEMY > 2)
  147.      {
  148.           enemy[2] =  new CSprite(0,0,32,32,"avatar3.bmp");
  149.           assert(enemy[2] != NULL);
  150.           enemy[2]->setTileXY(1,18);
  151.           enemy[2]->setHome(9,10);
  152.           enemy[2]->setSTATE(SPAWN); // move towards target
  153.           enemy[2]->setFacing(NORTH);
  154.           enemy[2]->setHasBody(false);
  155.      }
  156.  
  157.      if (NUM_ENEMY > 3)
  158.      {
  159.           enemy[3] =  new CSprite(0,0,32,32,"avatar4.bmp");
  160.           assert(enemy[3] != NULL);
  161.           enemy[3]->setTileXY(18,18);
  162.           enemy[3]->setHome(10,10);
  163.           enemy[3]->setSTATE(SPAWN); // move towards target
  164.           enemy[3]->setFacing(NORTH);
  165.           enemy[3]->setHasBody(false);
  166.      }
  167.  
  168.      player =  new CSprite(0,0,32,32,"bunny.bmp");
  169.      assert(player != NULL);
  170.      player->setupAnim(4,WEST,4);
  171.      player->setDelay(5);
  172.      player->setTileXY(9,15);
  173.      player->setSTATE(ALIVE); // move towards target
  174.  
  175.      carrot = new CSprite(0,0,32,32,"carrot.bmp");
  176.      assert(carrot != NULL);
  177.      apple  = new CSprite(0,0,32,32,"apple.bmp");
  178.      assert(apple != NULL);
  179.      eyeballs = new CSprite(0,0,32,32,"eyeballs.bmp");
  180.      assert(eyeballs != NULL );
  181.      eatable = new CSprite(0,0,32,32,"eatable.bmp");
  182.      assert(eatable != NULL );    
  183.  
  184.      //sprites for tile background
  185.      tiles = LoadTexture("tiles.bmp", D3DCOLOR_XRGB(255,0,255));
  186.      assert(tiles  != NULL);
  187.  
  188.      //reset timing
  189.      start = GetTickCount();
  190.      Frames = 0;
  191.      Death_Delay = 0;
  192.  
  193.      // initialize array to keep track of dots
  194.      for (int y=0; y < MAPHEIGHT; y++)
  195.           for (int x=0; x < MAPWIDTH; x++)
  196.                if ( getMap(x,y) == 1)
  197.                     dotList[x][y] = 1;
  198.                else
  199.                     dotList[x][y] = 0;
  200.  
  201.      // assign the power up dots
  202.      dotList[1][1]=2;
  203.      dotList[18][1]=2;
  204.      dotList[1][18]=2;
  205.      dotList[18][18]=2;
  206.  
  207.      // set the score
  208.      score.carrots = 0;
  209.      score.deaths  = 0;
  210.    
  211.      //return okay
  212.      return 1;
  213. }
  214.  
  215. static long frame_cnt = 0;
  216. //the main game loop
  217. void Game_Run(HWND hwnd)
  218. {
  219.      //make sure the Direct3D device is valid
  220.      if (d3ddev == NULL)
  221.           return;
  222.  
  223.      //update mouse and keyboard
  224.      Poll_Keyboard();
  225.      Poll_Mouse();
  226.  
  227.      if (Key_Down(DIK_ESCAPE))
  228.           PostMessage(hwnd, WM_DESTROY, 0,0);
  229.  
  230.      //this keeps the game running at a steady frame rate
  231.      if (GetTickCount() - start >= (1000 / FRAMES_PER_SECOND))
  232.      {
  233.           //reset timing
  234.           start = GetTickCount();
  235.  
  236.           // for debug purposes
  237.           frame_cnt++;
  238.  
  239.           // Collision checks
  240.           // player vs dots
  241.           int dots_remaining=0;
  242.           for (int y=0; y < MAPHEIGHT; y++)
  243.                for (int x=0; x < MAPWIDTH; x++)
  244.                     if (dotList[x][y] > 0)
  245.                     {
  246.                          // carrot rect is only 30x30 for these purposes
  247.                          RECT r1;
  248.                          r1.left   = (x*32) + 12;
  249.                          r1.top    = (y*32) + 12;
  250.                          r1.right  = r1.left + 8;
  251.                          r1.bottom = r1.top + 8;
  252.  
  253.                          RECT r2 = player->getWindowRect();
  254.  
  255.                          if ( rectCollision(r1, r2) )
  256.                          {
  257.                               if ( dotList[x][y] == 2)
  258.                               {
  259.                                    // power pill was eaten
  260.                                    ppStart = GetTickCount();
  261.                                    ppEnd   = ppStart + 6000;
  262.                                    ppActive = true;
  263.                                    player->setSpeed(4);
  264.                               }
  265.                               // yummy eats
  266.                               dotList[x][y] = 0;
  267.                               score.carrots++;
  268.                          }
  269.                          else
  270.                               dots_remaining++;
  271.                     }
  272.  
  273.           // see if "all" the dots got eatean
  274.           if (dots_remaining < 1)
  275.           {
  276.                MessageBox(hwnd, "You won! Those carrots were yummy, huh?!", "You win - Game Over", MB_OK);
  277.                exit(0);
  278.           }
  279.  
  280.           // check if player is dead
  281.           // update player sprite
  282.           if ( player->getSTATE() == DEAD )
  283.           {
  284.                player->setSTATE(ALIVE);
  285.                player->setFacing(SOUTH);
  286.  
  287.                // deduct a life
  288.                LIVES--;
  289.  
  290.                if (LIVES > 0)
  291.                {
  292.                    MessageBox(hwnd, "You died! But still had a life left ... keep trying.", "Game Over", MB_OK);
  293.                    // likely lost keyboard, try to re-aquire
  294.                    Init_Keyboard(hwnd);
  295.                }
  296.                else
  297.                {
  298.                     MessageBox(hwnd, "Sorry, you died! Poor Rabbit ... no more lives left.", "Game Over", MB_OK);
  299.                     exit(0);                      
  300.                     // exit from game
  301.                }              
  302.  
  303.                // if they killed player, they should move away - so he has a chance to get back up
  304.                // a time is displayed in middle of the screen, giving about 60s to recover
  305.                for (int a=0; a < NUM_ENEMY; a++)
  306.                {
  307.                     POINT home = enemy[a]->getHome();
  308.  
  309.                     enemy[a]->setTileXY(home.x,home.y);
  310.  
  311.                     // respawn enemies at "base"
  312.                     enemy[a]->setSTATE( SPAWN );
  313.                }  
  314.           }
  315.  
  316.           // player and enemies
  317.           // but only bother if player not already "dying"      
  318.           // ... while we are at it, check if a power pill is active
  319.           for (int a=0; a < NUM_ENEMY; a++)
  320.           {
  321.                RECT r1 = enemy[a]->getWindowRect();
  322.                RECT r2 = player->getWindowRect();
  323.  
  324.                // check if power pill was eaten
  325.                if ( ppActive )
  326.                {
  327.                     ppStart = GetTickCount();
  328.  
  329.                     if (ppStart > ppEnd)
  330.                     {
  331.                          ppActive = false;
  332.                          player->setSpeed(2);
  333.                     }
  334.                }
  335.  
  336.                // if they are in SPAWN state, pill won't affect them
  337.                // but will catch them once they go back into CHASE mode
  338.                switch(enemy[a]->getSTATE())
  339.                {
  340.                case SPAWN:
  341.                     // ignore if SPAWN state
  342.                     break;
  343.                case EVADE:
  344.                     if (! ppActive )
  345.                          enemy[a]->setSTATE(CHASE);
  346.                     break;
  347.                case CHASE:
  348.                     if ( ppActive)
  349.                          enemy[a]->setSTATE(EVADE);
  350.                     break;
  351.                }
  352.                
  353.  
  354.                // make sure player is alive, ghost is chasing him and it has a body ... then check for collision
  355.                if ( player->getSTATE() == ALIVE && enemy[a]->getSTATE() == CHASE )
  356.                     if ( rectCollision(r1, r2) )
  357.                     {
  358.                          // re-orient player in case "died" in middle of square
  359.                          POINT cT = player->getTile();
  360.                          player->setTileXY(cT.x,cT.y);
  361.  
  362.                          // turn south and "die"
  363.                          player->setFacing(SOUTH);
  364.                          player->setSTATE(DEAD);
  365.                          player->setMoveXY(0,0);
  366.                          player->setMoving(false);
  367.                          player->setFrame(0);
  368.                     }
  369.  
  370.                // make sure player is alive, ghost is chasing him and it has a body ... then check for collision
  371.                if ( player->getSTATE() == ALIVE && enemy[a]->getSTATE() == EVADE )
  372.                     if ( rectCollision(r1, r2) )
  373.                     {
  374.                          // turn into eye balls and head back to respawn
  375.                          enemy[a]->setHasBody(false);
  376.                          enemy[a]->goHome();
  377.                          enemy[a]->setSTATE(SPAWN);
  378.                          score.deaths++;
  379.                     }
  380.           }
  381.  
  382.           // update player sprite
  383.           if (player->isBetweenTiles())
  384.           {
  385.                player->nextFrame();
  386.                player->doMove();
  387.           }
  388.           else
  389.           {
  390.                // stop in each square
  391.                player->setFrame(0);
  392.                player->setMoveXY(0,0);
  393.           }
  394.  
  395.           // check for player input
  396.           POINT cPT = player->getTile();
  397.  
  398.           errorLog << "------- player state = " << player->getSTATE() << " and the bool between tiles is " << player->isBetweenTiles() << endl;
  399.  
  400.           // only allow movement if ALIVE and not between tiles
  401.           if ( (player->getSTATE() == ALIVE) && (! player->isBetweenTiles()) )
  402.                if (Key_Down(DIK_UP))
  403.                {// move NORTH                  
  404.                     // first see if valid move              
  405.                     if ( getMap( cPT.x, cPT.y-1 ) == 1 )
  406.                     {              
  407.                          player->setMoveXY(0,0);
  408.                          player->setFacing(NORTH);
  409.                          player->setMoving(true);
  410.                          player->doMove();
  411.                     }
  412.                }
  413.                else if (Key_Down(DIK_DOWN))
  414.                {
  415.                     // first see if valid move              
  416.                     if ( getMap(cPT.x,cPT.y+1) == 1 )
  417.                     {              
  418.                          player->setMoveXY(0,0);
  419.                          player->setFacing(SOUTH);
  420.                          player->setMoving(true);
  421.                          player->doMove();
  422.                     }
  423.                }
  424.                else if (Key_Down(DIK_RIGHT))   
  425.                {
  426.                     // first see if valid move              
  427.                     if ( getMap(cPT.x+1,cPT.y) == 1 )
  428.                     {              
  429.                          player->setMoveXY(0,0);
  430.                          player->setFacing(EAST);
  431.                          player->setMoving(true);
  432.                          player->doMove();
  433.                     }
  434.                }
  435.                else if (Key_Down(DIK_LEFT))
  436.                {
  437.                     // first see if valid move              
  438.                     if ( getMap(cPT.x-1,cPT.y) == 1 )
  439.                     {              
  440.                          player->setMoveXY(0,0);
  441.                          player->setFacing(WEST);
  442.                          player->setMoving(true);
  443.                          player->doMove();
  444.                     }
  445.                }
  446.                else if (Key_Down(DIK_NUMPAD5))
  447.                {
  448.                     player->setMoveXY(0,0);
  449.                     player->setMoving(false);
  450.                     player->setFrame(0);
  451.                     player->doMove();
  452.                }
  453.  
  454.                // check AI states
  455.                checkAI();
  456.      }
  457.  
  458.      /* ----- DRAW WINDOW ----- */
  459.  
  460.      //start rendering
  461.      if (d3ddev->BeginScene())
  462.      {
  463.           //erase the entire background
  464.           d3ddev->ColorFill(backbuffer, NULL, D3DCOLOR_XRGB(0,0,0));
  465.  
  466.           //start sprite handler
  467.           sprite_handler->Begin(D3DXSPRITE_ALPHABLEND);
  468.  
  469.           // draw tiles to back buffer -- using textures so it uses transparency properly
  470.           DrawTilesBack();
  471.  
  472.           // draw "dots" -- carrots and apples
  473.           for (int y=0; y < MAPHEIGHT; y++)
  474.                for (int x=0; x < MAPWIDTH; x++)
  475.                {
  476.                     if (dotList[x][y] == 1)
  477.                     {
  478.                          POINT currPT = {x,y};
  479.                          int startX = currPT.x * 32;
  480.                          int startY = currPT.y * 32;          
  481.                          DrawSprite(carrot->getImage(),0,32,32,1,startX,startY);
  482.                     }
  483.                     else if (dotList[x][y] == 2)
  484.                     {
  485.                          POINT currPT = {x,y};
  486.                          int startX = currPT.x * 32;
  487.                          int startY = currPT.y * 32;          
  488.                          DrawSprite(apple->getImage(),0,32,32,1,startX,startY);
  489.                     };
  490.                }
  491.  
  492.                // draw the sprites ontop of the tiles
  493.                DrawSprites();
  494.  
  495.                //stop drawing
  496.                sprite_handler->End();
  497.  
  498.                // show the SCORE
  499.                tStr = "Food: " + toString<int>(score.carrots);
  500.                drawText(tStr, 60, 0);  
  501.  
  502.                tStr = "Lives: " + toString<int>(LIVES);
  503.                drawText(tStr, 180, 0);  
  504.  
  505.                // show the number of frames
  506.                tStr = "Frame: " + toString<int>(frame_cnt);
  507.                drawText(tStr, (WINDOW_WIDTH/2), WINDOW_HEIGHT-30);  
  508.  
  509.                // if power pill active, show timer count down
  510.                if (ppActive)
  511.                {
  512.                     int remaining = (int) ((ppEnd - ppStart) / 100);
  513.                     tStr = "(" + toString<int>(remaining) + ")";
  514.                     drawText(tStr, 230,2);
  515.                }
  516.  
  517.                // show states of enemies
  518.                for (int e = 0; e < NUM_ENEMY; e++)
  519.                {
  520.                     POINT cPT = enemy[e]->getTile();
  521.                     POINT tPT = enemy[e]->getHome();                    
  522.  
  523.                     switch( enemy[e]->getSTATE() )
  524.                     {
  525.                     case SPAWN: tStr = "Spawn"; break;
  526.                     case CHASE: tStr = "Chase"; break;
  527.                     case EVADE: tStr = "Evade"; break;
  528.                     case DEAD:  tStr = "Dead";  break;
  529.                     default:    tStr = "Error"; break;
  530.                     }
  531.                     drawText(tStr, 4, 260 + (20*e) );
  532.                    
  533.                }
  534.  
  535.                // show current state of player              
  536.                switch(player->getSTATE())
  537.                {
  538.                     case ALIVE:drawText("Player: Alive", 320,0); break;
  539.                     case DEAD: drawText("Player: Dead" , 320,0); break;
  540.                     default:   drawText("Player: Error", 320,0); break;
  541.                }
  542.  
  543.                if (player->isBetweenTiles())
  544.                     drawText("moving",580,0);
  545.  
  546.                //stop rendering
  547.                d3ddev->EndScene();
  548.      } // done rendering
  549.  
  550.      //display the back buffer on the Window
  551.      d3ddev->Present(NULL, NULL, NULL, NULL);
  552. }
  553.  
  554. //frees memory and cleans up before the game ends
  555. void Game_End(HWND hwnd)
  556. {
  557.      errorLog.close();
  558.  
  559.      Kill_Keyboard();
  560.      Kill_Mouse();
  561.  
  562.      fflush(stdout);
  563.      fclose(stdout);
  564.  
  565.      if (sprite_handler != NULL)
  566.           sprite_handler->Release();
  567. }
  568. // END ----------------------- GAME FUNCTIONS ----------------------- //
  569.  
  570. // test for collisions
  571. int SpriteCollision(CSprite *sprite1, CSprite *sprite2)
  572. {
  573.      RECT rect1;
  574.      rect1.left   = sprite1->getX() + 1;
  575.      rect1.top    = sprite1->getY() + 1;
  576.      rect1.right  = sprite1->getX() + sprite1->getWidth()  -1;
  577.      rect1.bottom = sprite1->getY() + sprite1->getHeight() -1;
  578.  
  579.      RECT rect2;
  580.      rect2.left   = sprite2->getX() + 1;
  581.      rect2.top    = sprite2->getY() + 1;
  582.      rect2.right  = sprite2->getX() + sprite2->getWidth()  -1;
  583.      rect2.bottom = sprite2->getY() + sprite2->getHeight() -1;
  584.  
  585.      RECT dest;
  586.      return IntersectRect(&dest, &rect1, &rect2);
  587. }
  588.  
  589. //This function does the real work of drawing a single tile from the
  590. //source image onto the backbuffer.
  591. void DrawSprite(LPDIRECT3DTEXTURE9 source,  // source surface image
  592.                 int tilenum,                // tile #
  593.                 int width,              // tile width
  594.                 int height,             // tile height
  595.                 int columns,                // columns of tiles
  596.                 int destx,              // destination x
  597.                 int desty)              // destination y
  598. {
  599.      //create a RECT to describe the source image
  600.      RECT r1;
  601.  
  602.      r1.left = (tilenum % columns) * width;
  603.      r1.top = (tilenum / columns) * height;
  604.      r1.right = r1.left + width;
  605.      r1.bottom = r1.top + height;
  606.  
  607.      // location to draw sprite
  608.      D3DXVECTOR3 position((float)destx, (float)desty, 0);
  609.  
  610.      //draw the player sprite
  611.      sprite_handler->Draw(
  612.           source,
  613.           &r1,
  614.           NULL,
  615.           &position,
  616.           D3DCOLOR_XRGB(255,255,255));
  617. }
  618.  
  619. // draws the layer #2 (player objects)
  620. void DrawSprites()
  621. {
  622.      int tiles_row=6; // tiles per row in sprite sheet
  623.      int columns, rows;
  624.      int x, y;
  625.      POINT destPT;
  626.  
  627.      //calculate the number of columns and rows
  628.      columns = MAPWIDTH;
  629.      rows    = MAPHEIGHT;
  630.  
  631.      //draw tiles onto the scroll buffer surface
  632.      for (int a=0; a < NUM_ENEMY; a++)
  633.      {
  634.           x = enemy[a]->getX();
  635.           y = enemy[a]->getY();
  636.  
  637.           // offset for isometric maps on odd rows
  638.           destPT.x = x; // * TILEWIDTH;
  639.           destPT.y = y; // * TILEHEIGHT;
  640.  
  641.           //draw the player sprite
  642.           if ( enemy[a]->getHasBody() )
  643.           {
  644.                if (ppActive)
  645.                     DrawSprite(eatable->getImage(),0,eatable->getWidth(),eatable->getHeight(),1,destPT.x,destPT.y);                                  
  646.                else
  647.                     DrawSprite(enemy[a]->getImage(),0,enemy[a]->getWidth(),enemy[a]->getHeight(),1,destPT.x,destPT.y);
  648.           }
  649.           else
  650.                // draw eye floating balls
  651.                DrawSprite(eyeballs->getImage(),0,eyeballs->getWidth(),eyeballs->getHeight(),1,destPT.x,destPT.y);
  652.  
  653.           //          sprite_handler->Draw(avatars[a]->getImage(), &avatars[a]->getRect(),NULL,&destPT, D3DCOLOR_XRGB(255,255,255));
  654.      }
  655.  
  656.      //  tile_num                                            columns    
  657.      //DrawSprite(player->getImage(),player->getFrame(),player->getWidth(),player->getHeight(),4,player->getX(), player->getY());
  658.      // location to draw sprite
  659.      D3DXVECTOR3 position((float)player->getX(), (float)player->getY(), 0);
  660.      sprite_handler->Draw(player->getImage(), &player->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  661. }
  662.  
  663. //This function fills the tilebuffer with tiles representing
  664. //the current scroll display based on scrollx/scrolly.
  665. // draws straight to backbuffer
  666. void DrawTilesBack()
  667. {
  668.      int tiles_per_row=BACK_PER_ROW; // tiles per row in sprite sheet
  669.      int columns, rows;
  670.      int x, y; // destX,destY;
  671.      int tilenum;
  672.      POINT destPT;
  673.  
  674.      //calculate the number of columns and rows
  675.      columns = MAPWIDTH;  //WINDOWWIDTH  / TILEWIDTH;      // 32 * 20 = 640
  676.      rows    = MAPHEIGHT; //WINDOWHEIGHT / (TILEHEIGHT/4); // 24 * 20 = 480
  677.  
  678.      //draw tiles onto the scroll buffer surface
  679.      for (y=0; y < rows; y++)
  680.      {
  681.           for (x=0; x < columns; x++)
  682.           {
  683.                // offset for isometric maps on odd rows
  684.                destPT.x = x * TILEWIDTH;
  685.                destPT.y = y * TILEHEIGHT;
  686.  
  687.                //retrieve the tile number from this position
  688.                tilenum = MAPDATA[(y) * MAPWIDTH + (x)];
  689.  
  690.                // LAYER #1 - TILES
  691.                //draw the tile onto the scroll buffer
  692.                DrawSprite(tiles,tilenum,TILEWIDTH,TILEHEIGHT,tiles_per_row, destPT.x, destPT.y);
  693.           } // for x ...
  694.      } // for y ...
  695. } // drawtiles ...
  696.  
  697. // roll dice
  698. int rollDice(int Dice, int Number = 1)
  699. {
  700.      int sum=0;
  701.      for (int i=1; i <= Number; i++)
  702.           sum += rand() % Dice + 1;
  703.  
  704.      return sum;
  705. };
  706.  
  707. void drawText(string outText, int windowX, int windowY)
  708. {
  709.      RECT textbox;
  710.      textbox.left   = windowX;
  711.      textbox.top    = windowY;
  712.      textbox.right  = windowX + ( outText.size() * 8);
  713.      textbox.bottom = windowY + 16; // 90x30 box ok?
  714.  
  715.      dxfont->DrawTextA(  NULL,
  716.                          outText.c_str(),
  717.                          outText.size(),
  718.                          &textbox,
  719.                          DT_LEFT,
  720.                          D3DCOLOR_ARGB(255, 120, 255, 120)
  721.                          );
  722. };
  723.  
  724. // used for debug purposes, writing numbers to Window
  725. // draw numbers to Window
  726. void drawNumbers(int number, int windowX, int windowY)
  727. {
  728.      tStr = toString<int>(number);
  729.  
  730.      RECT textbox;
  731.      textbox.left   = windowX;
  732.      textbox.top    = windowY;
  733.      textbox.right  = windowX + 90;
  734.      textbox.bottom = windowY + 30; // 90x30 box ok?
  735.  
  736.      dxfont->DrawTextA(  NULL,
  737.                          tStr.c_str(),
  738.                          tStr.size(),
  739.                          &textbox,
  740.                          DT_RIGHT,
  741.                          D3DCOLOR_ARGB(255, 120, 255, 120)
  742.                          );
  743. };
  744.  
  745. int getMap( int c_x, int c_y)
  746. {
  747.      if ( (c_x < 1) || (c_y < 1) || (c_x > (MAPWIDTH-2)) || (c_y > (MAPHEIGHT-2)) )
  748.      {
  749.           return 0;
  750.      }
  751.      return MAPDATA[(MAPWIDTH * c_y) + (c_x)];
  752. };
  753.  
  754. void do_FSM(int a)
  755. {
  756.      // first lets make sure he knows where his target location is
  757.      switch(enemy[a]->getSTATE())
  758.      {
  759.      case SPAWN:
  760.           {
  761.                enemy[a]->goHome();
  762.  
  763.                // see if have arrived at spawn point
  764.                if ( enemy[a]->atDest() )
  765.                {
  766.                     enemy[a]->setSTATE(CHASE);
  767.  
  768.                     // check if it just eyes or actually alive
  769.                     if (! enemy[a]->getHasBody() )
  770.                          enemy[a]->setHasBody(true);
  771.  
  772.                     // this is used to make it appear as eye-balls until its respawned
  773.                }
  774.           }; break;
  775.  
  776.      case CHASE:
  777.           {
  778.                // target players location
  779.                POINT dest;
  780.                dest.x = player->getTile().x;
  781.                dest.y = player->getTile().y;
  782.                enemy[a]->setTarget( dest.x, dest.y );
  783.           }; break;
  784.      case EVADE:
  785.           {
  786.                // find the most distant corner and go there
  787.                int pX = player->getTile().x;
  788.                int pY = player->getTile().y;
  789.  
  790.                int cX = enemy[a]->getTile().x;
  791.                int cY = enemy[a]->getTile().y;
  792.  
  793.                int range=0, highest=0;
  794.                for (int r=0; r<4; r++)
  795.                {
  796.                     int distance = abs(corners[r].x - pX) + (corners[r].y - pY);
  797.                     if (distance > range)
  798.                     {
  799.                          range = distance;
  800.                          highest = r;
  801.                     }
  802.                }
  803.  
  804.                errorLog << "corner " << highest<< " at " << corners[highest].x << "," << corners[highest].y << " is most distance from player, go there" << endl;
  805.  
  806.                POINT dest = corners[highest];
  807.                enemy[a]->setTarget( dest.x, dest.y );          
  808.           }; break;
  809.      }
  810. }
  811.  
  812. bool within( int value, int min, int max)
  813. {
  814.      return ((value > min) && (value < max));    
  815. };
  816.  
  817. bool findPath(int a)
  818. {
  819.      /*
  820.      This is a home-brewed version of A* based off http://en.wikipedia.org/wiki/Pathfinding#Sample_algorithm
  821.      Also added a bit more logic to speed things up and reduce redundancy
  822.  
  823.      Basic Rules:
  824.      1. Create a list of the four adjacent cells, with a counter variable of the current element's counter variable + 1
  825.      2. Check all cells in each list for the following two conditions:
  826.      a. If the cell is a wall, remove it from the list
  827.      b. If there is an element in the main list with the same coordinate and an equal or lower counter, remove it from the list
  828.      c. Add all remaining cells in the list to the end of the main list
  829.      d. Go to the next item in the list
  830.      */
  831.  
  832.      errorLog << "START findPath ----" << endl;
  833.  
  834.      // store the current and target tile locations
  835.      POINT currTile = enemy[a]->getTile();
  836.      POINT target = enemy[a]->getTarget();
  837.  
  838.      errorLog << "current tile = " << currTile.x << "," << currTile.y << " and target = " << target.x << "," << target.y << endl;
  839.  
  840.      // initialize main Queue
  841.      for (int i=0; i < MAX_PATHS; i++)
  842.      {
  843.           Queue[i].x = 0;
  844.           Queue[i].y = 0;
  845.           Queue[i].cnt = 0;
  846.      }
  847.  //    errorLog << "initialized main Queue (" << MAX_PATHS << ")" << endl;
  848.  
  849.      // initializing boolean list to track map locations looked at
  850.      for (int y=0;y < MAPHEIGHT; y++)
  851.           for (int x=0; x < MAPWIDTH; x++)
  852.                inList[x][y] = false;
  853.  
  854.  //    errorLog << "set inList[" << MAPWIDTH << "][" << MAPHEIGHT << "] to false" << endl;
  855.  
  856.      // start list/queue with destination target location (assuming its a valid location)
  857.      Queue[0].x = target.x;
  858.      Queue[0].y = target.y;
  859.      Queue[0].cnt = 1;
  860.      cnt=1;
  861.      inList[target.x][target.y] = true;
  862.  
  863.   //   errorLog << "set start of Queue to " << target.x << "," << target.y << " and set inList[][] = true" << endl;
  864.  
  865. // CHECK BETWEEN HERE --
  866.      // cycle through list, until we reach current location
  867.      bool found = false;
  868.   //   errorLog << "found set to false" << endl;
  869.      while ((cnt < MAX_PATHS) && (!found))
  870.      {
  871.   //        errorLog << " ... inside while loop ... " << endl;
  872.  
  873.           // only create this once ... to avoid fragmenting memory & other issues
  874.           static listType tQueue[MAX_PATHS];          
  875.  
  876.           // initialize temp Queue
  877.           for (int i=0; i < MAX_PATHS;i++)
  878.           {
  879.                tQueue[i].x = 0;
  880.                tQueue[i].y = 0;
  881.                tQueue[i].cnt = 0;
  882.           }
  883.           tCnt=0;
  884.   //        errorLog << " initialized tQueue " << MAX_PATHS << " and tCnt=" << tCnt << endl;
  885.  
  886.  //         errorLog << " checking neighbors : " << endl;
  887.           // find neighbors of each entry in list
  888.           for (int i=0; i < cnt; i++)
  889.           {
  890.                int c_x   = Queue[i].x;
  891.                int c_y   = Queue[i].y;
  892.                int c_cnt = Queue[i].cnt;  
  893.     //           errorLog << "Queue [" << i << "] is " << c_x << "," << c_y << "," << c_cnt << " tCnt=" << tCnt << endl;
  894.  
  895.                // if not out of range, or already in neighbor list, and map location is "walk-able" ... then add it.
  896.                // x-1
  897.                if (getMap((c_x-1),c_y) == 1)
  898.                     if ( ! inList[c_x-1][c_y] )                        
  899.                          {
  900.   //                            errorLog << "not in list and map[" << (c_x-1) << "][" << (c_y) << "] = " << getMap((c_x-1),(c_y)) << " tCnt=" << tCnt  << endl;
  901.                               // add to temp List
  902.                               tQueue[tCnt].x = c_x -1;
  903.                               tQueue[tCnt].y = c_y;
  904.                               tQueue[tCnt].cnt = c_cnt+1;
  905.                               tCnt++;
  906.                          }
  907.  
  908.                // if not out of range, or already in neighbor list, and map location is "walk-able" ... then add it.
  909.                // x+1
  910.                if (getMap((c_x+1), c_y) == 1)
  911.                     if ( ! inList[c_x+1][c_y] )                        
  912.                          {
  913.    //                           errorLog << "not in list and map[" << (c_x+1) << "][" << (c_y) << "] = " << getMap((c_x+1),(c_y)) << " tCnt=" << tCnt  << endl;
  914.                               // add to temp List
  915.                               tQueue[tCnt].x = c_x + 1;
  916.                               tQueue[tCnt].y = c_y;
  917.                               tQueue[tCnt].cnt = c_cnt+1;
  918.                               tCnt++;
  919.                          }
  920.  
  921.                // if not out of range, or already in neighbor list, and map location is "walk-able" ... then add it.
  922.                // y-1
  923.                if (getMap((c_x), (c_y-1)) == 1)
  924.                     if ( ! inList[c_x][c_y-1] )                        
  925.                          {
  926.   //                            errorLog << "not in list and map[" << (c_x) << "][" << (c_y-1) << "] = " << getMap((c_x),(c_y-1)) << " tCnt=" << tCnt  << endl;
  927.                               // add to List
  928.                               tQueue[tCnt].x = c_x;
  929.                               tQueue[tCnt].y = c_y-1;
  930.                               tQueue[tCnt].cnt = c_cnt+1;
  931.                               tCnt++;
  932.                          }
  933.  
  934.                // if not out of range, or already in neighbor list, and map location is "walk-able" ... then add it.
  935.                // y+1
  936.                if (getMap((c_x), (c_y+1)) == 1)
  937.                     if ( ! inList[c_x][c_y+1] )                        
  938.                          {
  939. //                              errorLog << "not in list and map[" << (c_x) << "][" << (c_y+1) << "] = " << getMap((c_x),(c_y+1)) << " tCnt=" << tCnt << endl;
  940.                               // add to List
  941.                               tQueue[tCnt].x = c_x;
  942.                               tQueue[tCnt].y = c_y+1;
  943.                               tQueue[tCnt].cnt = c_cnt+1;
  944.                               tCnt++;
  945.                          }
  946.           }
  947.  
  948.           // now check if any of these temp
  949.           // If there is an element in the main list with the same coordinate and an equal or lower counter, remove it from the list
  950.           // update: this is redundant,since we ignore those already in the list now
  951.           /*
  952.           for (int p=0; p < tCnt; p++)
  953.           {
  954.           //if its already in list, we can assume its a lower value and ignore this one
  955.           if (tQueue[p].cnt > 0)
  956.           if ( inList[tQueue[p].x][tQueue[p].y] )
  957.           tQueue[p].cnt = -1;
  958.           }
  959.           */
  960.  
  961.           // add it to main list if not found
  962.           for (int p=0; p < tCnt; p++)
  963.                if (tQueue[p].cnt > 0)
  964.                {
  965.                     int c_x   = tQueue[p].x;
  966.                     int c_y   = tQueue[p].y;
  967.                     int c_cnt = tQueue[p].cnt;
  968.  
  969.    //                 errorLog << "adding (" << c_x << "," << c_y  << "," << c_cnt  << ") to main Queue - at " << cnt << endl;
  970.                     // add to main list                                
  971.                     Queue[cnt].x = c_x;
  972.                     Queue[cnt].y = c_y;
  973.                     Queue[cnt].cnt = c_cnt;
  974.                     cnt++;
  975.  
  976.                     // make sure to keep track of whats been added
  977.                     inList[c_x][c_y] = true;
  978.                }
  979.  
  980.           // see if current tile has been found
  981.           found = inList[currTile.x][currTile.y];
  982. //          errorLog << "current found boolean is = " << found << endl;
  983.      }
  984.      errorLog << "------------------------------" << endl;
  985.  
  986.      // dump map
  987.      int tMAP[20][20];
  988.      for (int ty=0; ty < MAPHEIGHT; ty++)
  989.           for (int tx=0; tx < MAPWIDTH; tx++)
  990.                tMAP[tx][ty]=0;
  991.      for (int q=0; q < cnt; q++)
  992.      {
  993.           int tx = Queue[q].x;
  994.           int ty = Queue[q].y;
  995.           int tcnt = Queue[q].cnt;
  996.           tMAP[tx][ty] = tcnt;          
  997.      }
  998.      
  999.      tMAP[currTile.x][currTile.y] = -1; // mark current location with (-1)
  1000.  
  1001.      for (int ty=0; ty < MAPHEIGHT; ty++)
  1002.      {
  1003.           for (int tx=0; tx < MAPWIDTH; tx++)
  1004.                errorLog <<  setw(4) << tMAP[tx][ty];
  1005.           errorLog << endl;
  1006.      }
  1007. // AND -- HERE --
  1008.  
  1009.      // cancel all previous move settings
  1010.      enemy[a]->setMoveXY(0,0);
  1011.  
  1012.      // check to see if NO PATH FOUND ...
  1013.      if (!found)
  1014.      {
  1015.           errorLog << "path not found!" << endl;
  1016.           return false;
  1017.      }
  1018.      else
  1019.      {
  1020.           errorLog << "path found, choosing next step" << endl;
  1021.           // PATH was found
  1022.  
  1023.           // Now, see which of the squares around the current location has the lowest cnt value
  1024.           // this will determine which path is the shortest to target
  1025.  
  1026.           // update: use the temporary xy map to make this easier
  1027.           // NORTH
  1028.           goN =tMAP[currTile.x][currTile.y-1]; if (goN == 0) goN = MAX_PATH;
  1029.           // SOUTH
  1030.           goS =tMAP[currTile.x][currTile.y+1]; if (goS == 0) goS = MAX_PATH;
  1031.           // EAST
  1032.           goE =tMAP[currTile.x+1][currTile.y]; if (goE == 0) goE = MAX_PATH;
  1033.           // WEST
  1034.           goW =tMAP[currTile.x-1][currTile.y]; if (goW == 0) goW = MAX_PATH;
  1035.  
  1036.           errorLog << " E,N,S,W = " << goE << "," << goN << "," << goS << "," << goW << endl;
  1037.  
  1038.           // determine which direction to move
  1039.           POINT newTile = currTile;
  1040.           switch( lowest(goE, goN, goS, goW) )
  1041.           {
  1042.                case EAST:  newTile.x++; break; // east
  1043.                case NORTH: newTile.y--; break; // north
  1044.                case SOUTH: newTile.y++; break; // south              
  1045.                case WEST:  newTile.x--; break; //west
  1046.           }
  1047.  
  1048.           enemy[a]->gotoTile(newTile.x, newTile.y);
  1049.           errorLog << "new goto Tile is (" << newTile.x << "," << newTile.y << ") from " << currTile.x << "," << currTile.y << endl;
  1050.      }
  1051.  
  1052.      // ok, we found a path ... and are moving towards it
  1053.      return true;
  1054. }
  1055.  
  1056. void checkAI()
  1057. {
  1058.      // check AI states                
  1059.      // change unit location(s)
  1060.      for (int a = 0; a < NUM_ENEMY; a++)
  1061.      {            
  1062.           POINT currTile = enemy[a]->getTile();
  1063.  
  1064.           if ( enemy[a]->isBetweenTiles() )
  1065.           {
  1066.                // dont change behavior between tiles, just keep moving
  1067.                enemy[a]->doMove();
  1068.           }
  1069.           else
  1070.           {
  1071.                // FIND NEXT TILE ... IN PATH TOWARDS GOAL
  1072.  
  1073.                // FSM
  1074.                do_FSM(a);
  1075.  
  1076.                // OK - we should have a destination now ... lets find a path to it ...
  1077.  
  1078.                if ( findPath(a) )
  1079.                {
  1080.                     errorLog << "--------------------- DONE findPath() and returned true ... moving" << endl << endl;
  1081.                     enemy[a]->doMove();
  1082.                }
  1083.                else
  1084.                {
  1085.                     errorLog << "---------------------- DONE - findPath) returned FALSE! " << endl;
  1086.  
  1087.                     // no path found - now what ?!
  1088.                     // pick a random open direction to go ...
  1089.                     POINT dest = currTile;
  1090.  
  1091.                     DIRS currDir = enemy[a]->getFacing();
  1092.  
  1093.                     switch(currDir)
  1094.                     {
  1095.                     case NORTH:
  1096.                          {
  1097.                               // try to continue going North
  1098.                               // scan for y-1
  1099.                               if ( getMap(currTile.x, currTile.y-1) == 1 ) // 1 = walkable
  1100.                                    dest.y--;
  1101.                          }; break;
  1102.                     case SOUTH:
  1103.                          {
  1104.                               // scan for y+1
  1105.                               if ( getMap(currTile.x, currTile.y+1) == 1 ) // 1 = walkable
  1106.                                    dest.y++;
  1107.                          }; break;
  1108.                     case EAST:
  1109.                          {
  1110.                               // scan for x+1
  1111.                               if ( getMap(currTile.x+1, currTile.y) == 1 ) // 1 = walkable
  1112.                                    dest.x++;
  1113.                          }; break;
  1114.                     case WEST:
  1115.                          {
  1116.                               // scan for x-1
  1117.                               if ( getMap(currTile.x-1, currTile.y) == 1 ) // 1 = walkable
  1118.                                    dest.x--;
  1119.                          }; break;
  1120.                     };
  1121.  
  1122.                     // if cant continue in current direction
  1123.                     if (currTile.x == dest.x && currTile.y == dest.y)
  1124.                     {
  1125.                          // scan for x-1 (WEST)
  1126.                          if (currDir != EAST)
  1127.                               if ( getMap(currTile.x-1, currTile.y) == 1 ) // 1 = walkable
  1128.                                    dest.x--;
  1129.  
  1130.                          // scan for x+1 (EAST)
  1131.                          if (currDir != WEST)
  1132.                               if ( getMap(currTile.x+1, currTile.y) == 1 ) // 1 = walkable
  1133.                                    dest.x++;
  1134.  
  1135.                          // scan for y-1 (NORTH)
  1136.                          if (currDir != SOUTH)
  1137.                               if ( getMap(currTile.x, currTile.y-1) == 1 ) // 1 = walkable
  1138.                                    dest.y--;
  1139.  
  1140.                          // scan for y+1 (SOUTH)
  1141.                          if (currDir != NORTH)
  1142.                               if ( getMap(currTile.x, currTile.y+1) == 1 ) // 1 = walkable
  1143.                                    dest.y++;
  1144.                     }
  1145.  
  1146.                     enemy[a]->setMoveXY(0,0);
  1147.                     enemy[a]->gotoTile(dest.x,dest.y);
  1148.                     enemy[a]->doMove();
  1149.                }
  1150.           }
  1151.      }    
  1152. }