Chris Pollett >
Old Classes >
PIC197

   ( Print View )

Enrollment info

Course Info:


Homework Assignments:
Practice Exams: PIC:
                                   












HW2 Solutions Page

Return to homework page.

//Only modified files from demo03 from book displayed.

//defines.h: essential defines for Invader game
//Copyright Ian Parberry, 1999
//Last updated April 3, 2000
//modified for invader porblem by Chris Pollett 2001

#ifndef __DEFINES_H__
#define __DEFINES_H__

#define SCREEN_WIDTH 640 //pixels wide
#define SCREEN_HEIGHT 480 //pixels high
#define COLORS 256 //number of colors
#define COLOR_DEPTH 8 //number of bits to store colors
#define TRANSPARENT_COLOR 255 //transparent palette position

//
//mod's for invaders
//

#define BUNKER_NUM 3 // number of bunkers

#define BUNKER_OFFSET SCREEN_WIDTH/(BUNKER_NUM+1) // space b/w bunkers

#define MIN_LEFTPOS SCREEN_WIDTH/(2*BUNKER_NUM) // farthest invaders/player can
                                                // move left

#define MAX_RIGHTPOS SCREEN_WIDTH-MIN_LEFTPOS // farthest invaders/player can
                                              // move right


#endif

// File Name: InvaderGroup.h
//
// Class Name: CInvaderGroup
//
// Purpose: acts as a container for a space invader CObjects
//          Has methods to push and remove invaders. Also
//          has methods to draw the whole invader group and
//          update whole groups position.
//

#ifndef __CIGRP__
#define __CIGRP__

#include <vector>
using namespace std;

#include "objects.h"
#include "defines.h"

#define MAX_INVGRPROWS 3
#define MAX_INVGRPCOLS 4

class CInvaderGroup
{
	private:

	vector<CObject> v; // holds invaders stored in this group
                           // CObject are relatively small
	                   // if were larger should use pointer to a CObject

	int xVel; //left right speed of invaders
	int yOffset; // how far invaders move down when reach a side

	public:

	CInvaderGroup() {yOffset=0;
	                 xVel=-1;} //constructor

        void draw(LPDIRECTDRAWSURFACE surface); //draw invaders to surface

        void create(int x,int y,CBaseSprite* inv[MAX_INVGRPROWS]); //create
                                                                   //instance
                             //note: probably would be better to have inv
                             //be a vector.

        void move(); //make a move depending on time and speed

        void remove(int i){ v.erase(&(v[i]));} //remove object from invader
                                               // group

        void push(CObject o) {v.push_back(o);} //add invader ot group

	void clear(){ v.clear();} //delete all invaders from group

	int getSize() { return v.size();} //return the number of invaders in
                                          //group
	int getTop(); // y-position of top most invaders
	int getBottom(); // y-position of bottom most invaders
	int getRight(); // x-position of rightmost invader
	int getLeft(); // x-position of leftmost invader
};

#endif

//
// Filename: InvaderGroup.cpp
//
// Purpose: contains member function defintions for CInvaderGroup
//

#include "invadergroup.h"

//
// Member Function: draw
//
// Purpose: draws each invader in the group to the designated surface
//

void CInvaderGroup::draw(LPDIRECTDRAWSURFACE surface)
{
	vector<CObject>::iterator i;
	for(i=v.begin(); i !=v.end(); i++)
		i->draw(surface);

}

//
// Member Function: create
//
// Purpose: creates a ``default'' space invader group. (As opposed
//          to doing push external to the class to make a designer
//          one.) The default group is horizontally centered at x and its last
//          row is at height y. inv is the array of sprite images for
//          the different rows of the group


void CInvaderGroup::create(int x,int y, CBaseSprite* inv[MAX_INVGRPROWS])
{
	CObject obj;
	int xOld;
	int xOffset = SCREEN_WIDTH/(1.5*MAX_INVGRPCOLS); //initial x spacing
                                                         //of invaders

	xVel = -1; //initial invader velocity

	yOffset = y/(MAX_INVGRPROWS); // vertical spacing b/w invaders

	x -= (xOffset * MAX_INVGRPCOLS/2); //passed x in assumed to be
                                           //center of invader group
                                           //so now get left side

	xOld=x;

	for(int i=0; i<MAX_INVGRPROWS; i++)
	{
		for(int j=0; j<MAX_INVGRPCOLS; j++)
		{
		   obj.create(x, y, xVel,0,inv[i]);
		   push(obj);
		   x+=xOffset;

		}
		x = xOld;
		y-= yOffset;
	}

	yOffset /=4; // yOffset now used to control how far invaders
                     // move down when reach a side of screen.
}

