1. // Beginning Game Programming, 2nd Edition
  2. // Chapter 8
  3. // Tiled_Sprite program source code file
  4.  
  5. #include <cassert>
  6. #include "game.h"
  7.  
  8. // default background map
  9. int MAPDATA[MAPWIDTH*MAPHEIGHT];
  10.  
  11. // used for displaying the sprites
  12. LPD3DXSPRITE sprite_handler;
  13.  
  14. // used to prevent stacking overlays
  15. int mapOverlay[MAPWIDTH][MAPHEIGHT];
  16.  
  17. // holds the info on the map overlays
  18. OverlayType MapOverlays[MAX_OVERLAYS];
  19.  
  20. int tempX,tempY; // used as a temp, to store current location
  21.  
  22. int level = 0; // starting level
  23.  
  24. //the wave sound
  25. CSound *sound_fire,*sound_exp, *sound_sword, *sound_club,*sound_magic,*sound_walk_left,*sound_walk_right,*sound_miss, *sound_orc_hit, *sound_orc_grunt, *sound_heal;
  26.  
  27. // UNITs
  28. CSprite *player,*player_casting,*player_dying;
  29.  
  30. // enemy #1
  31. CSprite *orc,*orc_attacking,*orc_dying;
  32.  
  33. // enemy #2
  34. CSprite *mushy, *mushy_attacking, *mushy_dying, *mushy_appearing;
  35.  
  36. // overlay tiles
  37. LPDIRECT3DTEXTURE9 tiles,bushes;
  38.  
  39. // MAGIC
  40. CSprite *firebolt,*explosion;
  41. //RECT magic_border;
  42.  
  43. // used to print numbers on screen
  44. CSprite *numbers, *bars;
  45.  
  46. // text graphics
  47. LPDIRECT3DSURFACE9 text_destroyed,text_orcs,text_bushes,text_level,text_header;
  48.  
  49. // counter for trees destroyed so far
  50. int trees_destroyed     =   0;
  51. int enemy_destroyed     =   0;
  52. int reset_death         =   0;
  53.  
  54. // timer for recovery of mana
  55. int mana_timer          =   0;
  56. SPELLS curSpell         =   SPELL_NONE;
  57.  
  58. // track which overlay # fireball collided with last, -1 means none
  59. int fireball_collision = -1;
  60.  
  61. //timing variable
  62. long start = GetTickCount();
  63. HRESULT result;
  64.  
  65. // ----------------------- GAME FUNCTIONS ----------------------- //
  66.  
  67. //initializes the game
  68. int Game_Init(HWND hwnd)
  69. {
  70.     //set random number seed
  71.     srand(time(NULL));
  72.  
  73.    //initialize mouse
  74.     if (!Init_Mouse(hwnd))
  75.     {
  76.         MessageBox(hwnd, "Error initializing the mouse", "Error", MB_OK);
  77.         return 0;
  78.     }
  79.  
  80.     //initialize keyboard
  81.     if (!Init_Keyboard(hwnd))
  82.     {
  83.         MessageBox(hwnd, "Error initializing the keyboard", "Error", MB_OK);
  84.         return 0;
  85.     }
  86.  
  87.     //create sprite handler object
  88.     result = D3DXCreateSprite(d3ddev, &sprite_handler);
  89.     if (result != D3D_OK)
  90.      return 0;
  91.  
  92.     // load map data
  93.     ifstream mapFile("map00.txt");
  94.  
  95.     // read in data from map file
  96.     assert(! mapFile.fail());
  97.  
  98.     // int MAPDATA[MAPWIDTH*MAPHEIGHT]
  99.     //MAPWIDTH   16  
  100.     //MAPHEIGHT  45
  101.     char buffer[256];
  102.     char line[255];
  103.     int row=0;
  104.     while (! mapFile.eof())
  105.     { // read in the map data
  106.         for (int column=0; column < (MAPWIDTH-1); column++)
  107.         {
  108.             mapFile.getline(line, sizeof(buffer), ',');
  109.             MAPDATA[row * MAPWIDTH + column] = atoi(line);         
  110.         }
  111.         // grab the end of line
  112.         mapFile.getline(line, sizeof(buffer), '\n');
  113.         MAPDATA[row * MAPWIDTH + (MAPWIDTH-1)] = atoi(line);
  114.  
  115.         row++;
  116.     }
  117.     mapFile.close();
  118.  
  119.  
  120.     /*
  121.     NOTE: loading textures with "pink" (255,0,255) as the transparent color
  122.     */
  123.  
  124.     // health mana bars
  125.     bars = new CSprite(100,SCREEN_HEIGHT-64,64,6,"bars.bmp");
  126.     assert(bars);
  127.     bars->setupAnim(1,EAST,7);
  128.  
  129.     //sprites for tile background
  130.     tiles = LoadTexture("tiles.bmp", D3DCOLOR_XRGB(255,0,255));
  131.     assert(tiles);
  132.  
  133.     // sprites for overlay (over original tiles)
  134.     bushes = LoadTexture("bushes.bmp", D3DCOLOR_XRGB(255,0,255));
  135.     assert(bushes);
  136.  
  137.     // Menu stuff
  138.     text_destroyed  = LoadSurface("text_destroyed.bmp", D3DCOLOR_XRGB(0,0,0));
  139.     assert(text_destroyed);
  140.     text_orcs   = LoadSurface("text_orcs.bmp", D3DCOLOR_XRGB(0,0,0));
  141.     assert(text_orcs);
  142.     text_bushes = LoadSurface("text_bushes.bmp", D3DCOLOR_XRGB(0,0,0));
  143.     assert(text_bushes);
  144.     text_level  = LoadSurface("text_level.bmp", D3DCOLOR_XRGB(0,0,0));
  145.     assert(text_level);
  146.     text_header = LoadSurface("border.bmp", D3DCOLOR_XRGB(0,0,0));
  147.     assert(text_header);
  148.  
  149.  
  150.     // numbers
  151.     numbers = new CSprite(0,0,18,16,"little_numbers.bmp");
  152.     assert(numbers);
  153.     numbers->setupAnim(10,EAST,10); // 1 row, numbers 0 to 9
  154.  
  155. //player
  156.     player = new CSprite(80,80,96,96,"stan-staff-walking.bmp");
  157.     assert(player);
  158.     player->setupAnim(9,SOUTH,10); // 9 sprites per anim, initially heading EAST, 10 sprites per row on sheet
  159.     player->setMoveXY(0,0);
  160.     player->setMoving(false);
  161.     player->setBorder(8,SCREEN_WIDTH - 96 - 16,-16,SCREEN_HEIGHT - 96 -32); // works for 96x96 sprites, keeps sprite from walking off screen
  162.     player->setSTATE(ACTIVE);
  163.  
  164.     // casting a spell
  165.     player_casting = new CSprite(0,0,96,96,"stan-staff-magic.bmp");
  166.     assert(player_casting);
  167.     player_casting->setupAnim(11,EAST,10); //  sprites per anim, initial heading ,  sprites per row on sheet
  168.     player_casting->setMoveXY(0,0);
  169.     player_casting->setMoving(false);  
  170.  
  171.     // dying
  172.     player_dying = new CSprite(0,0,96,96,"stan-staff-killed.bmp");
  173.     assert(player_dying);
  174.     player_dying->setupAnim(9,EAST,10); //  sprites per anim, initial heading ,  sprites per row on sheet
  175.     player_dying->setMoveXY(0,0);
  176.     player_dying->setMoving(false);
  177.  
  178.  
  179. // MONSTER(s)
  180.     //"orc" walking, attacking, dying
  181.     int destX = SCREEN_WIDTH  - 128;
  182.     int destY = SCREEN_HEIGHT - 128;
  183.  
  184.     orc = new CSprite(destX,destY,96,96,"orc.bmp");
  185.     assert(orc);
  186.     orc->setBorder(8,SCREEN_WIDTH - 112,-16,SCREEN_HEIGHT - 128); // works for 96x96 sprites, keeps sprite from walking off screen
  187.     orc->setupAnim(8,NW,10); // sprites per anim, initial heading, sprites per row on sheet
  188.     orc->setSTATE(ACTIVE);
  189.     orc->Combat->setDefense(10);
  190.     orc->Combat->setOffense(12);
  191.     orc->setHP(22);
  192.  
  193.     orc_attacking = new CSprite(destX,destY,96,96,"orc-attacking.bmp");
  194.     assert(orc_attacking);
  195.     orc_attacking->setupAnim(13,SOUTH,10); // sprites per anim, initial heading, sprites per row on sheet
  196.     orc_attacking->setMoveXY(0,0);
  197.  
  198.     orc_dying = new CSprite(destX,destY,128,128,"orc-dying.bmp");
  199.     assert(orc_dying);
  200.     orc_dying->setupAnim(13,SOUTH,8); // sprites per anim, initial heading, sprites per row on sheet
  201.     orc_dying->setMoveXY(0,0);
  202.  
  203.     // MUSHROOM MAN
  204.     mushy = new CSprite(destX,destY,96,96,"mushman_walk.bmp");
  205.     assert(mushy);
  206.     mushy->setBorder(8,SCREEN_WIDTH - 112,-16,SCREEN_HEIGHT - 128); // works for 96x96 sprites, keeps sprite from walking off screen
  207.     mushy->setupAnim(8,EAST,10); // sprites per anim, initial heading, sprites per row on sheet
  208.     mushy->setSTATE(INACTIVE); // doesnt start visible
  209.     mushy->Combat->setDefense(10);
  210.     mushy->Combat->setOffense(8);
  211.     mushy->setHP(12);
  212.  
  213.     mushy_attacking = new CSprite(destX,destY,96,96,"mushman_attack.bmp");
  214.     assert(mushy_attacking);
  215.     mushy_attacking->setupAnim(11,EAST,10); // sprites per anim, initial heading, sprites per row on sheet
  216.     mushy_attacking->setMoveXY(0,0);
  217.  
  218.     mushy_dying = new CSprite(destX,destY,96,96,"mushman_dying.bmp");
  219.     assert(mushy_dying);
  220.     mushy_dying->setupAnim(7,EAST,10); // sprites per anim, initial heading, sprites per row on sheet
  221.     mushy_dying->setMoveXY(0,0);
  222.  
  223.     mushy_appearing = new CSprite(destX,destY,96,96,"mushman_appear.bmp");
  224.     assert(mushy_appearing);
  225.     mushy_appearing->setupAnim(7,EAST,10); // sprites per anim, initial heading, sprites per row on sheet
  226.     mushy_appearing->setMoveXY(0,0);
  227.  
  228. //fire bolt
  229.     firebolt = new CSprite(SCREEN_WIDTH,SCREEN_HEIGHT,64,64,"firebolt.bmp");
  230.     assert(firebolt);
  231.     firebolt->setupAnim(21,EAST,16); // 21 sprites per anim, initially heading EAST, 16 sprites per row on sheet
  232.     firebolt->setDelay(2);
  233.     // used to check when it is close to edge of map; it explodes if so
  234.     firebolt->setBorder(32,SCREEN_WIDTH - 64,8,SCREEN_HEIGHT - 48);
  235.     firebolt->setSTATE(INACTIVE);
  236.  
  237. // + explosion
  238.     explosion = new CSprite(SCREEN_WIDTH,SCREEN_HEIGHT,75,78,"explosion2.bmp");//explosion.bmp 75x78 - 15 per anim, 5 per row
  239.     assert(explosion);
  240.     explosion->setupAnim(15,EAST,5); // 15 sprites per anim, initially heading EAST, 5 sprites per row on sheet
  241.     explosion->setSTATE(INACTIVE);
  242.  
  243.     // Load the Sound effects / wav files
  244.     //firebolt launched
  245.     sound_fire = LoadSound("fire.wav");
  246.     assert(sound_fire);
  247.  
  248.     //explosion
  249.     sound_exp = LoadSound("explosion-01.wav");
  250.     assert(sound_exp);
  251.  
  252.     //attack - sword
  253.     sound_sword = LoadSound("attack sword.wav");
  254.     assert(sound_sword);
  255.  
  256.     //attack - club/staff
  257.     sound_club = LoadSound("attack club.wav");
  258.     assert(sound_club);
  259.  
  260.     //magic
  261.     sound_magic = LoadSound("magic.wav");
  262.     assert(sound_magic);
  263.  
  264.     //magic heal
  265.     sound_heal = LoadSound("magic 2.wav");
  266.     assert(sound_heal);
  267.  
  268.     //attack miss
  269.     sound_miss = LoadSound("klink.wav");
  270.     assert(sound_miss);
  271.  
  272.     //orc hit/dying
  273.     sound_orc_hit = LoadSound("orc-death.wav");
  274.     assert(sound_orc_hit);
  275.  
  276.     //orc grunt
  277.     sound_orc_grunt = LoadSound("orc grunt.wav");
  278.     assert(sound_orc_grunt);
  279.  
  280.     /* ----------- SETUP the overlays  -------- */
  281.     // initialize map overlay array to (0)
  282.     // to make sure there are no duplicate location used
  283.     for (int y=0; y < MAPHEIGHT; y++)
  284.         for (int x = 0; x < MAPWIDTH; x++)
  285.             mapOverlay[x][y] = 0;
  286.  
  287.     // default is (50) random overlays
  288.     int omin = 1;
  289.     int omax = NUM_OVERLAYS; // number of overlay tiles in the file
  290.     int randTile;
  291.     int tileX,tileY;
  292.     POINT tPT;
  293.  
  294.     // MAX_OVERLAYS controls # of overlays to use
  295.     for (int i=0; i < MAX_OVERLAYS; i++)
  296.     {
  297.         randTile = rand() % omax + omin;
  298.  
  299.         do
  300.         {      
  301.             // keep away from map edges
  302.             tileX = rand() % (MAPWIDTH  -4) + 2;
  303.             tileY = rand() % (MAPHEIGHT -4) + 2;
  304.         } while (mapOverlay[tileX][tileY] != 0);
  305.         // repeat until xy chosen is equal to 0, which means no overlay
  306.        
  307.         // now assign the random tile to chosen xy
  308.         mapOverlay[tileX][tileY] = randTile;
  309.  
  310.         // determine on screen xy from tile xy
  311.         tPT = tile_to_world(tileX,tileY);
  312.  
  313.         // overlay details
  314.         MapOverlays[i].tile     = randTile;     // overlay tile #
  315.         MapOverlays[i].x        = tileX;        // tile x
  316.         MapOverlays[i].y        = tileY;        // tile y
  317.  
  318.         // overlay rectangle for collision checking (screen xy)
  319.         MapOverlays[i].r1.left   = tPT.x + 15;
  320.         MapOverlays[i].r1.right  = tPT.x + 47; // 32 pixels wide (x)
  321.         MapOverlays[i].r1.top    = tPT.y + 24;
  322.         MapOverlays[i].r1.bottom = tPT.y + 48; // 24 pixels tall (y)
  323.     }  
  324.  
  325.     //return okay
  326.     return 1;
  327. }
  328.  
  329.  
  330.  
  331. //the main game loop
  332. void Game_Run(HWND hwnd)
  333. {
  334.     //make sure the Direct3D device is valid
  335.     if (d3ddev == NULL)
  336.         return;
  337.  
  338.     //update mouse and keyboard
  339.     Poll_Mouse();
  340.     Poll_Keyboard();
  341.  
  342.     //after short delay, ready for next frame?
  343.     //this keeps the game running at a steady frame rate
  344.     if (GetTickCount() - start >= 30)
  345.     {
  346.         // recover some mana, slowly
  347.         mana_timer++;
  348.         if (mana_timer >= MANA_DELAY)
  349.         {
  350.             player->modMana(1);
  351.             mana_timer = 0;
  352.         }
  353.  
  354.         //reset timing
  355.         start = GetTickCount();
  356.  
  357.        // CHECK for SPACEBAR
  358.        if ( KEY_DOWN(VK_SPACE) && (firebolt->getSTATE() == INACTIVE)  && (explosion->getSTATE() == INACTIVE) && (player->getSTATE() == ACTIVE))
  359.        {
  360.            // returns true if have enough, false if not
  361.            if (! player->modMana(- MANA_FIRE) )
  362.            {
  363.                PlaySound(sound_miss);
  364.            }
  365.            else
  366.            { // enough mana to cast it, lets do it!
  367.                player->setSTATE(CASTING);
  368.                curSpell = SPELL_FIRE;
  369.  
  370.                //casting_spell = true;
  371.                player_casting->setFrame(0);// it will be incremented (1) before displayed
  372.                player_casting->setLastFrame(9);
  373.                player->setMoveXY(0,0);
  374.                player->setMoving(false);
  375.                player_casting->setXY(player->getX(),player->getY()); // set XY
  376.                player_casting->setFacing(player->getFacing(),false);          // make sure player is facing correct direction
  377.  
  378.                // spell casting sound
  379.                PlaySound(sound_magic);
  380.            }
  381.        };
  382.  
  383.        // CHECK for NUM PAD 0 = HEAL
  384.        if ( KEY_DOWN(VK_NUMPAD0) && (player->getSTATE() == ACTIVE))
  385.        {
  386.            // returns true if have enough, false if not
  387.            if (! player->modMana(- MANA_HEAL) )
  388.            {
  389.                PlaySound(sound_miss);
  390.            }
  391.            else
  392.            { // enough mana to cast it, lets do it!
  393.                player->setSTATE(CASTING);
  394.  
  395.                curSpell = SPELL_HEAL;
  396.  
  397.                //casting_spell = true;
  398.                player_casting->setFrame(0);// it will be incremented (1) before displayed
  399.                player_casting->setLastFrame(9);
  400.                player->setMoveXY(0,0);
  401.                player->setMoving(false);
  402.                player_casting->setXY(player->getX(),player->getY()); // set XY
  403.                player_casting->setFacing(player->getFacing(),false);          // make sure player is facing correct direction
  404.  
  405.                // spell casting sound
  406.                PlaySound(sound_magic);
  407.            }
  408.        };
  409.  
  410.        // check status of player avatar
  411.        switch(player->getSTATE())
  412.        {
  413.         case INACTIVE: break;
  414.         case ACTIVE:
  415.             {
  416.                 // respond to arrow keys and move player unit
  417.                 player->checkArrowsKeys();
  418.                 player->doMove(true);
  419.  
  420.                 // check for collision, player vs. overlays
  421.                 // if found, reverse movement and stop player
  422.                 if ( collisionOverlays(player->getRhombus()) )
  423.                 {
  424.                     // ADD:
  425.                     // if close to a small mushroom - make it destroy the overlay
  426.                     // and spawn a mushroom
  427.  
  428.                     // cancel move
  429.                     tempX = player->getX() - player->getMoveX();
  430.                     tempY = player->getY() - player->getMoveY();
  431.                     player->setXY(tempX,tempY);
  432.  
  433.                     // STOP moving
  434.                     player->setMoveXY(0,0);
  435.  
  436.                     // reset anim - if moved
  437.                     if (player->getFrame() > 1)
  438.                         player->setFrame(1); // reset to initial frame of animation also
  439.  
  440.                     // make sure player is now stopped
  441.                     player->setMoving(false);
  442.                 }
  443.  
  444.                 //animate player sprite -- if moving and not casting a spell
  445.                 if (player->isMoving())
  446.                     player->nextFrame();
  447.             };
  448.             break;
  449.         case CASTING:
  450.             {
  451.                if ( player_casting->nextFrame() > 0) // increment frame ... if its reached the end,it will return > 0
  452.                    if (curSpell == SPELL_FIRE)
  453.                    {               
  454.                        // not active until spell casting anim is done
  455.                        player->setSTATE(ACTIVE);
  456.                        firebolt->setSTATE(ACTIVE);
  457.  
  458.                        // setup the firebolt sprite            
  459.                        firebolt->setFacing(player->getFacing(),false);             
  460.                        tempX=tempY=0;
  461.                        switch(player->getFacing())
  462.                        {
  463.                         case NORTH: tempY=-4;   break;
  464.                         case SOUTH: tempY=4;    break;
  465.                         case  EAST: tempX= 4;   break;
  466.                         case  WEST: tempX=-4;   break;
  467.                         case    NE: tempX=4;   
  468.                                     tempY=-2;   break;
  469.                         case    NW: tempX=-4;  
  470.                                     tempY=-2;   break;
  471.                         case    SE: tempX=4;   
  472.                                     tempY=2;    break;
  473.                         case    SW: tempX=-4;  
  474.                                     tempY=2;    break;
  475.                        };
  476.                        firebolt->setMoveXY(tempX,tempY);
  477.  
  478.                        // set X and Y to center of player sprite
  479.                        switch(player->getFacing())
  480.                        {
  481.                         case NORTH: tempX=24;
  482.                                     tempY=-16;  break;
  483.                         case SOUTH: tempX=16;
  484.                                     tempY=64;   break;
  485.                         case  EAST: tempY=16;
  486.                                     tempX=64;   break;
  487.                         case  WEST: tempY=8;
  488.                                     tempX=-16;  break;
  489.                         case    NE: tempX=32;  
  490.                                     tempY=-8;   break;
  491.                         case    NW: tempX=-8;  
  492.                                     tempY=-8;   break;
  493.                         case    SE: tempX=48;  
  494.                                     tempY=48;   break;
  495.                         case    SW: tempX=-8;  
  496.                                     tempY=48;   break;
  497.                        };
  498.                        firebolt->setXY(player->getX()+tempX,player->getY()+tempY);
  499.  
  500.                        // firebolt sound
  501.                        PlaySound(sound_fire);
  502.                    }  // ... casting a spell and reach last frame - which activates the explosion
  503.                    else
  504.                        if (curSpell == SPELL_HEAL)
  505.                        {
  506.                            // does not take effect until spell casting anim is done
  507.                            player->setSTATE(ACTIVE);
  508.  
  509.                            PlaySound(sound_heal);
  510.  
  511.                            player->Heal(); // full heal
  512.                        }
  513.             }; 
  514.             break;
  515.         case DYING:
  516.             {
  517.                 // advance frame - see if its done
  518.                 if ( player_dying->nextFrame() > 0)
  519.                 {
  520.                     player->setSTATE(DEAD);            
  521.                     player_dying->setFrame(player_dying->getLastFrame());
  522.                     player->setDelay(0);
  523.                 }
  524.             }
  525.             break;
  526.         case DEAD:
  527.             {
  528.                 // delay for a few seconds
  529.                 player->setDelay(player->getDelay() + 2);
  530.  
  531.                 // you are dead, game over
  532.                 // waits a few cycles first though
  533.                 if (player->getDelay() > DEATH_DELAY)
  534.                     PostMessage(hwnd, WM_DESTROY, 0, 0);
  535.             };
  536.             break;
  537.        }
  538.  
  539.         // if explosion is already happening - continue animation
  540.         if (explosion->getSTATE() == ACTIVE)
  541.         {              
  542.             // animate sprite
  543.             if (explosion->nextFrame() > 0)
  544.             {
  545.                //explosion_active = false; // left the screen (x)
  546.                 explosion->setSTATE(INACTIVE);
  547.  
  548.                // "destroy" the overlay so it disappears
  549.                if (fireball_collision > -1)
  550.                {
  551.                    if ((MapOverlays[fireball_collision].tile >= 10) && (MapOverlays[fireball_collision].tile <= 18))                       
  552.                        if (mushy->getSTATE() == INACTIVE)
  553.                        {
  554.                            // set the new state
  555.                            mushy->setSTATE(APPEARING);                         
  556.  
  557.                            // set the startin location, offsetting for sprite size difference                          
  558.                            mushy->setX(MapOverlays[fireball_collision].r1.left -31); // offset for difference in size
  559.                            mushy->setY(MapOverlays[fireball_collision].r1.top  -40);
  560.                            mushy_appearing->setX(MapOverlays[fireball_collision].r1.left -31); // offset for difference in size
  561.                            mushy_appearing->setY(MapOverlays[fireball_collision].r1.top  -40);
  562.                        }
  563.  
  564.                    // set collided overlay to (-1) so its ignored
  565.                    MapOverlays[fireball_collision].r1.left   = -1;
  566.                    MapOverlays[fireball_collision].r1.top    = -1;
  567.                    MapOverlays[fireball_collision].r1.right  = -1;
  568.                    MapOverlays[fireball_collision].r1.bottom = -1;
  569.                    MapOverlays[fireball_collision].tile =  -1;
  570.                    fireball_collision = -1;                         // reset
  571.                }
  572.  
  573.                // fireball_collision ctonains [i] of map over lay which was collided with
  574.             }
  575.             else
  576.             {
  577.                 if (explosion->getFrame() == 8)
  578.                     if (fireball_collision > -1)
  579.                        {
  580.                            // this should make the overlay dis-appear
  581.                            // before the fireball explosion is done
  582.                            MapOverlays[fireball_collision].x = -1;
  583.                            MapOverlays[fireball_collision].y = -1;
  584.  
  585.                            trees_destroyed++; // increment counter for trees destroyed
  586.                        }           
  587.             }
  588.  
  589.         }      
  590.  
  591.         // animate the firebolt
  592.         //if (fireball_active)
  593.         if (firebolt->getSTATE() == ACTIVE)
  594.         {  
  595.             // grab current location
  596.             tempX = firebolt->getX();
  597.             tempY = firebolt->getY();
  598.  
  599.             //move the sprite & animate it
  600.             firebolt->doMove(false);           
  601.             firebolt->nextFrame();
  602.  
  603.             // CHECK COLLISION 
  604.             // OVERLAYS & SPRITE
  605.             fireball_collision =  getOverlayCollided(firebolt->getRhombus());
  606.             if ( fireball_collision > -1 )
  607.             {
  608.                 // fireball collided
  609.                 firebolt->setMoveXY(0,0); // stop
  610.                 firebolt->setSTATE(INACTIVE);
  611.             }
  612.  
  613.             // see if firebolt hits the enemy sprite
  614.             if (orc->getSTATE() != INACTIVE)
  615.                 if ( rectCollision(orc->getRhombus(),firebolt->getRhombus()) )
  616.                 {
  617.                     // Orc was hit by fireball - apply damage
  618.                     // fireball collided
  619.                     firebolt->setMoveXY(0,0); // stop
  620.                     //fireball_active=false; // it explodes, so vanish
  621.                     firebolt->setSTATE(INACTIVE);
  622.                     fireball_collision=-1;
  623.  
  624.                     // apply damage
  625.                     int OFF = player->Combat->getOffense() + rollDice(6,3); // +3d6 for sword/firebolt
  626.                     int DEF = orc->Combat->getDefense();                   
  627.  
  628.                     // Apply damage
  629.                     if ((OFF - DEF) < 1)
  630.                     {
  631.                         // target missed
  632.                         PlaySound(sound_miss);
  633.                     }
  634.                     else
  635.                     {   // target hit
  636.                         // play sound - orc dying
  637.                         PlaySound(sound_orc_hit);
  638.  
  639.                         //apply the damage and see if still alive
  640.                         if ( orc->doDamage(OFF - DEF) ) // true=dying false=still alive
  641.                         {
  642.                             // orc is now dying
  643.                             orc->setSTATE(DYING);
  644.                             orc->setMoving(false);
  645.                             orc->setMoveXY(0,0);
  646.                            
  647.                             // set position of orc-dying
  648.                             orc_dying->setXY(orc->getX()-16,orc->getY()-16);
  649.                         }
  650.                     } // applying damage
  651.                 } // fireball collides with orc ...
  652.  
  653.             // see if firebolt hits the enemy sprite
  654.             if (mushy->getSTATE() != INACTIVE)
  655.                 if ( rectCollision(mushy->getRhombus(),firebolt->getRhombus()) )
  656.                 {
  657.                     // Orc was hit by fireball - apply damage
  658.                     // fireball collided
  659.                     firebolt->setMoveXY(0,0); // stop
  660.                     //fireball_active=false; // it explodes, so vanish
  661.                     firebolt->setSTATE(INACTIVE);
  662.                     fireball_collision=-1;
  663.  
  664.                     // apply damage
  665.                     int OFF = player->Combat->getOffense() + rollDice(6,3); // +3d6 for sword/firebolt
  666.                     int DEF = mushy->Combat->getDefense();                 
  667.  
  668.                     // Apply damage
  669.                     if ((OFF - DEF) < 1)
  670.                     {
  671.                         // target missed
  672.                         PlaySound(sound_miss);
  673.                     }
  674.                     else
  675.                     {   // target hit
  676.                         // play sound - mushy dying
  677.                         PlaySound(sound_orc_hit);
  678.  
  679.                         //apply the damage and see if still alive
  680.                         if ( mushy->doDamage(OFF - DEF) ) // true=dying false=still alive
  681.                         {
  682.                             // mushy is now dying
  683.                             mushy->setSTATE(DYING);
  684.                             mushy->setMoving(false);
  685.                             mushy->setMoveXY(0,0);
  686.                            
  687.                             // set position of mushy-dying
  688.                             mushy_dying->setXY(mushy->getX(),mushy->getY());
  689.                             // set its facing as well
  690.                             mushy_dying->setFacing(mushy->getFacing(),false);
  691.                         }
  692.                     } // applying damage
  693.                 } // fireball collides with mushy ...
  694.  
  695.             // check if the fireball hits a border
  696.             if ( firebolt->hitBorder() )
  697.                firebolt->setSTATE(INACTIVE);               
  698.  
  699.             // if fireball went inactive, explode it
  700.             if (firebolt->getSTATE() == INACTIVE)
  701.             {
  702.                 //explosion_active = true;
  703.                 explosion->setSTATE(ACTIVE);
  704.  
  705.                 tempX = firebolt->getX();
  706.                 tempY = firebolt->getY();
  707.                 switch(firebolt->getFacing())
  708.                 {
  709.                 case EAST: tempX += 24; tempY -=16; break;
  710.                 case NE:   tempX +=  8; tempY -=16; break;
  711.                 case SE:   tempX -= 16; tempY -=16; break;
  712.                 case NORTH:tempY -=32;
  713.                     break;
  714.                 case SOUTH:tempY += 8;
  715.                     break;
  716.                 case SW: tempX -= 16; tempY -=24;break;
  717.                 case NW: tempX -= 16; tempY -=16;break;
  718.                 case WEST: tempX -= 16; tempY -= 16;break;
  719.                 }
  720.                 // make it explode close to contact location
  721.  
  722.                 // use previous location of firebolt to explode, centered
  723.                 explosion->setXY(tempX,tempY);             
  724.  
  725.                 // BOOOMM!
  726.                 PlaySound(sound_exp);
  727.             }
  728.         }; // ... if fireball active
  729.  
  730.         // check status of ORC
  731.         switch(orc->getSTATE())
  732.         {
  733.             case INACTIVE: {
  734.                             // do nothing
  735.                            } break;
  736.             case ACTIVE:   {
  737.                             // see if he is close enough to attack
  738.                             if (findDistance(orc->getAnchor(), player->getAnchor()) <= 25.f) // pixels
  739.                             {
  740.                                 // make sure player isnt dead
  741.                                 if ( player->getSTATE() > CASTING )
  742.                                     break;
  743.  
  744.                                 // ATTACK
  745.                                 player->setMoveXY(0,0);
  746.                                 player->setMoving(false);
  747.                                 orc->setMoveXY(0,0);
  748.  
  749.                                 // set anim state
  750.                                 orc->setSTATE(ATTACKING);
  751.  
  752.                                 // set location on screen
  753.                                 orc_attacking->setXY(orc->getX(),orc->getY());
  754.  
  755.                                 // turn to face player
  756.                                 orc_attacking->setFacing( getDirection(player->getAnchor(), orc_attacking->getAnchor()),false);
  757.  
  758.                                 // exit from this switch statement
  759.                                 break;
  760.                             }
  761.  
  762.                             // animate and make sounds
  763.                             orc->nextFrame();
  764.  
  765.                            // see if player is "in sight"
  766.                            // try to head towards player
  767.                            if (  findDistance(orc->getAnchor(), player->getAnchor()) <= SIGHT_RANGE )
  768.                                if (rand() % 50 > 25)
  769.                                    {   // 50% chance of turning towards player
  770.                                        // possible direction towards Player
  771.                                        DIRS temp = getDirection(player->getScreenPT(), orc->getScreenPT());
  772.  
  773.                                        // set new direction and facing
  774.                                        orc->setFacing(temp,true);
  775.                                    }  
  776.  
  777.                             // check if not moving, move random
  778.                             if ( (orc->getMoveX() == 0) && (orc->getMoveY() == 0) )
  779.                             {          
  780.                                int oldDir = orc->getFacing();
  781.  
  782.                                tempX=tempY=0;
  783.                                int newDir;                             
  784.                                do
  785.                                {
  786.                                    newDir = rand() % 8 + 1; // random direction
  787.                                } while(newDir == oldDir);
  788.  
  789.                                // set new direction and facing
  790.                                orc->setNewDIR(newDir);
  791.                             }
  792.  
  793.                             // move
  794.                             orc->doMove(true);
  795.  
  796.                             // check for collision, sprite vs. overlays
  797.                             if ( collisionOverlays(orc->getRhombus()) )
  798.                             {
  799.                                 // cancel move
  800.                                 tempX = orc->getX() - orc->getMoveX();
  801.                                 tempY = orc->getY() - orc->getMoveY();
  802.                                 orc->setXY(tempX,tempY);
  803.  
  804.                                 // STOP moving
  805.                                 orc->setMoveXY(0,0);
  806.  
  807.                                 // reset anim - if moved
  808.                                 if (orc->getFrame() > 1)
  809.                                     orc->setFrame(1); // reset to initial frame of animation also
  810.  
  811.                                 // make sure orc is now stopped
  812.                                 orc->setMoving(false);
  813.                             }
  814.                            } break;
  815.             case ATTACKING:  
  816.                            {
  817.                             // make sure player isnt dead
  818.                             if ( player->getSTATE() > CASTING )
  819.                             {
  820.                                 // attack over
  821.                                 orc->setSTATE(ACTIVE);
  822.                                 break;
  823.                             }
  824.  
  825.                             // see if the attack is done
  826.                             if (orc_attacking->nextFrame() > 0)
  827.                             {
  828.                                 // attack finished
  829.                                 orc->setSTATE(ACTIVE);
  830.  
  831.                                 // see if target is still "in range"
  832.                                 if (findDistance(orc->getAnchor(), player->getAnchor()) > 25.f)
  833.                                     break; // break out if target is gone now
  834.                                
  835.                                 // just a test of sound
  836.                                 PlaySound(sound_sword);
  837.  
  838.                                 // apply damage
  839.                                 int OFF = orc->Combat->getOffense() + rollDice(6,3); // + 3d6
  840.                                 int DEF = player->Combat->getDefense();
  841.  
  842.                                 // Apply damage
  843.                                 if ((OFF - DEF) > 0)
  844.                                 {
  845.                                     if (player->doDamage(OFF - DEF) > 0)
  846.                                     {
  847.                                         // player is now dying ...
  848.                                         player->setSTATE(DYING);
  849.                                     }
  850.                                 }
  851.                                 else
  852.                                     PlaySound(sound_miss);
  853.                                     // its a miss .. play sounds
  854.                             }
  855.                            } break;
  856.             case DYING:    {
  857.                                 orc_dying->nextFrame(); // advance frame
  858.                                 if ( orc_dying->getFrame() == orc_dying->getLastFrame() )
  859.                                 {
  860.                                     orc->setSTATE(DEAD);
  861.                                     enemy_destroyed++;
  862.                                     orc->setDelay(0);
  863.                                 }
  864.                            } break;
  865.  
  866.             case DEAD:      {
  867.                                 orc->setDelay(orc->getDelay() + 1);
  868.                                 if (orc->getDelay() > DEATH_DELAY)
  869.                                 {
  870.                                     // orc grunts as he gets up
  871.                                     PlaySound(sound_orc_grunt);
  872.  
  873.                                     // wake up orc
  874.                                     orc->Heal();
  875.                                     orc->setSTATE(ACTIVE);
  876.                                     orc->setFrame(1);
  877.                                     orc->setDelay(3); // default delay
  878.                                 }
  879.                             }
  880.                             break;
  881.         } // end ORC state check
  882.  
  883.         switch(mushy->getSTATE())
  884.         {
  885.             case INACTIVE: {
  886.                             // do nothing
  887.                            } break;
  888.             case ACTIVE:   {
  889.                             // see if he is close enough to attack
  890.                             if (findDistance(mushy->getAnchor(), player->getAnchor()) <= 25.f) // pixels
  891.                             {
  892.                                 // make sure player isnt dead
  893.                                 if ( player->getSTATE() > CASTING )
  894.                                     break;
  895.  
  896.                                 // ATTACK
  897.                                 player->setMoveXY(0,0);
  898.                                 player->setMoving(false);
  899.                                 mushy->setMoveXY(0,0);
  900.  
  901.                                 // set anim state
  902.                                 mushy->setSTATE(ATTACKING);
  903.  
  904.                                 // set location on screen
  905.                                 mushy_attacking->setXY(mushy->getX(),mushy->getY());
  906.  
  907.                                 // turn to face player
  908.                                 mushy_attacking->setFacing( getDirection(player->getAnchor(), mushy_attacking->getAnchor()) ,false);
  909.                                 mushy_attacking->setFrame(1);
  910.  
  911.                                 // exit from this switch statement
  912.                                 break;
  913.                             }
  914.  
  915.                             // animate and make sounds
  916.                             mushy->nextFrame();
  917.  
  918.                             // check if not moving, move random
  919.                            // see if player is "in sight"
  920.                            // try to head towards player
  921.                             if (  findDistance(mushy->getAnchor(), player->getAnchor()) <= SIGHT_RANGE )
  922.                                 if (rand() % 50 > 25)
  923.                                    {
  924.                                        // possible direction towards Player
  925.                                        DIRS temp = getDirection( player->getScreenPT(),mushy->getScreenPT() );
  926.  
  927.                                        if (temp != mushy->getFacing())
  928.                                            mushy->setFacing(temp,true);
  929.                                    }
  930.  
  931.                             if ( (mushy->getMoveX() == 0) && (mushy->getMoveY() == 0) )
  932.                             {
  933.                                int newDir;                         
  934.                                do
  935.                                {
  936.                                    newDir= rand() % 8 + 1; // random direction
  937.                                } while(newDir == mushy->getFacing());
  938.                                // make sure its not the same move as last time
  939.                                // so he doesnt repeat same behavior
  940.                                mushy->setNewDIR(newDir);
  941.                             }
  942.  
  943.                             // move
  944.                             mushy->doMove(true);
  945.  
  946.                             // check for collision, sprite vs. overlays
  947.                             if ( collisionOverlays(mushy->getRhombus()) )
  948.                             {
  949.                                 // cancel move
  950.                                 tempX = mushy->getX() - mushy->getMoveX();
  951.                                 tempY = mushy->getY() - mushy->getMoveY();
  952.                                 mushy->setXY(tempX,tempY);
  953.  
  954.                                 // STOP moving
  955.                                 mushy->setMoveXY(0,0);
  956.  
  957.                                 // reset anim - if moved
  958.                                 if (mushy->getFrame() > 1)
  959.                                     mushy->setFrame(1); // reset to initial frame of animation also
  960.  
  961.                                 // make sure mushy is now stopped
  962.                                 mushy->setMoving(false);
  963.                             }
  964.                            } break;
  965.             case ATTACKING:  
  966.                            {
  967.                             // make sure player isnt dead
  968.                             if ( player->getSTATE() == DEAD )
  969.                             {   // attack over
  970.                                 mushy->setSTATE(ACTIVE);
  971.                                 break;
  972.                             }
  973.  
  974.                             // increment frame and
  975.                             // see if the attack is done
  976.                             if (mushy_attacking->nextFrame() > 0)
  977.                             {
  978.                                 // attack finished
  979.                                 mushy->setSTATE(ACTIVE);
  980.  
  981.                                 // see if target is still "in range"
  982.                                 if (findDistance(mushy->getAnchor(), player->getAnchor()) > 25.f)
  983.                                     break; // break out if target is gone now
  984.                                
  985.                                 // just a test of sound
  986.                                 PlaySound(sound_club);
  987.  
  988.                                 // apply damage
  989.                                 int OFF = mushy->Combat->getOffense() + rollDice(6,3); // + 3d6
  990.                                 int DEF = player->Combat->getDefense();
  991.  
  992.                                 // Apply damage
  993.                                 if ((OFF - DEF) > 0)
  994.                                 {
  995.                                     if (player->doDamage(OFF - DEF) > 0)
  996.                                     {
  997.                                         // player is now dying ...
  998.                                         player->setSTATE(DYING);
  999.                                     }
  1000.                                 }
  1001.                                 else
  1002.                                     PlaySound(sound_miss);
  1003.                                     // its a miss .. play sounds
  1004.                             }
  1005.                            } break;
  1006.             case APPEARING: {
  1007.                                 // Spawns in the mushroom man, from a tiny shroom
  1008.                                 if ( mushy_appearing->nextFrame() > 0)
  1009.                                 {
  1010.                                     mushy->setSTATE(ACTIVE);
  1011.                                     mushy->setXY(mushy_appearing->getX(),mushy_appearing->getY());
  1012.                                     mushy->setFacing(mushy_appearing->getFacing(),false);
  1013.                                 }
  1014.                            } break;
  1015.  
  1016.             case DYING:    {
  1017.                                 // advance frame
  1018.                                 if (mushy_dying->nextFrame() > 0)                              
  1019.                                 {
  1020.                                     mushy_dying->setFrame(mushy_dying->getLastFrame());
  1021.                                     mushy->setSTATE(DEAD);
  1022.                                     mushy->setDelay(0);
  1023.                                     enemy_destroyed++;
  1024.  
  1025.                                     // magic mushrooms, full hp & mana
  1026.                                     player->Heal();
  1027.                                     player->modMana(50); // caps out at max anyways
  1028.                                     PlaySound(sound_heal);
  1029.                                 }
  1030.                            } break;
  1031.             case DEAD:     {
  1032.                                 // corpse to stick around for a short while
  1033.                                 mushy->setDelay(mushy->getDelay() +1);
  1034.  
  1035.                                 if (mushy->getDelay() > DEATH_DELAY)
  1036.                                 {   // set it up for "next time"
  1037.                                     mushy->Heal();
  1038.                                     mushy->setSTATE(INACTIVE);
  1039.                                     mushy->setFrame(1);
  1040.                                     mushy->setDelay(3); // default delay
  1041.                                 }
  1042.                            }
  1043.                            break;
  1044.         } // end MUSHMAN state check
  1045.  
  1046.    }
  1047.    
  1048.    /* ----- DRAW SCREEN ----- */
  1049.  
  1050.    //     if (GetTickCount() - start >= 30)
  1051.  
  1052.     //start rendering
  1053.     if (d3ddev->BeginScene())
  1054.     {
  1055.         //erase the entire background
  1056.         d3ddev->ColorFill(backbuffer, NULL, D3DCOLOR_XRGB(0,0,0));
  1057.  
  1058.         //start sprite handler
  1059.         sprite_handler->Begin(D3DXSPRITE_ALPHABLEND);
  1060.  
  1061.         // draw tiles to back buffer -- using textures so it uses transparency properly
  1062.         DrawTilesBack();
  1063.  
  1064.         // draw the sprites ontop of the tiles
  1065.         DrawSprites();
  1066.  
  1067.         // special effects go last
  1068.         if (explosion->getSTATE() == ACTIVE)
  1069.         {  
  1070.             //create vector to update sprite position
  1071.             D3DXVECTOR3 position((float)explosion->getX(), (float)explosion->getY(), 0);
  1072.             //draw the explosion sprite
  1073.             sprite_handler->Draw(explosion->getImage(), &explosion->getRect(),NULL, &position,D3DCOLOR_XRGB(255,255,255));
  1074.         } // explosion
  1075.  
  1076.         // draw the game menu stuff
  1077.         drawMenu();
  1078.  
  1079.         //stop drawing
  1080.         sprite_handler->End();
  1081.  
  1082.         //stop rendering
  1083.         d3ddev->EndScene();
  1084.     } // if (d3ddev->BeginScene())
  1085.  
  1086.     //display the back buffer on the screen
  1087.     d3ddev->Present(NULL, NULL, NULL, NULL);
  1088.  
  1089.     //check for escape key (to exit program)
  1090.     if (KEY_DOWN(VK_ESCAPE))
  1091.         PostMessage(hwnd, WM_DESTROY, 0, 0);
  1092.  
  1093. }
  1094.  
  1095. //frees memory and cleans up before the game ends
  1096. void Game_End(HWND hwnd)
  1097. {
  1098.     Kill_Keyboard();
  1099.     Kill_Mouse();
  1100.  
  1101.     fflush(stdout);
  1102.     fclose(stdout);
  1103.  
  1104. //  if (dinput != NULL)
  1105. //      dinput->Release();
  1106.  
  1107.     if (sprite_handler != NULL)
  1108.         sprite_handler->Release();
  1109. }
  1110. // END ----------------------- GAME FUNCTIONS ----------------------- //
  1111.  
  1112. // test for collisions
  1113. int SpriteCollision(CSprite *sprite1, CSprite *sprite2)
  1114. {
  1115.     RECT rect1;
  1116.     rect1.left   = sprite1->getX() + 1;
  1117.     rect1.top    = sprite1->getY() + 1;
  1118.     rect1.right  = sprite1->getX() + sprite1->getWidth()  -1;
  1119.     rect1.bottom = sprite1->getY() + sprite1->getHeight() -1;
  1120.  
  1121.     RECT rect2;
  1122.     rect2.left   = sprite2->getX() + 1;
  1123.     rect2.top    = sprite2->getY() + 1;
  1124.     rect2.right  = sprite2->getX() + sprite2->getWidth()  -1;
  1125.     rect2.bottom = sprite2->getY() + sprite2->getHeight() -1;
  1126.  
  1127.     RECT dest;
  1128.     return IntersectRect(&dest, &rect1, &rect2);
  1129. }
  1130.  
  1131. // test for collisions
  1132. bool rectCollision(RECT r1, RECT r2)
  1133. {
  1134.  
  1135. return ( r1.left   < r2.right  &&
  1136.          r1.top    < r2.bottom &&
  1137.          r1.right  > r2.left   &&        
  1138.          r1.bottom > r2.top    );
  1139. }
  1140.  
  1141. // POINT lastCollision;
  1142. // returns TRUE if sprite rhombus overlays and overlays
  1143. // FALSE otherwise -- and returns TRUE on first collision
  1144. bool collisionOverlays(RECT dest_rhombus)
  1145. {
  1146.  // CHECK COLLISION // OVERLAYS & SPRITE
  1147.  
  1148.     // check if the given RECT overlaps with
  1149.     for (int i=0; i<= MAX_OVERLAYS; i++)
  1150.         if ( rectCollision(dest_rhombus, MapOverlays[i].r1) )
  1151.             return true;                       
  1152.  
  1153.     return false; // if no collisions found
  1154. }
  1155.  
  1156. //returns overlay # which has collided
  1157. int getOverlayCollided(RECT dest_rhombus)
  1158. {
  1159.     // CHECK COLLISION  // OVERLAYS & SPRITE
  1160.     for (int i=0; i<= MAX_OVERLAYS; i++)
  1161.         // check if the given RECT overlaps with
  1162.         if (rectCollision(dest_rhombus, MapOverlays[i].r1))
  1163.             return i;                      
  1164.  
  1165.     return -1; // if no collisions found
  1166. }
  1167.  
  1168.  
  1169. // returns a RECT for collisions
  1170. RECT getTileRECT(int x,int y) // screen xy
  1171. {
  1172.     RECT rhombus;
  1173.  
  1174.     // returns a rectangle pointing to rhombus of tile (based on the xy coords provided)
  1175.     int center = x + (TILEWIDTH /2); // 0 + 32
  1176.     rhombus.left  = center - (RHOMBUSWIDTH/2);
  1177.     rhombus.right = center + (RHOMBUSWIDTH/2);
  1178.  
  1179.     center = (y + TILEHEIGHT) - (RHOMBUSHEIGHT);
  1180.     rhombus.top    = center - (RHOMBUSHEIGHT/2);
  1181.     rhombus.bottom = center + (RHOMBUSHEIGHT/2);
  1182.  
  1183.     return rhombus;
  1184. }
  1185.  
  1186. //returns screen coordinates for a given x,y tile location
  1187. // which refers to where to draw the 64x64 sprite, so the bottom
  1188. // 64x32 rhombus lines up properly
  1189. POINT tile_to_world(int tilex, int tiley)
  1190. {
  1191.     POINT tPoint;
  1192.  
  1193.     tPoint.x = tilex * TILEWIDTH + (tiley & 1)*(TILEWIDTH/2);
  1194.     tPoint.y = tiley * (TILEHEIGHT/4);
  1195.  
  1196.     return tPoint;
  1197. }
  1198.  
  1199. //This function does the real work of drawing a single tile from the
  1200. //source image onto the backbuffer.
  1201. void DrawSprite(LPDIRECT3DTEXTURE9 source,  // source surface image
  1202.                 int tilenum,                // tile #
  1203.                 int width,                  // tile width (although ALL sprites used this way are 64x64)
  1204.                 int height,                 // tile height
  1205.                 int columns,                // columns of tiles
  1206.                 int destx,                  // destination x
  1207.                 int desty)                  // destination y
  1208. {
  1209.     //create a RECT to describe the source image
  1210.     RECT r1;
  1211.     r1.left = (tilenum % columns) * width;
  1212.     r1.top = (tilenum / columns) * height;
  1213.     r1.right = r1.left + width;
  1214.     r1.bottom = r1.top + height;
  1215.  
  1216.     // location to draw sprite
  1217.     D3DXVECTOR3 position((float)destx, (float)desty, 0);
  1218.  
  1219.     //draw the player sprite
  1220.     sprite_handler->Draw(
  1221.         source,
  1222.         &r1,
  1223.         NULL,
  1224.         &position,
  1225.         D3DCOLOR_XRGB(255,255,255));
  1226. }
  1227.  
  1228. // draws the layer #2 (bushes) and #3 (player objects)
  1229. void DrawSprites()
  1230. {
  1231.     int tiles_row=3; // tiles per row in sprite sheet
  1232.     int columns, rows;
  1233.     int x, y;
  1234.     POINT destPT;
  1235.  
  1236.     //calculate the number of columns and rows
  1237.     columns = MAPWIDTH;
  1238.     rows    = MAPHEIGHT;
  1239.    
  1240.     //draw tiles onto the scroll buffer surface
  1241.     for (y=0; y < rows; y++)
  1242.     {
  1243.         for (x=0; x < columns; x++)
  1244.         {          
  1245.             // convert from tile xy to screen xy
  1246.             destPT = tile_to_world(x,y);
  1247.  
  1248.             // LAYER #2 - TREES
  1249.             // cycle through overlays, to see if any share this tile x and y
  1250.             for (int i=0; i < MAX_OVERLAYS;i++)
  1251.                 if ( (MapOverlays[i].x == x) && (MapOverlays[i].y == y) )
  1252.                     {
  1253.                         // bushes are all 64x64 ... so we can ignore any offsetX,Y values -- they will center OK
  1254.                         DrawSprite(bushes, MapOverlays[i].tile ,TILEWIDTH,TILEHEIGHT,8,destPT.x,destPT.y);
  1255.                     }
  1256.  
  1257.             // LAYER #3 - SPRITES      
  1258.             // player
  1259.  
  1260.             // using basic rect collision detection - to determine if player sprite overlaps this tile
  1261.             RECT tRect;
  1262.             tRect.top    = player->getY()+1;           
  1263.             tRect.left   = player->getX()+1;
  1264.             tRect.bottom = tRect.top + (player->getHeight()*0.6) -4 ; // allows player to walk behind upper part of overlays, like trees
  1265.             tRect.right  = tRect.left + player->getWidth() -4;
  1266.             // rectangle around players sprite -- to check if it overlaps background sprites
  1267.     /* SPRITES */
  1268.         // PLAYER
  1269.             // compares player sprite RECT with a given tiles RECT
  1270.             if ( rectCollision(tRect, getTileRECT(destPT.x,destPT.y)) )
  1271.                 {
  1272.                     //create vector to update sprite position
  1273.                     D3DXVECTOR3 playerPos((float)player->getX(), (float)player->getY(), 0);
  1274.  
  1275.                     //draw the player sprite
  1276.                     switch(player->getSTATE())
  1277.                     {
  1278.                         case ACTIVE:
  1279.                             {
  1280.                                 sprite_handler->Draw(player->getImage(), &player->getRect(),NULL,&playerPos, D3DCOLOR_XRGB(255,255,255));
  1281.                             };
  1282.                             break;
  1283.                         case CASTING:
  1284.                             {
  1285.                                 sprite_handler->Draw(player_casting->getImage(), &player_casting->getRect(),NULL,&playerPos, D3DCOLOR_XRGB(255,255,255));
  1286.                             };
  1287.                             break;
  1288.                         case DYING:
  1289.                             {
  1290.                                 sprite_handler->Draw(player_dying->getImage(), &player_dying->getRect(),NULL,&playerPos, D3DCOLOR_XRGB(255,255,255));
  1291.                             };
  1292.                             break;
  1293.                         case DEAD:
  1294.                             {
  1295.                                 sprite_handler->Draw(player_dying->getImage(), &player_dying->getRect(),NULL,&playerPos, D3DCOLOR_XRGB(255,255,255));
  1296.                             };
  1297.                             break;
  1298.                     }
  1299.                 } // if ...
  1300.  
  1301.         // ORC
  1302.             // if orc is active and might overlap these tiles
  1303.             tRect.top    = orc->getY()+1;          
  1304.             tRect.left   = orc->getX()+1;
  1305.             tRect.bottom = tRect.top + (orc->getHeight()*0.6) -4 ; // allows orc to walk behind upper part of overlays, like trees
  1306.             tRect.right  = tRect.left + orc->getWidth() -4;
  1307.  
  1308.             // If the sprites RECT might collide with this tile, draw it --
  1309.             if ( rectCollision(tRect, getTileRECT(destPT.x,destPT.y)) )
  1310.                 switch(orc->getSTATE())
  1311.                 {
  1312.                     case INACTIVE: break; // do nothing
  1313.                     case ACTIVE:   {
  1314.                                     D3DXVECTOR3 position((float)orc->getX(), (float)orc->getY(), 0);
  1315.                                     sprite_handler->Draw(orc->getImage(), &orc->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1316.                                    } break;
  1317.                     case ATTACKING:  
  1318.                                    {
  1319.                                     D3DXVECTOR3 position((float)orc_attacking->getX(), (float)orc_attacking->getY(), 0);
  1320.                                     sprite_handler->Draw(orc_attacking->getImage(), &orc_attacking->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1321.                                    } break;
  1322.                     case DYING:    {
  1323.                                     D3DXVECTOR3 position((float)orc_dying->getX(), (float)orc_dying->getY(), 0);
  1324.                                     sprite_handler->Draw(orc_dying->getImage(), &orc_dying->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1325.                                    } break;
  1326.                     case DEAD:    {
  1327.                                     //orc_dying->setFrame(13); // last frame
  1328.                                     D3DXVECTOR3 position((float)orc_dying->getX(), (float)orc_dying->getY(), 0);
  1329.                                     sprite_handler->Draw(orc_dying->getImage(), &orc_dying->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1330.                                    } break;
  1331.                 }
  1332.  
  1333.             // if mushy is active and might overlap these tiles
  1334.             tRect.top    = mushy->getY()+1;        
  1335.             tRect.left   = mushy->getX()+1;
  1336.             tRect.bottom = tRect.top + (mushy->getHeight()*0.6) -4 ; // allows mushy to walk behind upper part of overlays, like trees
  1337.             tRect.right  = tRect.left + mushy->getWidth() -4;
  1338.  
  1339.             // If the sprites RECT might collide with this tile, draw it --
  1340.             if ( rectCollision(tRect, getTileRECT(destPT.x,destPT.y)) )
  1341.                 switch(mushy->getSTATE())
  1342.                 {
  1343.                     case INACTIVE: break; // do nothing
  1344.                     case ACTIVE:   {
  1345.                                     D3DXVECTOR3 position((float)mushy->getX(), (float)mushy->getY(), 0);
  1346.                                     sprite_handler->Draw(mushy->getImage(), &mushy->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1347.                                    } break;
  1348.                     case ATTACKING:  
  1349.                                    {
  1350.                                     D3DXVECTOR3 position((float)mushy_attacking->getX(), (float)mushy_attacking->getY(), 0);
  1351.                                     sprite_handler->Draw(mushy_attacking->getImage(), &mushy_attacking->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1352.                                    } break;
  1353.                     case DYING:    {
  1354.                                     D3DXVECTOR3 position((float)mushy_dying->getX(), (float)mushy_dying->getY(), 0);
  1355.                                     sprite_handler->Draw(mushy_dying->getImage(), &mushy_dying->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1356.                                    } break;
  1357.                     case APPEARING:{
  1358.                                     D3DXVECTOR3 position((float)mushy_appearing->getX(), (float)mushy_appearing->getY(), 0);
  1359.                                     sprite_handler->Draw(mushy_appearing->getImage(), &mushy_appearing->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1360.                                    } break;
  1361.                     case DEAD:    {
  1362.                                     D3DXVECTOR3 position((float)mushy_dying->getX(), (float)mushy_dying->getY(), 0);
  1363.                                     sprite_handler->Draw(mushy_dying->getImage(), &mushy_dying->getRect(),NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1364.                                    } break;
  1365.                 }
  1366.  
  1367.  
  1368.         // FIREBALL
  1369.             // create basic collision rect
  1370.             tRect.top    = firebolt->getY()+1;         
  1371.             tRect.left   = firebolt->getX()+1;
  1372.             tRect.bottom = tRect.top + (firebolt->getHeight()*0.6) -4 ;
  1373.             tRect.right  = tRect.left + firebolt->getWidth() -4;
  1374.  
  1375.             // if fireball is active and might overlap these tiles
  1376.             if (firebolt->getSTATE() == ACTIVE)
  1377.                 if ( rectCollision(tRect, getTileRECT(destPT.x,destPT.y)) )
  1378.                     {
  1379.                         //create vector to update sprite position
  1380.                         D3DXVECTOR3 position3((float)firebolt->getX(), (float)firebolt->getY(), 0);
  1381.  
  1382.                         //draw the firebolt sprite
  1383.                         sprite_handler->Draw(firebolt->getImage(), &firebolt->getRect(),NULL,&position3, D3DCOLOR_XRGB(255,255,255));
  1384.                     } // if ...
  1385.         } // for x ...
  1386.     } // for y ...
  1387. }
  1388.  
  1389. //This function fills the tilebuffer with tiles representing
  1390. //the current scroll display based on scrollx/scrolly.
  1391. // draws straight to backbuffer
  1392. void DrawTilesBack()
  1393. {
  1394.     int tiles_per_row=BACK_PER_ROW; // tiles per row in sprite sheet
  1395.     int columns, rows;
  1396.     int x, y; // destX,destY;
  1397.     int tilenum;
  1398.     POINT destPT;
  1399.    
  1400.     //calculate the number of columns and rows
  1401.     columns = MAPWIDTH;  //WINDOWWIDTH  / TILEWIDTH;      // 64 * 20 = 1280
  1402.     rows    = MAPHEIGHT; //WINDOWHEIGHT / (TILEHEIGHT/4); // 32 * 24 = 768
  1403.    
  1404.     //draw tiles onto the scroll buffer surface
  1405.     for (y=0; y < rows; y++)
  1406.     {
  1407.         for (x=0; x < columns; x++)
  1408.         {
  1409.             // skip tile #16 on odd rows
  1410.             if ((x+1 == columns) && (y & 1))
  1411.             {
  1412.                 // skip last tile on odd rows, so it fits on screen better
  1413.             }
  1414.             else
  1415.             {
  1416.                 // offset for isometric maps on odd rows
  1417.                 destPT = tile_to_world(x, y);
  1418.  
  1419.                 //retrieve the tile number from this position
  1420.                 tilenum = MAPDATA[(y) * MAPWIDTH + (x)];
  1421.  
  1422.                 // LAYER #1 - TILES
  1423.                 //draw the tile onto the scroll buffer
  1424.                 DrawSprite(tiles,tilenum,TILEWIDTH,TILEHEIGHT,tiles_per_row, destPT.x, destPT.y);
  1425.             }
  1426.         } // for x ...
  1427.     } // for y ...
  1428. } // drawtiles ...
  1429.  
  1430. float findDistance(POINT pt1,POINT pt2)
  1431. {
  1432.     float distance;
  1433.     float   dx = pt1.x - pt2.x;
  1434.     float   dy = pt1.y - pt2.y;
  1435.     distance = sqrt(dx*dx + dy*dy);
  1436.     return distance;
  1437. }
  1438.  
  1439. // roll dice
  1440. int rollDice(int Dice, int Number = 1)
  1441. {
  1442.     int sum=0;
  1443.     for (int i=1; i <= Number; i++)
  1444.         sum += rand() % Dice + 1;
  1445.  
  1446.     return sum;
  1447. };
  1448.  
  1449. // draw bar at location; using current, max # and color
  1450. void drawBar(int x, int y,int curr,int max, int color)
  1451. {
  1452.     // 0 for red, 1 for blue
  1453.     int width  = 64;
  1454.     int height = 6;
  1455.     RECT r1 =  {0,0,width,height};
  1456.  
  1457.     D3DXVECTOR3 position((float)x, (float)y, 0);
  1458.  
  1459.     //draw the sprite
  1460.     bars->setFrame(0);
  1461.     r1 = bars->getRect();
  1462.     sprite_handler->Draw(bars->getImage(), &r1,NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1463.  
  1464.     // draw bar %
  1465.     // percentage
  1466.     int pixels   = width * curr / max;
  1467.  
  1468.     // set frame for color
  1469.     bars->setFrame(color);
  1470.  
  1471.     r1 = bars->getRect();
  1472.     r1.right = r1.left + pixels;
  1473.     sprite_handler->Draw(bars->getImage(), &r1,NULL,&position, D3DCOLOR_XRGB(255,255,255));
  1474. }
  1475.  
  1476. // determine which direction to turn to face target
  1477. DIRS getDirection(POINT sourcePT, POINT targetPT)
  1478. {
  1479.     int a = 0; // centered horizontally
  1480.     // left to right
  1481.     if (sourcePT.x < targetPT.x) // left
  1482.         a = -1;
  1483.     if (sourcePT.x > targetPT.x) // right
  1484.         a = 1;
  1485.  
  1486.     int b = 0; // centered vertically
  1487.     // top to bottom
  1488.     if (sourcePT.y < targetPT.y) // above
  1489.         b = -1;
  1490.     if (sourcePT.y > targetPT.y) // below
  1491.         b = 1;
  1492.  
  1493.     switch(a)
  1494.     {
  1495.         case -1:
  1496.             if (b == -1)
  1497.                 { return NW; }
  1498.             else if (b == 1)
  1499.                 { return SW; }
  1500.             else
  1501.                 return WEST;
  1502.             break;
  1503.         case 1:
  1504.             if (b == -1)
  1505.                 { return NE; }
  1506.             else if (b == 1)
  1507.                 { return SE; }
  1508.             else
  1509.                 return EAST;
  1510.             break;
  1511.         case 0:
  1512.             if (b == -1)
  1513.                 { return NORTH; }
  1514.             else
  1515. //              if (b == 1)
  1516.                 { return SOUTH; }
  1517.             break;
  1518.     }
  1519.  
  1520.  
  1521. }
  1522.  
  1523. // draw numbers to screen
  1524. void drawNumbers(int number, int screenX, int screenY)
  1525. {
  1526.     int tile_width  = 18;
  1527.     int tile_height = 16;
  1528.  
  1529.     // no more than 10 digits I hope
  1530.     char buffer[99];// = itoa(number); //"1234";
  1531.     sprintf(buffer,"%d",number);
  1532.  
  1533.     int tempX=screenX;
  1534.     int tempY=screenY;
  1535.     int digit;
  1536.     int end = strlen(buffer);
  1537.  
  1538.     for (int cnt=0; cnt < end; cnt++)
  1539.     {      
  1540.         if (cnt >= 10)
  1541.             break;
  1542.         //create vector to update sprite position
  1543.         D3DXVECTOR3 position((float) tempX + (tile_width * cnt), (float)tempY, 0);
  1544.  
  1545.         char temp = buffer[cnt];
  1546.         digit = atoi(&temp);
  1547.        
  1548.         // draw the tile at location
  1549.         numbers->setFrame(digit);
  1550.         sprite_handler->Draw(numbers->getImage(), &numbers->getRect(),NULL, &position,D3DCOLOR_XRGB(255,255,255));
  1551.     }
  1552. }
  1553.  
  1554. // draws all the words and numbers that make up the games menu system
  1555. void drawMenu()
  1556. {
  1557.  
  1558.     int x = 0;
  1559.     int y = 0;
  1560.  
  1561.     RECT r1;
  1562.     r1.left     = x;
  1563.     r1.right    = x + 707;
  1564.     r1.top      = y;
  1565.     r1.bottom   = y + 27;
  1566.     d3ddev->StretchRect(text_header, NULL, backbuffer, &r1, D3DTEXF_NONE);
  1567.  
  1568.     // Level
  1569.     x=60;
  1570.     drawNumbers(level,x,y);    
  1571.  
  1572.     // PLAYER info
  1573.     // current HP
  1574.     x=200;
  1575.     drawNumbers(player->Combat->curHP(),x,y);  
  1576.     if (player->getSTATE() != DEAD)
  1577.     {
  1578.         drawBar(16+player->getX(),player->getY(),player->Combat->curHP(),player->Stats->fullHP(),5);  // red bar
  1579.         drawBar(16+player->getX(),player->getY()+6,player->curMana(),player->Stats->fullMana(),1);    // blue bar
  1580.     }
  1581.  
  1582.     // PLAYER Offense
  1583.     x+=60;
  1584.     drawNumbers(player->Combat->getOffense(),x,y);     
  1585.  
  1586.     // PLAYER Defense
  1587.     x+=60;
  1588.     drawNumbers(player->Combat->getDefense(),x,y); 
  1589.  
  1590.     // --- SCORE ---
  1591.     // ORC info
  1592.     drawNumbers(enemy_destroyed,600,y);    
  1593.     // Bushes
  1594.     drawNumbers(trees_destroyed,720,y);    
  1595.  
  1596.     // players mana
  1597. //  drawNumbers(player->curMana(),800,y);      
  1598.  
  1599.     // -- Health Bars -- //
  1600.     // ORC
  1601.     if (orc->getSTATE() != DEAD)
  1602.         drawBar(16+orc->getX(),orc->getY(),orc->Combat->curHP(),orc->Stats->fullHP(),5);
  1603.  
  1604.     // MUSHROOM MAN
  1605.     if ((mushy->getSTATE() != DEAD) && (mushy->getSTATE() != INACTIVE))
  1606.         drawBar(16+mushy->getX(),mushy->getY(),mushy->Combat->curHP(),mushy->Stats->fullHP(),5);
  1607.  
  1608. };