home *** CD-ROM | disk | FTP | other *** search
/ Media Share 13 / mediashare_13.zip / mediashare_13 / ZIPPED / PROGRAM / WTJ9403.ZIP / BARNHART / SO_BASE.CPP < prev    next >
C/C++ Source or Header  |  1993-11-21  |  12KB  |  453 lines

  1.  
  2. // so_base.cpp - Implements ScreenObj and ScreenItem classes
  3. //               Provides much of the functionality, especially painting,
  4. //                 for derived classes beneath
  5.  
  6. // Andy Barnhart
  7. // This program is provided as a sample to accompany an article on graphics
  8. //    animation using C++
  9. // This seems to work for me and I hope it works for you. No other warranty
  10. //    is expressed or implied.
  11.  
  12. #include <so_all.hpp>
  13.  
  14. // Set up our class statics
  15. HDC ScreenObj::hDC = 0;
  16. HWND ScreenObj::hWnd = 0;
  17. HANDLE ScreenObj::hInst = 0;
  18. ScreenObj *ScreenObj::Background = NULL;
  19.  
  20. // Some internal statics
  21. static HDC hMemDC1 =0;
  22. static HDC hMemDC2 =0;
  23. static HBITMAP hbmBlank = 0;
  24. static HBITMAP hbmPrev = 0;
  25.  
  26. // The screen size and location
  27. int WinX=0, WinY=0, WinSX=0, WinSY=0;
  28.                           
  29. // Initialize the screen and screen painting for this module                          
  30. void InitScreen( HWND hw, HDC hd, HANDLE hi)
  31.     {
  32.     
  33.     // We keep the DC open all the time and reuse it
  34.     ScreenObj::hDC = hd;
  35.     ScreenObj::hWnd = hw;
  36.     ScreenObj::hInst = hi;
  37.     
  38.     // We set up compatible DCs for later BitBlts
  39.     // This is important - everything being set up in advance helps us
  40.     //   paint fast enough during play
  41.     hMemDC1  = CreateCompatibleDC( hd);
  42.     hMemDC2  = CreateCompatibleDC( hMemDC1);
  43.     
  44.     // Load a blank bitmap for the background
  45.     hbmBlank = LoadBitmap( hi, "blank");
  46.     hbmPrev = SelectObject(hMemDC1, hbmBlank);
  47.     }
  48.  
  49. void TermScreen()
  50.     {
  51.     // Free up our memory DCs
  52.     if(hMemDC1)
  53.         {
  54.         if(hbmBlank)
  55.             {
  56.             SelectObject( hMemDC1, hbmPrev);
  57.             DeleteObject( hbmBlank);
  58.             }
  59.         DeleteDC( hMemDC1);
  60.         hMemDC1 = 0;
  61.         }
  62.     if(hMemDC2)
  63.         {
  64.         DeleteDC( hMemDC2);
  65.         hMemDC2 = 0;
  66.         }
  67.     }
  68.                              
  69. // If screen size changes, adjust. There is panning logic built in the
  70. //   game in so_play.cpp, but it is so slow on most systems that you will
  71. //   want to avoid using it. The code is left in as a sample, however.                             
  72. void SizeScreen( int x, int y)
  73.     {
  74.     static recurse = 0;
  75.     WinX = x;
  76.     WinY = y;
  77.     if(!ScreenObj::Background) return;
  78.     if( recurse) return;
  79.     recurse = 1;
  80.     if( WinX > ScreenObj::Background->width)
  81.         WinX -= ( WinX - ScreenObj::Background->width);
  82.     if( WinY > ScreenObj::Background->height)
  83.         WinY -= ( WinY - ScreenObj::Background->height);
  84.     if( (WinY != y) || (WinX != x) )
  85.         {
  86.         RECT rc;
  87.         GetWindowRect( ScreenObj::hWnd, &rc);
  88.         SetWindowPos( ScreenObj::hWnd, NULL, rc.left, rc.top,
  89.             (rc.right - rc.left) - (x - WinX),
  90.             (rc.bottom - rc.top) - (y - WinY), SWP_NOZORDER);
  91.         }
  92.     recurse = 0;
  93.     }
  94.      
  95. // Create ScreenObj and chain it in with others      
  96. ScreenObj::ScreenObj( int ix, int iy)
  97.     {
  98.     if( Background == NULL)
  99.         Background = this;
  100.     else
  101.         {
  102.         ScreenObj * so;
  103.         so = Background;
  104.         while (so->next != NULL) so = so->next;
  105.         so->next = this;
  106.         }
  107.     x=ix;
  108.     y=iy;
  109.     width=height=0;
  110.     hmult = wmult = 1;
  111.     shown=FALSE;
  112.     next = NULL;
  113.     }
  114.                    
  115. // Delete a ScreenObj and remove from the chain
  116. ScreenObj::~ScreenObj( void)
  117.     {
  118.     if( Background == this)
  119.         Background->next = this->next;
  120.     else
  121.         {
  122.         ScreenObj * so;
  123.         so = Background;
  124.         while (so->next != this) so = so->next;
  125.         so->next = this->next;
  126.         }
  127.     }
  128.      
  129. // Determine the distance between two ScreenObjs
  130. //  This is a convenience function so that an object that detects it will hit
  131. //  more than one other object during a move can decide which one it will hit
  132. //  first     
  133. int SODist( ScreenObj * SO1, ScreenObj * SO2)
  134.     {
  135.     int dx = 0, dy = 0;
  136.  
  137.     if( ((SO1->x + (SO1->width * SO1->wmult)) < SO2->x) ||
  138.             ( SO1->x > (SO2->x + (SO2->width * SO2->wmult) )) )
  139.         {
  140.         if( abs((SO1->x + (SO1->width * SO1->wmult)) - SO2->x) <
  141.                 abs(SO1->x - (SO2->x + (SO2->width * SO2->wmult) ) ) )
  142.             dx = abs((SO1->x + (SO1->width * SO1->wmult)) - SO2->x);
  143.         else
  144.             dx = abs( SO1->x - (SO2->x + (SO2->width * SO2->wmult) ));
  145.         }
  146.     if( ((SO1->y + (SO1->height * SO1->hmult)) < SO2->y) ||
  147.             ( SO1->y > (SO2->y + (SO2->height * SO2->hmult)) ))
  148.         {
  149.         if( abs((SO1->y + (SO1->height * SO1->hmult)) - SO2->y) <
  150.                 abs(SO1->y - (SO2->y + (SO2->height * SO2->hmult)) ) )
  151.             dy = abs((SO1->y + (SO1->height * SO1->hmult)) - SO2->y);
  152.         else
  153.             dy = abs( SO1->y - (SO2->y + (SO2->height * SO2->hmult)) );
  154.         }
  155.     return( dx + dy);
  156.     }
  157.         
  158. // ScreenBack - the screen background        
  159. ScreenBack::ScreenBack( int ix, int iy) : ScreenObj( 0, 0)
  160.     {
  161.     width = ix;
  162.     height = iy;
  163.     }
  164.  
  165. // Whenever we get called to Paint, we know we are first, so we just fill the
  166. //   bitmap with the black background color
  167. void ScreenBack::Paint( RECT *rc, HDC hPaintDC, BOOL memdc)
  168.     {
  169.     if( ! memdc)
  170.         FillRect( hPaintDC, rc, GetStockObject( BLACK_BRUSH));
  171.     else
  172.         {
  173.         RECT rcm;
  174.         rcm.top = rcm.left = 0;
  175.         rcm.bottom = rc->bottom - rc->top;
  176.         rcm.right = rc->right - rc->left;
  177.         FillRect( hPaintDC, &rcm, GetStockObject( BLACK_BRUSH));
  178.         }
  179.     }
  180.  
  181. // "Generic" ScreenItem Update will move toward a target if one has been 
  182. //   selected. If there is no where to move, it will return FALSE to 
  183. //   indicate it doesn't need to move anymore.
  184. ScreenItem::Update()
  185. {
  186.     if( ((x != targetx) || (y != targety)) && ( --periodct <= 0) )
  187.         {
  188.  
  189.         periodct = period;
  190.  
  191.         if( x > targetx)
  192.             {
  193.             x-= xinc;
  194.             if (x < targetx) x = targetx;
  195.             }
  196.         if( x < targetx)
  197.             {
  198.             x+= xinc;
  199.             if (x > targetx) x = targetx;
  200.             }
  201.         if( y > targety)
  202.             {
  203.             y-= yinc;
  204.             if (y < targety) y = targety;
  205.             }
  206.         if( y < targety)
  207.             {
  208.             y+= yinc;
  209.             if (y > targety) y = targety;
  210.             }
  211.  
  212.         if( shown)
  213.             Paint();
  214.         return( TRUE);
  215.         }
  216.     else
  217.         return( FALSE);
  218. }
  219.  
  220. // Derived objects will usually use ScreenObj's Paint functions, so they are
  221. //    highly functional. This first Paint determines the rectangle affected by
  222. //    the object, including it's last and current (after painting) position.
  223. //    It then uses the memory DC and calls all objects on the screen to Paint
  224. //    in it if needed (using the second Paint function). After all objects 
  225. //    which intersect the rectangle have painted into the memory bitmap, it is
  226. //    painted onto the screen. The Z order is maintained, and there is no 
  227. //    flickering. It's also pretty damn quick, if I may say so myself...
  228. void ScreenItem::Paint()
  229.     {
  230.     RECT rc;
  231.     ScreenObj * PntNxt;
  232.  
  233.     if( x < oldx)
  234.         rc.left = x;
  235.     else
  236.         rc.left = oldx;
  237.     if( y < oldy)
  238.         rc.top = y;
  239.     else
  240.         rc.top = oldy;
  241.     if( (x +width) < (oldx + width))
  242.         rc.right = oldx+width;
  243.     else
  244.         rc.right = x + width;
  245.     if( (y + height) < (oldy + height))
  246.         rc.bottom = oldy+height;
  247.     else
  248.         rc.bottom = y+height;
  249.  
  250.     for( PntNxt = Background; PntNxt != NULL; PntNxt = PntNxt->Next())
  251.                 PntNxt->Paint( &rc, hMemDC1, TRUE);
  252.     BitBlt( hDC,                        // dest DC
  253.                                 rc.left,rc.top,                  // dest origin
  254.                                 rc.right - rc.left, rc.bottom - rc.top,     // dest extents
  255.                                 hMemDC1,                     // src DC
  256.                                 0,0,                        // src origin
  257.                                 SRCCOPY );                  // ROP code
  258.  
  259.     oldx = x;
  260.     oldy = y;
  261.  
  262.     }
  263.  
  264.  
  265. // Paint into a memory DC (usually - sometimes it is the "real" main window DC
  266. //   if a WM_PAINT was received; then we are a little flashy. But we normally
  267. //   will not receive WM_PAINTs unless switching between apps.
  268. void ScreenItem::Paint( RECT *rc, HDC hPaintDC, BOOL memdc)
  269.     {
  270.     int icx, icy;
  271.         HBITMAP hbmOld;
  272.         
  273.     // If this object is hidden, or doesn't intersect the region, bail out!
  274.     if( !shown || !InRect( rc)) return;                                    
  275.     
  276.     // If it is a memory DC, it is only as large as the rect, and its origin
  277.     //   (logically) is the location of the rect. It's faster to do the math
  278.     //   than set and reset origins through the API (although we do that for
  279.     //   panning, but that's a whole other story)
  280.     if( memdc)
  281.         {
  282.         icx = x - rc->left;
  283.         icy = y - rc->top;
  284.         }
  285.     else
  286.         {
  287.         icx = x;
  288.         icy = y;
  289.         }
  290.  
  291.         hbmOld = SelectObject(hMemDC2, hbmImage);
  292.  
  293.         if(hbmOld)
  294.         {
  295.         // Blit the image in the new position
  296.         BitBlt( hPaintDC,                   
  297.                 icx, icy,         
  298.                 width,height,     
  299.                 hMemDC2,          
  300.                 0,0,SRCPAINT );
  301.  
  302.         SelectObject(hMemDC2, hbmOld);
  303.         }
  304.     }
  305.                       
  306. // Select a bitmap as the current bitmap for an object
  307. // This function does more work that necessary. I thought about adding logic
  308. //   for "non rectangular" hit detection by checking the bitmap bits in a DBI
  309. //   bitmap. I never got around to adding such logic, but I decided to leave
  310. //   what I had in, since this is only a sample...                      
  311. void ScreenItem::SelectBM( HBITMAP hb)
  312.     {
  313.     typedef struct xxxx
  314.         {
  315.         BITMAPINFO bminfo;
  316.         RGBQUAD r[15];
  317.         } xxx;
  318.  
  319.     xxx x;
  320.  
  321.     BITMAP bm;
  322.  
  323.     hbmImage = hb;
  324.     // Get bitmap size
  325.     GetObject(hbmImage, sizeof(bm), (LPSTR)&bm);
  326.     if( (hbits == NULL) || (width != bm.bmWidth) || (height != bm.bmHeight) ||
  327.             (linewidth != bm.bmWidthBytes) || (planes != bm.bmPlanes) )
  328.         {
  329.         width = bm.bmWidth;
  330.         height = bm.bmHeight;
  331.         linewidth = bm.bmWidthBytes;
  332.         if( hbits != NULL)
  333.             {
  334.             GlobalUnlock( hbits);
  335.             GlobalFree( hbits);
  336.             bits = NULL;
  337.             }
  338.         bitslen = height * linewidth;
  339.         planes = bm.bmPlanes;
  340.         hbits = GlobalAlloc( GMEM_MOVEABLE, bitslen);
  341.         bits = (BYTE far *) GlobalLock( hbits);
  342.         }
  343.     if( bits == NULL) return;
  344. //    GetBitmapBits( hbmImage, bitslen, bits);
  345.     x.bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER) + (16 * sizeof(RGBQUAD));
  346.     x.bminfo.bmiHeader.biWidth = width;
  347.     x.bminfo.bmiHeader.biHeight = height;
  348.     x.bminfo.bmiHeader.biPlanes = 1;
  349.     x.bminfo.bmiHeader.biBitCount = 4;
  350.     x.bminfo.bmiHeader.biCompression = BI_RGB;
  351.     x.bminfo.bmiHeader.biSizeImage = bitslen;
  352.     x.bminfo.bmiHeader.biXPelsPerMeter = 3072;
  353.     x.bminfo.bmiHeader.biYPelsPerMeter = 2048;
  354.     x.bminfo.bmiHeader.biClrUsed = 0;
  355.     x.bminfo.bmiHeader.biClrImportant = 0;
  356.     GetDIBits(hDC, hbmImage, 0, height, bits, &x.bminfo, DIB_RGB_COLORS);
  357.     }
  358.                                                   
  359. // ScreenItems are SreenObjs with only slightly higher functionality; they are
  360. //   aware of the resource to be loaded                                                  
  361. ScreenItem::ScreenItem( char *res, int ix, int iy, int incx, int incy) : ScreenObj( ix, iy)
  362.     {
  363.     hbits = NULL;
  364.     bits = NULL;
  365.     if( res != NULL)
  366.         {
  367.         HBITMAP hb;
  368.         hb = LoadBitmap( hInst, res );
  369.         SelectBM( hb);
  370.         }
  371.     else
  372.         hbmImage = NULL;
  373.     oldx=targetx=x;
  374.     oldy=targety=y;
  375.     xinc = incx;
  376.     yinc = incy;
  377.     periodct = period = 1;
  378.     SOType = NULL;
  379.     }
  380.  
  381. ScreenItem::~ScreenItem( void)
  382.     {
  383.     if( hbmImage ) DeleteObject(hbmImage);
  384.     if( hbits != NULL)
  385.         {
  386.         GlobalUnlock( hbits);
  387.         GlobalFree( hbits);
  388.         bits = NULL;
  389.         }
  390.     }
  391.  
  392. void ScreenItem::SetTarget( int tx, int ty)
  393.     {
  394.     targetx=tx - width/2;
  395.     targety=ty - height/2;
  396.     if(targetx > Background->width) targetx = Background->width;
  397.     if(targety > Background->height) targety = Background->height;
  398.     if(targetx < 0) targetx = 0;
  399.     if(targety < 0) targety = 0;
  400.     }
  401.  
  402. StillMotion::StillMotion( char *res, int ix, int iy, int nframes, int per)
  403.         : ScreenItem(NULL, ix, iy)
  404.     {
  405.     int i;
  406.     char acIc[32];
  407.     int len;
  408.      
  409.     SOType = TYPE_HAZARD;
  410.     if( nframes > 8)
  411.         numframes = 8;
  412.     else
  413.         numframes = nframes;
  414.  
  415.     xinc=yinc=0;
  416.     strcpy( acIc, res);
  417.     len = strlen( acIc);
  418.     strcat( acIc, "0");
  419.  
  420.     for(i = 0; i < numframes; i++)
  421.         {
  422.         BITMAP bm;                          // Bitmap info
  423.         acIc[len]++;
  424.         frames[i] = LoadBitmap( hInst, acIc);
  425.         }
  426.     curframe = 0;
  427.     periodct = period = per;
  428.     SelectBM( frames[0]);
  429.     }
  430.  
  431. StillMotion::~StillMotion( void)
  432.     {
  433.     int i;
  434.     for(i = 0; i < numframes; i++)
  435.         {
  436.         if (frames[i]) DeleteObject( frames[i]);
  437.         }
  438.     hbmImage = NULL;
  439.     }
  440.  
  441. BOOL StillMotion::Update( void)
  442.     {
  443.     if( --periodct <= 0)
  444.         {
  445.         if( (++curframe >= numframes) || (hbmImage == NULL) )
  446.             curframe = 0;
  447.         SelectBM( frames[curframe]);
  448.         periodct = period;
  449.         Paint();
  450.         }
  451.     return( TRUE);
  452.     }
  453.