//
// Member Function: move
//
// Purpose: updates the position of the invader group. Causes group to
//          switch direction and move down if reach side of screen.
//

void CInvaderGroup::move()
{
	vector<CObject>::iterator i;
	for(i=v.begin(); i != v.end(); i++)
	{
		i->move();
	}

	if(getLeft() <MIN_LEFTPOS || getRight() > MAX_RIGHTPOS)
	{
		xVel=-xVel;
		for(i=v.begin(); i != v.end(); i++)
		{
			i->setXVel(xVel);
			i->setY(i->getY()+yOffset);
		}


	}
}

//
// Coordinate member functions:
//
// The next four member functions are used to get the coordinates of
// bounding rectangle of the invader group. Could here could easily
// be improved.
//

int CInvaderGroup::getTop()
{
	vector<CObject>::iterator i;
	int top = SCREEN_HEIGHT;
	int tmp;
	for(i=v.begin(); i != v.end(); i++)
	{
	   tmp = i->getY();
	   if(top > tmp) top=tmp;
	}
	return top;
}


int CInvaderGroup::getBottom()
{
 	vector<CObject>::iterator i;
	int bot = 0;
	int tmp;
	for(i=v.begin(); i != v.end(); i++)
	{
	   tmp = i->getY();
	   if(bot < tmp) bot=tmp;
	}
	return bot;
}

int CInvaderGroup::getLeft()
{
	vector<CObject>::iterator i;
	int left = SCREEN_WIDTH;
	int tmp;
	for(i=v.begin(); i != v.end(); i++)
	{
	   tmp = i->getX();
	   if(left > tmp) left=tmp;
	}
	return left;
}

int CInvaderGroup::getRight()
{
	vector<CObject>::iterator i;
	int right = 0;
	int tmp;
	for(i=v.begin(); i != v.end(); i++)
	{
	   tmp = i->getX();
	   if(right < tmp) right=tmp;
	}
	return right;

}

//objects.h: header file for CObject class


//
// modified with set and get methods for speed and velocity --cpollett 2001
// also added stuff to cycle through sprite frames (similar to later demos).
//

//Copyright Ian Parberry, 1999
//Last updated September 28, 1999

#ifndef __OBJECTS__
#define __OBJECTS__

#include "bsprite.h"

class CObject{ //class for a moving object
  private:
    int m_nX,m_nY; //current location
    int m_nXspeed,m_nYspeed; //current speed
    int m_nLastXMoveTime; //last time the object moved

    int m_nCurrentFrame; //-CP what frame we're on

    int m_nLastFrameTime; //-CP added a variable holding last time
                          // animation changed

    CBaseSprite *m_pSprite; //pointer to sprite
  public:
    CObject(); //constructor
    void draw(LPDIRECTDRAWSURFACE surface); //draw
    void create(int x,int y,int xspeed,int yspeed,
      CBaseSprite *sprite); //create instance
    void accelerate(int xdelta,int ydelta=0); //change speed
    void move(); //make a move depending on time and speed

	//modified code -CP
        //various set and get methods

	int getX() {return m_nX;}
	int getY() {return m_nY;}
	void setX(int x) {m_nX = x;}
	void setY(int y) {m_nY = y;}
	int getXVel() {return m_nXspeed;}
	int getYVel() {return m_nYspeed;}
	void setXVel(int x) {m_nXspeed = x;}
	void setYVel(int y) {m_nYspeed = y;}



};

#endif

//objects.cpp

//Copyright Ian Parberry, 1999
//Last updated September 28, 1999

#include "objects.h"
#include "timer.h" //game timer

extern CTimer Timer;

CObject::CObject(){ //constructor
  m_nX=m_nY=m_nXspeed=m_nYspeed=0;
  m_pSprite=NULL; m_nLastXMoveTime=0;
  m_nCurrentFrame=0;
  m_nLastFrameTime=0;
}

void CObject::draw(LPDIRECTDRAWSURFACE surface){ //draw
  m_pSprite->draw(m_nCurrentFrame,m_nX,m_nY,surface);
}

void CObject::create(int x,int y,int xspeed,int yspeed,
                     CBaseSprite *sprite){
  m_nLastXMoveTime=0; //time
  m_nLastFrameTime=0;
  m_nCurrentFrame=0;
  m_nX=x; m_nY=y; //location
  m_nXspeed=xspeed; m_nYspeed=yspeed; //speed
  m_pSprite=sprite; //image
}

void CObject::accelerate(int xdelta,int ydelta){
  //change speed
  m_nXspeed+=xdelta; m_nYspeed+=ydelta;
}

void CObject::move(){ //move object
  const int XSCALE=8; //to scale back horizontal motion
  const int MARGIN=100; //margin on outside of page
  const int FRAME_RATE=500;
  int xdelta; //change in position
  int time=Timer.time(); //current time

  if(time-m_nLastFrameTime > FRAME_RATE) //-CP advance a frame of animation
  {                                      // if enough time has passed
      m_nCurrentFrame++;
      if(m_nCurrentFrame>= m_pSprite->frame_count())
             m_nCurrentFrame = 0;
  	  m_nLastFrameTime = time;
  }
  //horizontal motion
  int tfactor=time-m_nLastXMoveTime; //time since last move
  xdelta=(m_nXspeed*tfactor)/XSCALE; //x distance moved
  m_nX+=xdelta; //x motion
  if(m_nX<-MARGIN)m_nX=SCREEN_WIDTH+MARGIN; //wrap left
  if(xdelta||m_nXspeed==0) //record time of move
    m_nLastXMoveTime=time;
  if(m_nX>SCREEN_WIDTH+MARGIN)m_nX=-MARGIN; //wrap right
}

//
//Filename: main.cpp
//
//Purpose: Main window loop. Loads in Graphics controls player and
//         inavder group
//

//system includes
#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#include <stdio.h>
#include <stdlib.h>


//system defines
#define WIN32_LEAN_AND_MEAN

//custom includes
#include "defines.h" //global definitions
#include "bmp.h" //bmp file reader
#include "timer.h" //game timer
#include "bsprite.h" //for base sprite class
#include "objects.h" //for object class
#include "invadergroup.h" //for space invader group class

//globals

BOOL ActiveApp; //is this application active?

LPDIRECTDRAW lpDirectDrawObject=NULL; //direct draw object
LPDIRECTDRAWSURFACE lpPrimary=NULL; //primary surface
LPDIRECTDRAWPALETTE lpPrimaryPalette; //its palette
LPDIRECTDRAWSURFACE lpSecondary=NULL; //back buffer
LPDIRECTDRAWPALETTE lpSecondaryPalette; //its palette
LPDIRECTDRAWSURFACE lpBackground=NULL; //background image

CTimer Timer; //game timer

CBmpFileReader background; //background image
CBmpSpriteFileReader g_cSpriteImages; //sprite images

CObject spaceship; //spaceship object
CBaseSprite *spaceshipsprite=NULL; //spaceship sprite

CObject bunkers[BUNKER_NUM];
CBaseSprite *bunkersprite=NULL; //bunker sprite

CInvaderGroup invaders;
CBaseSprite *invgrpsprite[MAX_INVGRPROWS]; // invader sprites
int minInvHeight; // as low as invaders allowed to go

//helper functions

LPDIRECTDRAWPALETTE CreatePalette(LPDIRECTDRAWSURFACE surface);
BOOL InitDirectDraw(HWND hwnd);
HWND CreateDefaultWindow(char* name,HINSTANCE hInstance);

BOOL LoadSpaceShipSprite(){ //load player's spaceship image
  return spaceshipsprite->load(&g_cSpriteImages,0,1,1);
} //LoadSpaceShipSprite

BOOL LoadBunkerSprite(){ //load bunker image
  return bunkersprite->load(&g_cSpriteImages,0,121,1);
} //LoadBunkerSprite

BOOL LoadInvGrpSprite(){ //load invader group images
  int i;
  for(i=0; i < MAX_INVGRPROWS; i++)
  {
  	switch( i%3)
  	{
  	  case 0:
       if(!invgrpsprite[i]->load(&g_cSpriteImages,0,1,73))
                   return FALSE;
       if(!invgrpsprite[i]->load(&g_cSpriteImages,1,121,73))
                   return FALSE;
       break;
  	  case 1:
       if(!invgrpsprite[i]->load(&g_cSpriteImages,0,244,73))
                   return FALSE;
       if(!invgrpsprite[i]->load(&g_cSpriteImages,1,365,73))
                   return FALSE;
       break;
  	  case 2:
       if(!invgrpsprite[i]->load(&g_cSpriteImages,0,244,1))
                   return FALSE;
       if(!invgrpsprite[i]->load(&g_cSpriteImages,1,365,1))
                   return FALSE;
       break;

    }
  }
  return TRUE;
} //LoadInvGrpSprite


BOOL LoadImages(){ //load graphics from files to surfaces
  int i;

  //get the background image
  if(!background.load("bckgnd.bmp"))return FALSE; //read from file
  background.draw(lpBackground); //draw to background surface
  //set palettes in all surfaces
  if(!background.setpalette(lpPrimaryPalette))return FALSE;
  if(!background.setpalette(lpSecondaryPalette))return FALSE;
  //load the plane sprite
  if(!g_cSpriteImages.load("sprites.bmp"))return FALSE;
  spaceshipsprite=new CBaseSprite(1,121,72);
  if(!LoadSpaceShipSprite())return FALSE; //load spaceship images
  bunkersprite=new CBaseSprite(1,121,72);
  if(!LoadBunkerSprite())return FALSE; //load bunker images

  for(i=0; i<MAX_INVGRPROWS; i++)
     invgrpsprite[i] = new CBaseSprite(2,115,72);
  if(!LoadInvGrpSprite())return FALSE; //load invader group images

  return TRUE;
} //LoadImages

void CreateObjects(){
  int i; //don't mess with MST vs. rest of the world counter vars
  int sHeight= spaceshipsprite->height();

  spaceship.create(SCREEN_WIDTH/2,SCREEN_HEIGHT,
                    0,0,spaceshipsprite); //create plane


  for(i=0; i < BUNKER_NUM; i++)
  	 bunkers[i].create((i+1)*BUNKER_OFFSET,SCREEN_HEIGHT-sHeight,
  	                         0,0,bunkersprite);

  minInvHeight = SCREEN_HEIGHT - sHeight - bunkersprite->height();
  invaders.create(SCREEN_WIDTH*2/3,SCREEN_HEIGHT*1/2, invgrpsprite);

} //CreateObjects

BOOL RestoreSurfaces(){ //restore all surfaces
  BOOL result=TRUE, flag=FALSE;
  int i;

  if(SUCCEEDED(lpPrimary->Restore())) //if primary restored
    result=result&&background.draw(lpPrimary)&& //redraw image
      background.setpalette(lpPrimaryPalette); //set palette
  else return FALSE;
  if(SUCCEEDED(lpSecondary->Restore())) //if secondary restored
    result=result&&background.draw(lpSecondary)&& //redraw image
      background.setpalette(lpSecondaryPalette); //set palette
  else return FALSE;
  if(SUCCEEDED(lpBackground->Restore())) //if background restored
    result=result&&background.draw(lpBackground); //redraw image
  else return FALSE;
  if(spaceshipsprite->Restore()) //if spaceship sprite restored
    result=result&&LoadSpaceShipSprite(); //redraw image
  else return FALSE;
  if(bunkersprite->Restore()) //if bunker sprite restored
    result=result&&LoadBunkerSprite(); //redraw image
  for(i=0; i < MAX_INVGRPROWS; i++)
  {
    if(invgrpsprite[i]->Restore()) //if invader sprites restored
		flag = TRUE;
    else return FALSE;
  }

  if(flag) result=result&&LoadBunkerSprite(); //redraw image
  else return FALSE;
  return result;
} //RestoreSurfaces

BOOL PageFlip(){ //return TRUE if page flip succeeds
  if(lpPrimary->Flip(NULL,DDFLIP_WAIT)==DDERR_SURFACELOST)
    return RestoreSurfaces();
  return TRUE;
} //PageFlip

BOOL ComposeFrame(){ //compose a frame of animation
  int i;

  //draw background
  lpSecondary->BltFast(0,0,lpBackground,NULL,
    DDBLTFAST_NOCOLORKEY|DDBLTFAST_WAIT);
  //move spaceship
  spaceship.move();

  //
  // make sure space ship stays in bounds
  //

  if(spaceship.getX() < MIN_LEFTPOS)
  {
          spaceship.setX(MIN_LEFTPOS);
	      spaceship.setXVel(0);
  }
  if(spaceship.getX() > MAX_RIGHTPOS)
  {
          spaceship.setX(MAX_RIGHTPOS);
	      spaceship.setXVel(0);
  }

  // move invaders
  invaders.move();

  //
  // make sure invaders stays in bounds
  //

  if(invaders.getBottom() > minInvHeight)
  {
    invaders.clear();
    invaders.create(SCREEN_WIDTH*2/3,SCREEN_HEIGHT*1/2, invgrpsprite);
	Timer.start();
  }


  //draw objects
  spaceship.draw(lpSecondary);

  for(i=0; i < BUNKER_NUM; i++)
  	 bunkers[i].draw(lpSecondary);

  invaders.draw(lpSecondary);

  return TRUE;
} //ComposeFrame

BOOL ProcessFrame(){ //process a frame of animation
  ComposeFrame(); //compose a frame in secondary surface
  return PageFlip(); //flip video memory surfaces
} //ProcessFrame

BOOL keyboard_handler(WPARAM keystroke){ //keyboard handler
  BOOL result=FALSE; //return TRUE if game is to end
  switch(keystroke){
    case VK_ESCAPE: result=TRUE; break; //exit game


	case VK_SPACE: //delete a random invader
		 invaders.remove(rand()%invaders.getSize());
	break;

	//
        //space ship controls
	//

	case VK_LEFT:
	     spaceship.accelerate(-1);
	break;

	case VK_RIGHT:
	     spaceship.accelerate(1);
	break;

	case VK_DOWN:
	  spaceship.setXVel(0);
	break;


    default: break;
  }
  return result;
} //keyboard_handler

//message handler (window procedure)
long CALLBACK WindowProc(HWND hwnd,UINT message,
                         WPARAM wParam,LPARAM lParam){
  switch(message){
    case WM_ACTIVATEAPP: ActiveApp=wParam; break;
    case WM_CREATE: break;
    case WM_KEYDOWN: //keyboard hit
      if(keyboard_handler(wParam))DestroyWindow(hwnd);
      break;
    case WM_DESTROY: //end of game
      if(lpDirectDrawObject!=NULL){ //if DD object exists
        if(lpSecondary!=NULL) //if secondary surface exists
          lpSecondary->Release(); //release secondary surface
        if(lpPrimary!=NULL) //if primary surface exists
          lpPrimary->Release(); //release primary surface
        if(lpBackground!=NULL) //if background exists
          lpBackground->Release(); //release background
        if(spaceshipsprite!=NULL) //if spaceship sprite exists
          spaceshipsprite->Release(); //release spaceship sprite
        delete spaceshipsprite; //delete spaceship sprite
        if(bunkersprite!=NULL) //if bunker sprite exists
          bunkersprite->Release(); //release bunker sprite
        delete bunkersprite; //delete bunker sprite
        int i;
  		for(i=0; i < MAX_INVGRPROWS; i++)
  		{
    	  if(invgrpsprite[i]!=NULL) //if invgrpsprite exists
		    invgrpsprite[i]->Release(); //release invgrpsprite
          delete invgrpsprite[i];
  	    }
        lpDirectDrawObject->Release(); //release DD object
      }
      ShowCursor(TRUE); //show the mouse cursor
      PostQuitMessage(0); //and exit
      break;
    default: //default window procedure
      return DefWindowProc(hwnd,message,wParam,lParam);
  } //switch(message)
  return 0L;
} //WindowProc

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow){
  MSG msg; //current message
  HWND hwnd; //handle to fullscreen window
  hwnd=CreateDefaultWindow("directX demo 3",hInstance);
  if(!hwnd)return FALSE;
  //set up window
  ShowWindow(hwnd,nCmdShow); UpdateWindow(hwnd);
  SetFocus(hwnd); //allow input from keyboard
  ShowCursor(FALSE); //hide the cursor
  //init graphics
  BOOL OK=InitDirectDraw(hwnd);//initialize DirectDraw
  if(OK)OK=LoadImages(); //load images from disk
  if(!OK){ //bail out if initialization failed
    DestroyWindow(hwnd); return FALSE;
  }
  //start game timer
  Timer.start();
  //create objects
  CreateObjects();
  //message loop
  while(TRUE)
    if(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)){
      if(!GetMessage(&msg,NULL,0,0))return msg.wParam;
      TranslateMessage(&msg); DispatchMessage(&msg);
    }
    else if(ActiveApp)ProcessFrame(); else WaitMessage();
} //WinMain