Chris Pollett >
Old Classes >
PIC197

   ( Print View )

Enrollment info

Course Info:


Homework Assignments:
Practice Exams: PIC:
                                   












HW3 Solutions Page

Return to homework page.


//
// Filename: AStarNode.h
//
// Purpose: interface for AStarNode struct used by CIntelligentObject
//          for A* search.
//

#ifndef __ASTARNODE__
#define __ASTARNODE__

#pragma warning (disable:4786)

#include <set>
using namespace std;

#include "Polygon.h"

struct CAStarNode
{
	CPoint point; // current point
	int distSource; // cost to get to this point
	int distTarget; // estimated cost to solution
	CAStarNode *prev; // points parent
	bool operator<(const CAStarNode *b) const;
                        // used by open priority_queue
                        // cks if distSource+distTarget > b's values
};

struct lthan // used by STL set. ck if a's point lex less than b's point.
{
	bool operator()(const CAStarNode* a,const CAStarNode* b) const;
};
#endif

// Filename: AStarNode.cpp:
//
// Purpose: implementation of the CAStarNode class.
//

#include "AStarNode.h"

//
// Member Function: operator<(const CAStarNode* b) const
//
// Purpose: used by STL priority_queue in A* algorithm
//          to return the node of minimal estimated cost.
//          cost is distance from start to node + estimated distance from
//          node to solution
//

bool CAStarNode::operator <(const CAStarNode* b) const
{
	return distSource+distTarget>b->distSource+b->distTarget;
}

//
// Member Function: operator()(const CAStarNode* a, const CAStarNode* b) const
//
// Purpose: used by STL set in find function. Returns whether
//          a's point is lexicographically less than b's.
//

bool lthan::operator()(const CAStarNode* a, const CAStarNode* b) const
{

    return a->point.x< b->point.x || ((a->point.x ==b->point.x)
		   && ( a->point.y <b->point.y));
}

//
// Filename: ai.h:
//
// Purpose: Header file for intelligent object class
//          This is the class used to control the monster's motion
//
// Modified by: Chris Pollett 2001
//
// Copyright Ian Parberry, 1999
// Last updated October 25, 1999

#ifndef __AI__
#define __AI__

#include<vector>
#include<stack>

#include "objects.h"
#include "polygon.h"
#include "astarnode.h"

class CIntelligentObject: public CObject{
  private:

    int m_nLastAiTime; //last time AI was used
    int m_nAiDelayTime; //time until AI next used

    vector<CObject *> m_Obstacles; //an array of obstacles between
	                           // monster and player

    CPoint m_NextGoal; //last recorded position of player

    stack<CPoint> m_Path; // stores path to player.

    void ai(); //artificial intelligence

    void aStar(); // A* algorithm

    vector<CPoint> findChildren(CAStarNode *b); // used by A* to find
                                                // reachable vertices.

  public:
    CIntelligentObject(ObjectType object,int x,int y,
      int xspeed,int yspeed); // constructor

    virtual void move(); // move depending on time and speed

    stack<CPoint> getPath(); // returns most recent path to player

    void player(CPoint p); // used by ObjectManager to write player's
                           // cooridinates.
};

#endif

//
// Filename: ai.cpp
//
// Purpose: Has the definition of the CIntelligentObject class.
//          This class control the games monster and uses
//          the A* algorithm to intelligently hunt the player.
//
//Modified by Chris Pollett 2001
//Copyright Ian Parberry, 1999
//Last updated October 25, 1999

#pragma warning (disable:4786) // stop Visual complaining
                               // about overlong debugger info
#include <set>
#include<queue>
#include<vector>
using namespace std;

#include "ai.h" //class header
#include "timer.h" //game timer
#include "random.h" //for random number generator
#include "Objman.h" //for ObjectManager
#include "AStarNode.h" // for nodes for A* algorithm
#include "Polygon.h" // for polygon class

extern CTimer Timer;  //game timer
extern CRandom Random; //random number generator
extern CObjectManager ObjectManager; //game ObjectManager

//
// Member Function: CIntelligentObject
//
// Purpose: sets up default values for monster object
//

CIntelligentObject::CIntelligentObject(ObjectType object,
  int x,int y,int xspeed,int yspeed):
CObject(object,x,y,xspeed,yspeed){ //constructor
  m_bIntelligent=TRUE;
  m_nLastAiTime=0;
  m_nAiDelayTime=250;
  m_NextGoal = CObject::getLoc();// first goal is for nowhere
}

//
// Member Function: move()
//
// Purpose: updates monsters coordinates
//
void CIntelligentObject::move(){ //move object
  CObject::move(); //move like a dumb object
  ai(); //act intelligently
}


//
// Member Function: ai()
//
// Purpose: provides the ai for monsters. cks if we have reached
//          a goal pt. If so we check our A* path to get the next goal.
//          if the AiDelayTime has passed, check if we can directly go
//          to player. If so abandon A* star path and head to player.
//          If not and the current A* path is empty, calculate new path.
//          Otherwise, follow old path.
//

void CIntelligentObject::ai(){ //main AI function
  //do the following periodically
  vector<CPoint> tmpVec;
  CPolygon destPoly;
  CPoint p;

  //
  // ck if at goal pt. if so get next one.
  //

  if(CObject::m_GoalPt == CObject::getLoc() && m_Path.size()>0)
  {	  CObject::m_GoalPt=m_Path.top();
	  m_Path.pop();
  }

  //
  // ck time since last ai move.
  //

  if(Timer.elapsed(m_nLastAiTime,m_nAiDelayTime))
  {
      p.create(0,0);

      //
      // ck for obstacles b/w player and monster
      //

      tmpVec.push_back(CObject::getLoc());
      tmpVec.push_back(m_NextGoal);
      destPoly.create(tmpVec);

      m_Obstacles=ObjectManager.polyIntersect(p,destPoly,OBSTACLE);

      if(m_Obstacles.size() > 0 ) // if yes see if need to do A*
      {
		  if(m_Path.size() == 0)
		  {
			  m_GoalPt = CObject::getLoc();
		      aStar();
		  }
      }
      else //if no head to player
      {
		CObject::m_GoalPt = m_NextGoal;
		m_Path=stack<CPoint>();
      }

  }
}

//
// Member Function: player(CPoint p)
//
// Purpose: used by ObjectManager to write the current location of the player.
//

void CIntelligentObject::player(CPoint p){
	m_NextGoal=p;
}

//
// Member Function: aStar
//
// Purpose: calculates A* path b/w monster and player
//

void CIntelligentObject::aStar()
{
	priority_queue<CAStarNode *> open; //nodes to searched

	set<CAStarNode*, lthan> closed, allElts; // nodes that have been
                                                 // searched and a set
                                                 // of all nodes.
                                                 // The reason for ellElts
                                                 // is that it is hard to find
                                                 // an elt of a priority queue

	set<CAStarNode*, lthan>::iterator iter;

	vector<CPoint> children;
	CPoint childi;

	int i;

	CAStarNode *node, *child,tmp;

	//
	// we start the search from the current location
	//

	node= new CAStarNode();
	node->point=CObject::getLoc();
	node->distSource = 0;
	node->distTarget = (m_NextGoal - node->point).length();
	node->prev = NULL;
	open.push(node);


	BOOL solved = FALSE;


	//
	// loop while there are still nodes we haven't searched.
	//
	while(open.size()>0)
	{

		node = open.top(); // get the current best candidate
		open.pop();

		if(node->point == m_NextGoal) //ck if it is the player
		{
			solved=TRUE;
			break;
		}

		children=findChildren(node); // get a vector of its children
		closed.insert(node); // add the node to the list of
                                     // nodes we've already seen.

		//
		// now look at the children
		//
		for(i=0;i < children.size() ;i++)
		{
			childi = children[i];
			child = new CAStarNode();
			child->point = childi;

			tmp.point = childi;

			//
			// forget those we've already seen.
			//

			if(closed.find(&tmp) ==closed.end())
			{


				child->distSource = node->distSource +
				                (node->point - childi).length();
				child->distTarget = (m_NextGoal-childi).length();
				child->prev=node;

				//
				// ck if still on open list by looking at
                                // allElts. If so ck if we've found a
                                // a shorter path and update.
				//
				iter = allElts.find(&tmp);
				if(iter == allElts.end())
				{
					open.push(child);
					allElts.insert(child);
				}
				else if (child->distSource < (*iter)->distSource  )
				//
				// otherwise we just push on open queue
				//
				{
					open.push(child);
					allElts.erase(iter);
					allElts.insert(child);
				}

			}
		}

	}

	//
	// if we found a solution record it in m_Path
        // otherwise sit and do nothing.
	//
	if(solved)
	{
		m_Path=stack<CPoint>();
		do
		{
			m_Path.push(node->point);
			node = node->prev;
		}while(node->prev != NULL);

		if(m_Path.size() >0 )
		{	CObject::m_GoalPt=m_Path.top();

		}
	}
	else CObject::m_GoalPt=CObject::getLoc();
}

//
// Member Function: findChildren(CAStarNode *b)
//
// Purpose: finds all nodes "reachable" from node b.
//          Reachable means: (a) it is a vertex before or after b
//          on an obstacle. (b) b is the monster and the node
//          is a vertex of an obstacle and there is a clear path b/w
//          the two. (c) b is a vertex of an obstacle and the node
//          is on another obstacle and there is a clear path b/w them.
//
// Caveat:  This algorithm could probably be improved a lot.
//

vector<CPoint> CIntelligentObject::findChildren(CAStarNode *b)
{
	vector<CPoint> children, tmpVec;

	CPolygon obstPoly, ckEdge;
	CPoint bPoint = b->point, obstLoc, obstVertex, origin, midPt;

	int i,j;

	origin.create(0,0);

	//
	// loop over all obstacles;
        //
	for(i=0; i< m_Obstacles.size(); i++)
	{
		obstPoly = m_Obstacles[i]->getPathPolygon();
		obstLoc = m_Obstacles[i]->getLoc();

		//
		// loop over all points on an obstacle
		//

		for(j=0; j<obstPoly.size(); j++)
		{
			obstVertex = obstLoc + obstPoly.getVertex(j);

			//
			// ck if this is a vertex of an obstacle
			//
			if(!(obstVertex == bPoint))
			{
				tmpVec.clear();
				tmpVec.push_back(bPoint);
				tmpVec.push_back(obstVertex);
				ckEdge.create(tmpVec);

				//
				// ck if edge (b,obstVertex) crosses anything
				// where we have deleted the vertices b
                                // and obstVertex from the polygon (if they
                                // exist). This notion of deleting points
                                // from a polygon should work if we only
                                // delete one point.
                                //
				if(ObjectManager.polyIntersect(origin,ckEdge,OBSTACLE,&tmpVec).size()==0)
				{
					//
					// Make sure b was either the monster
                                        // or we had two vertices on different
					// polygons
                                        //

					if(bPoint == CObject::getLoc() || ObjectManager.polyIntersect(origin,ckEdge,OBSTACLE).size()==2)
						children.push_back(obstVertex);
				}
			}
			else // if vertex of obstacle add adjacent vertices
			{
				if(j>0) children.push_back(obstLoc+obstPoly.getVertex(j-1));
				if(j<obstPoly.size()-1) children.push_back(obstLoc+obstPoly.getVertex(j+1));
				  // break;
			}
		}

		//
		// finally check if there is a direct path from here to player.
		//

		tmpVec.clear();
		tmpVec.push_back(bPoint);
		tmpVec.push_back(m_NextGoal);
		ckEdge.create(tmpVec);
		if(ObjectManager.polyIntersect(origin,ckEdge,OBSTACLE,&tmpVec).size()==0)
				children.push_back(m_NextGoal);

	}

	return children;
}

//
// Member Function: getPath()
//
// Purpose: returns the current A* path to the player.
//

stack<CPoint> CIntelligentObject::getPath()
{
	return m_Path;
}

//
// Filename: Main.cpp
//
// Purpose: contain WinMain, event handlers for HW3. Loads in images.
//
// Modified by: Chris Pollett
// Copyright Ian Parberry, 1999
// Last updated May 22, 2000

//system includes
#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#include <stdio.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 "csprite.h" //for clipped sprite class
#include "objects.h" //for object class
#include "objman.h" //for object manager
#include "random.h" //for random number generator

//defines
#define MAX_OBJECTS 5 //max number of objects in game

//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

CObjectManager ObjectManager(MAX_OBJECTS); //object manager

CClippedSprite *g_pSprite[NUM_SPRITES]; //sprites

CRandom Random; //random number generator

//
// Helper functions Prototypes
//

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

//
// Function Category: Load Sprite Helper functions
//
// Purpose: Load in sprites... We have a different sprite for collided and
//          non-collided player monsters.
//

BOOL LoadNPlayerSprite(){
  BOOL result=TRUE;
  result=result&&g_pSprite[NORMALPLAYER]->
    load(&g_cSpriteImages,0,1,1);
  result=result&&g_pSprite[NORMALPLAYER]->
    load(&g_cSpriteImages,1,58,1);
  return result;
} //LoadNPlayerSprite

BOOL LoadCPlayerSprite(){
  BOOL result=TRUE;
  result=result&&g_pSprite[COLLIDEPLAYER]->
    load(&g_cSpriteImages,0,115,1);
  result=result&&g_pSprite[COLLIDEPLAYER]->
    load(&g_cSpriteImages,1,172,1);
  return result;
} //LoadNPlayerSprite

BOOL LoadNMonsterSprite(){
  BOOL result=TRUE;
  result=result&&g_pSprite[NORMALMONSTER]->
    load(&g_cSpriteImages,0,229,1);
  result=result&&g_pSprite[NORMALMONSTER]->
    load(&g_cSpriteImages,1,287,1);
  return result;
} //LoadNMonsterSprite

BOOL LoadCMonsterSprite(){
  BOOL result=TRUE;
  result=result&&g_pSprite[COLLIDEMONSTER]->
    load(&g_cSpriteImages,0,345,1);
  result=result&&g_pSprite[COLLIDEMONSTER]->
    load(&g_cSpriteImages,1,403,1);
  return result;
} //LoadNMonsterSprite


BOOL LoadObstacleSprite(){
  return g_pSprite[OBSTACLE]->
    load(&g_cSpriteImages,0,1,230);
} //LoadObstacleSprite


BOOL LoadImages(){ //load graphics from files to surfaces
  //get the background image
  if(!background.load("bckgnd.bmp"))return FALSE; //read from file
  background.draw(lpPrimary);
    background.draw(lpSecondary);
  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 sprites...
  if(!g_cSpriteImages.load("sprites.bmp"))return FALSE;

  //...the player
  g_pSprite[NORMALPLAYER]=new CClippedSprite(2,56,58);
  if(!LoadNPlayerSprite())return FALSE; //load normal player

  g_pSprite[COLLIDEPLAYER]=new CClippedSprite(2,56,58);
  if(!LoadCPlayerSprite())return FALSE; //load collide player

  //...the monster
  g_pSprite[NORMALMONSTER]=new CClippedSprite(2,53,58);
  if(!LoadNMonsterSprite())return FALSE; //load normal player

  g_pSprite[COLLIDEMONSTER]=new CClippedSprite(2,53,58);
  if(!LoadCMonsterSprite())return FALSE; //load collide player

  //...the obstacle
  g_pSprite[OBSTACLE]=new CClippedSprite(1,168,185);
  if(!LoadObstacleSprite())return FALSE; //load normal player

  return TRUE;
} //LoadImages

//
// Function Name: CreateObjects
//
// Purpose: uses the ObjectManager to create the monster and
//          the player and sets the player as the current object.
//

void CreateObjects(){


  ObjectManager.create(NORMALMONSTER,3*SCREEN_WIDTH/4,
                       SCREEN_HEIGHT/2,0,0);

  ObjectManager.set_current(
    ObjectManager.create(NORMALPLAYER,SCREEN_WIDTH/4,
                         SCREEN_HEIGHT/2,0,0));

} //CreateObjects

//
// Function Category: Frame processing functions
//
// Purpose: maintain and update DirectDraw surfaces
//

BOOL RestoreSurfaces(){ //restore all surfaces
  BOOL result=TRUE;
  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(g_pSprite[NORMALPLAYER]->Restore()) //if normal player restored
    result=result&&LoadNPlayerSprite(); //redraw image
  else return FALSE;
  if(g_pSprite[COLLIDEPLAYER]->Restore()) //if collide player restored
    result=result&&LoadCPlayerSprite(); //redraw image
  else return FALSE;
  if(g_pSprite[NORMALMONSTER]->Restore()) //if normal monster restored
    result=result&&LoadNMonsterSprite(); //redraw image
  else return FALSE;
  if(g_pSprite[COLLIDEMONSTER]->Restore()) //if collide monster restored
    result=result&&LoadCMonsterSprite(); //redraw image
  else return FALSE;
  if(g_pSprite[OBSTACLE]->Restore()) //if normal monster restored
    result=result&&LoadObstacleSprite(); //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
  background.draw(lpSecondary);

  ObjectManager.animate(lpSecondary); //draw objects
  return TRUE;
} //ComposeFrame

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


//
// Function Category: Event Handlers and WinMain
//
// Purpose: set up main input loops handle events in particular
//          player keystrokes
//

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

    //
    //Player direction motions
    //

    case VK_UP: ObjectManager.accelerate(0,-1); break;
    case VK_DOWN: ObjectManager.accelerate(0,1); break;
    case VK_LEFT: ObjectManager.accelerate(-1,0); break;
    case VK_RIGHT: ObjectManager.accelerate(1,0); break;
    case VK_F2: ObjectManager.stopCurrent(); break; //stop player

    //
    // add a new obstacle
    //

    case VK_F1:
                // loop around trying to add an obstacle.
                // If collides with an existing object kill the new object.

		while(notGood && tries <100)
		{
			index=ObjectManager.create(OBSTACLE,
				Random.number(84,SCREEN_WIDTH-84),
				Random.number(185,SCREEN_HEIGHT),0,0);
			if(index >=0 )
			{
			  if(!ObjectManager.objectIntersect(index))
					 notGood=FALSE;

			  else ObjectManager.kill(index);
			}
			else notGood=FALSE;
			tries++;
		}
	break;
    //
    //Print player coords as well as draw A* path
    //

    case VK_F3: ObjectManager.toggleCoords(); break;

    //
    //Make monster avoid or chase player
    //

    case VK_SPACE: ObjectManager.toggleAvoid(); 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
        for(int i=0; i<NUM_SPRITES; i++){ //for each sprite
          if(g_pSprite[i]) //if sprite exists
            g_pSprite[i]->Release(); //release sprite
          delete g_pSprite[i]; //delete sprite
        }
        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 6",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
  for(int i=0; i<NUM_SPRITES; i++) //null out sprites
    g_pSprite[i]=NULL;
  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

//
// Filename: objects.h
//
// Purpose: header file for CObject class
//
// Modified by: Chris Pollett
//
// Copyright Ian Parberry, 1999
// Last updated October 27, 1999

#ifndef __OBJECTS__
#define __OBJECTS__

#include "bsprite.h"
#include "bmp.h"
#include "polygon.h"

//object types
enum ObjectType{NORMALMONSTER=0, COLLIDEMONSTER, NORMALPLAYER,
  COLLIDEPLAYER, OBSTACLE,NUM_SPRITES};
//note: NUM_SPRITES must be last

enum LocomotionType{PLAYER_MOTION,MONSTER_MOTION,NO_MOTION};
   // player motion is given by the object x and y speed
   // monster motion is based on a nonadjustable speed and
   // is always directed toward the point stored in m_Goal.

class CObject{ //class for a moving object
  friend class CObjectManager;
  protected:
    CPolygon m_Polygon; // polygon used to ck for object collisions
    CPolygon m_PathPolygon; // polygon used for A* path finding

    CPoint m_GoalPt; // if a Monster then the next point to move towards
                     // or away from

    BOOL m_bAvoid; //holds whether monster is in avoid or chase mode

    int m_nX,m_nY; //current location
    int m_nXspeed,m_nYspeed; //current speed
    int m_nLastXMoveTime; //last time moved horizontally
    int m_nLastYMoveTime; //last time moved vertically
    CBaseSprite *m_pSprite; //pointer to sprite
    int m_nWidth,m_nHeight; //width and height of sprite
    int m_nMinXSpeed,m_nMaxXSpeed; //min, max horizontal speeds
    int m_nMinYSpeed,m_nMaxYSpeed; //min, max vertical speeds
    int m_nCurrentFrame; //frame to be displayed
    int m_nFrameCount; //number of frames in animation
    int m_nLastFrameTime; //last time the frame was changed
    int m_nFrameInterval; //interval between frames
    LocomotionType m_eLocomotion; //mode of travel
    ObjectType m_eObject; //what kind of object is this?
    int m_nBirthTime; //time of creation
    BOOL m_bIntelligent; //TRUE if object is intelligent
  public:
    CObject(ObjectType object,int x,int y,
      int xspeed,int yspeed); //constructor
    void draw(LPDIRECTDRAWSURFACE surface); //draw
    void accelerate(int xdelta,int ydelta=0); //change speed
    virtual void move(); //move depending on time and speed
    CPolygon getPolygon(); // returns bounding polygon of object
    CPolygon getPathPolygon(); // returns polygon used for A* path finding
    CPoint getLoc(); // returns current location of object
    void toggleAvoid(); // changes monster for chase to avoid and back
};

BOOL IntersectObject(CObject *o1, CObject *o2); // cks if two
                                                // objects bounding polygons
                                                // intersect
#endif

//
// Filename: objects.cpp
//
// Purpose: contains implementation of CObject class
//
// Modified by: Chris Pollett 2001
//
//Copyright Ian Parberry, 1999
//Last updated October 27, 1999

#include "objects.h"
#include "polygon.h" // for polygons
#include "timer.h" //game timer
#include "csprite.h" //for clipped sprite class
#include "random.h" //for random number generator


extern CTimer Timer; //game timer
extern CRandom Random; //random number generator
extern CClippedSprite *g_pSprite[]; //sprite

CObject::CObject(ObjectType object,int x,int y,
                 int xspeed,int yspeed){ //constructor
  vector<CPoint> v; //used to set polygons around objects
  CPoint tmp;       //

  //defaults
  m_nCurrentFrame=0; m_nLastFrameTime=Timer.time();
  m_nFrameInterval=30;
  m_eLocomotion=NO_MOTION;
  m_bIntelligent=FALSE;
  //common values
  m_eObject=object; //type of object
  m_nLastXMoveTime=m_nLastYMoveTime=Timer.time(); //time
  m_nX=x; m_nY=y; //location
  m_nXspeed=xspeed; m_nYspeed=yspeed; //speed
  m_pSprite=g_pSprite[object];
  m_nFrameCount=m_pSprite->frame_count();
  m_nHeight=m_pSprite->height();
  m_nWidth=m_pSprite->width();
  m_nBirthTime=Timer.time(); //time of creation
  m_bAvoid=FALSE;
  m_GoalPt=getLoc();
  //customized values
  switch(object){
    case NORMALPLAYER:
    case COLLIDEPLAYER:
      m_nMinXSpeed=-3; m_nMaxXSpeed=3;
      m_nMinYSpeed=-3; m_nMaxYSpeed=3;
      m_nFrameInterval=500;
      m_eLocomotion=PLAYER_MOTION;

	  tmp.create(-28,0); // set up bounding polygon for player
	  v.push_back(tmp);  // Let's do it in as brute force a way
	  tmp.create(28,0);  // as possible
	  v.push_back(tmp);
	  tmp.create(28,-56);
	  v.push_back(tmp);
	  tmp.create(-28,-56);
	  v.push_back(tmp);
	  tmp.create(-28,0);
	  v.push_back(tmp);

	  m_Polygon.create(v);
      break;
    case NORMALMONSTER:
    case COLLIDEMONSTER:

	  m_nMinXSpeed=-3; m_nMaxXSpeed=-3;
      m_nMinYSpeed=-3; m_nMaxYSpeed=3;
      m_nFrameInterval=500;
      m_eLocomotion=MONSTER_MOTION;
	  m_bIntelligent = TRUE;

	  tmp.create(-28,0); // set up bounding polygon for monster
	  v.push_back(tmp);
	  tmp.create(28,0);
	  v.push_back(tmp);
	  tmp.create(28,-56);
	  v.push_back(tmp);
	  tmp.create(-28,-56);
	  v.push_back(tmp);
	  tmp.create(-28,0);
	  v.push_back(tmp);

	  m_Polygon.create(v);
      break;
    case OBSTACLE:
	  tmp.create(-84,0); // set up bounding polygon for obstacle
	  v.push_back(tmp);
	  tmp.create(84,0);
	  v.push_back(tmp);
	  tmp.create(84,-185);
	  v.push_back(tmp);
	  tmp.create(-84,-185);
	  v.push_back(tmp);
	  tmp.create(-84,-130);
	  v.push_back(tmp);
	  tmp.create(30,-130);
	  v.push_back(tmp);
	  tmp.create(30,-50);
	  v.push_back(tmp);
	  tmp.create(-84,-50);
	  v.push_back(tmp);
	  tmp.create(-84,0);
	  v.push_back(tmp);

	  m_Polygon.create(v);

	  v.clear();            // set up path polygon for obstacle
	  tmp.create(-106,50);  // used by A* in finding paths
	  v.push_back(tmp);
	  tmp.create(106,50);
	  v.push_back(tmp);
	  tmp.create(106,-179);
	  v.push_back(tmp);
	  tmp.create(-106,-179);
	  v.push_back(tmp);

	  tmp.create(-106,-80); //add an additional edge monster is allowed to
	  v.push_back(tmp);     //traverse not in bounding polygon.
	  tmp.create(-106,-44);
	  v.push_back(tmp);
	  tmp.create(-106,-80);
	  v.push_back(tmp);

	  tmp.create(8,-80);
	  v.push_back(tmp);
	  tmp.create(8,-44);
	  v.push_back(tmp);
	  tmp.create(-106,-44);
	  v.push_back(tmp);
	  tmp.create(-106,50);
	  v.push_back(tmp);
	  m_PathPolygon.create(v);
      break;
  }
}

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

 //repeating animation
  int t=m_nFrameInterval/(1+abs(m_nXspeed)); //frame interval
  if(m_nFrameCount>1&&Timer.elapsed(m_nLastFrameTime,t))

  if(++m_nCurrentFrame>=m_nFrameCount){ //wrap
          m_nCurrentFrame=0;
        }

}

void CObject::accelerate(int xdelta,int ydelta){
//change speed
  //horizontal
  m_nXspeed+=xdelta;
  if(m_nXspeed<m_nMinXSpeed)m_nXspeed=m_nMinXSpeed;
  if(m_nXspeed>m_nMaxXSpeed)m_nXspeed=m_nMaxXSpeed;
  //vertical
  m_nYspeed+=ydelta;
  if(m_nYspeed<m_nMinYSpeed)m_nYspeed=m_nMinYSpeed;
  if(m_nYspeed>m_nMaxYSpeed)m_nYspeed=m_nMaxYSpeed;
}

void CObject::toggleAvoid(){ //toggle whether monster should avoid or chase
	if(m_bAvoid) m_bAvoid=FALSE;
	else m_bAvoid=TRUE;
}
void CObject::move(){ //move object
  const int XSCALE=16; //to scale back horizontal motion
  const int YSCALE=32; //to scale back vertical motion
  int xdelta,ydelta; //change in position
  int time=Timer.time(); //current time
  int tfactor; //time since last move
  CPoint tmp;

  switch(m_eLocomotion){
    case PLAYER_MOTION:// players move according to their x and y speed

      //horizontal motion
      tfactor=time-m_nLastXMoveTime; //time since last move
      xdelta=(m_nXspeed*tfactor)/XSCALE; //x distance moved
      m_nX+=xdelta; //x motion
      if(xdelta||m_nXspeed==0) //record time of move
        m_nLastXMoveTime=time;
      if(m_nX<-m_nWidth/2)m_nX=SCREEN_WIDTH+m_nWidth/2;
      if(m_nX>SCREEN_WIDTH+m_nWidth/2)m_nX=-m_nWidth/2;
      //vertical motion
      tfactor=time-m_nLastYMoveTime; //time since last move
      ydelta=(m_nYspeed*tfactor)/YSCALE; //y distance moved
      m_nY+=ydelta; //y motion
      if(m_nY<0)m_nY=SCREEN_HEIGHT+m_nHeight;
      if(m_nY> SCREEN_HEIGHT+m_nHeight)m_nY=0;
      if(ydelta||m_nYspeed==0) //record time of move
        m_nLastYMoveTime=time;
      break;
	case MONSTER_MOTION: //monster move towards a goal pt m_Goal
	  tmp = CObject::getLoc();
	  if( tmp == m_GoalPt)
	  {
		m_nYspeed = 0;
		m_nXspeed = 0;
	  }
	  else
	  {
		tmp = m_GoalPt-tmp;
		m_nYspeed = abs(tmp.y*m_nMaxYSpeed/tmp.length());
		m_nXspeed = m_nMaxYSpeed-m_nYspeed;

		if(tmp.y <0) m_nYspeed = -m_nYspeed;
		if(tmp.x <0) m_nXspeed = -m_nXspeed;
	  }

	  if(m_bAvoid) //or in the opposite direction if they are in avoid mode
	  {
		m_nYspeed = -m_nYspeed;
	    m_nXspeed = -m_nXspeed;
	  }
	  tfactor=time-m_nLastXMoveTime; //time since last move

      xdelta=(m_nXspeed*tfactor)/YSCALE; //x distance moved
	  m_nX+=xdelta; //x motion
      if(xdelta || m_nXspeed==0) //record time of move
        m_nLastXMoveTime=time;
      if(m_nX<-m_nWidth/2)m_nX=SCREEN_WIDTH+m_nWidth/2;
      if(m_nX> SCREEN_WIDTH+m_nWidth/2)m_nX=-m_nWidth/2;

      tfactor=time-m_nLastYMoveTime; //time since last move
      ydelta=(m_nYspeed*tfactor)/YSCALE; //x distance moved
	  m_nY+=ydelta; //y motion
      if(ydelta || m_nYspeed==0) //record time of move
         m_nLastYMoveTime=time;

	  if(m_nY<0)m_nY=SCREEN_HEIGHT+m_nHeight;
      if(m_nY>SCREEN_HEIGHT+m_nHeight)m_nY=0;


      break;
    default: break;
  }
}

CPolygon CObject::getPolygon() // return objects bounding polygon
{
	return m_Polygon;
}

CPolygon CObject::getPathPolygon() // return object A* path polygon
{
	return m_PathPolygon;
}


CPoint CObject::getLoc() // return current position of object
{
	CPoint tmp;
	tmp.create(m_nX,m_nY);
	return tmp;
}

BOOL IntersectObject(CObject *ob1, CObject *ob2)
{
	return IntersectPolygon(ob1->getLoc(),ob1->getPolygon(),
	                        ob2->getLoc(),ob2->getPolygon());
}

// Filename: objman.h
//
// Purpose: Header file for the object manager. This class handles the
//          creation and drawing of objects on the screen
//
// Modified by: CHris Pollett 2001
//
// Copyright Ian Parberry, 1999
// Last updated October 27, 1999

#ifndef __OBJMAN__
#define __OBJMAN__

#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#include <stack>

#include "objects.h"
#include "Polygon.h"

class CObjectManager{
  private:
    CObject **m_pObjectList; //list of objects in game
    int m_nCount; //how many objects in list
    int m_nMaxCount; //maximum number of objects
    int m_nCurrentObject; //index of the current object
    //distance functions
    int distance(int x1,int y1,int x2,int y2); //in universe
    int distance(int first,int second); //between objects
	BOOL m_bCoordsOn;
	int drawCoordinates(CPoint p, LPDIRECTDRAWSURFACE surface);
	int drawPath(CPoint p, CPoint p2,
		stack<CPoint> path, LPDIRECTDRAWSURFACE surface);
    //collision detection
    void collision_detection(); //all collisions
    //managing dead objects
    void replace(int index); //replace by next in series
  public:
    CObjectManager(int max); //constructor
    ~CObjectManager(); //destructor
    int create(ObjectType object,int x,int y,
      int xspeed,int yspeed); //create new object
   void kill(int index); //remove object from list
    //animate all objects
    void animate(LPDIRECTDRAWSURFACE surface);
    //the following functions operate on the current object
    void accelerate(int xdelta,int ydelta=0); //change speed
    BOOL objectIntersect(int j); //cks if any object
                                 // intersect with object j in the object list
                                 // probably should access this with an object
                                 // rather than an integer. (uses bounding
                                 // not A* polygon)

    vector<CObject *> polyIntersect(CPoint off, CPolygon poly, ObjectType type,
		vector<CPoint> *rem=NULL);
                        // ck if given polygon + offset
                        // intersects the A* path polygons of
                        // objects of the given type after the points in rem
                        // have been removed.

    void set_current(int index); //set current object
    void toggleAvoid(); // make monster swith b/w avoid and chase.
    void toggleCoords(); // say whether we draw each objects coordinates
    void stopCurrent(); // stop the current object motion
};

#endif

//
// Filename: objman.cpp
//
// Purpose: object manager class implementation
//
// Modified by: Chris Pollett 2001
//
// Copyright Ian Parberry, 1999
// Last updated October 28, 1999

#include <math.h> //for sqrt
#include<vector>
#include<stack>

#include "objman.h"
#include "polygon.h" //for polygons
#include "timer.h" //game timer
#include "csprite.h" //for clipped sprite class
#include "ai.h" //for intelligent objects

extern CTimer Timer; //game timer

extern CClippedSprite *g_pSprite[]; //sprites

CObjectManager::CObjectManager(int max){ //constructor
  m_nMaxCount=max; m_nCount=0; m_nCurrentObject=0;
  m_bCoordsOn = FALSE;
  m_pObjectList=new CObject*[max]; //object list
  for(int i=0; i<m_nMaxCount; i++) //create objects
    m_pObjectList[i]=NULL;
}

CObjectManager::~CObjectManager(){ //destructor
  for(int i=0; i<m_nMaxCount; i++) //for each object
    delete m_pObjectList[i]; //delete it
  delete[]m_pObjectList; //delete object list
}

int CObjectManager::create(ObjectType object,int x,int y,
                           int xspeed,int yspeed){
  if(m_nCount<m_nMaxCount){ //if room, create object
    int i=0; //index into object list
    while(m_pObjectList[i]!=NULL)i++; //find first free slot
    if(object==NORMALMONSTER || object==COLLIDEMONSTER) //intelligent object
      m_pObjectList[i]=
        new CIntelligentObject(object,x,y,xspeed,yspeed);
    else //dumb object
      m_pObjectList[i]=new CObject(object,x,y,xspeed,yspeed);
    m_nCount++;
    return i;
  }
  else return -1;
}

void CObjectManager::animate(LPDIRECTDRAWSURFACE surface){
  //move objects
  int i,j;
  CPolygon poly;
  CObject *obj;
  CPoint p;

  for(i=0; i<m_nMaxCount; i++) //for each object slot
    if(m_pObjectList[i]!=NULL){ //if there's an object there
      m_pObjectList[i]->move(); //move it
      if(m_pObjectList[i]->m_bIntelligent) //if intelligent
        //tell object about plane current position
        ((CIntelligentObject*)m_pObjectList[i])->player(
          m_pObjectList[m_nCurrentObject]->getLoc());
    }

  //collision detection
  collision_detection();
  //draw objects
  for(i=0; i<m_nMaxCount; i++) //for each object slot
  {
    obj = m_pObjectList[i];
    if(obj !=NULL) //if there's an object there
    {
	  obj->draw(surface); //draw it
	  if(m_bCoordsOn) //if we're in coords mode draw coords
	  {               // and if monster its A* path
		poly = obj->getPolygon();
		p = obj->getLoc();
		for(j=0; j<poly.size(); j++)
			drawCoordinates(p+poly.getVertex(j), surface);
		if(obj->m_bIntelligent)
			drawPath(p, obj->m_GoalPt, ((CIntelligentObject*)obj)->getPath(), surface);

	  }
	}
  }
}

void CObjectManager::accelerate(int xdelta,int ydelta){
  //change speed of current object
  m_pObjectList[m_nCurrentObject]->
    accelerate(xdelta,ydelta);
}

void CObjectManager::toggleAvoid() //changes monsters b/w chase and avoid mode
{
	int i;
	for(i=0; i<m_nMaxCount; i++) //for each object slot
		if(m_pObjectList[i]!=NULL) //if there's an object there
		  m_pObjectList[i]->toggleAvoid(); //toggle it
}

void CObjectManager::toggleCoords() //toggles whethere we draw objects coords or
                                    // not
{
	m_bCoordsOn = !m_bCoordsOn;
}

void CObjectManager::set_current(int index){
  //set current object
  if(index>=0&&index<m_nCount)m_nCurrentObject=index;
}


int CObjectManager::distance(int x1,int y1,int x2,int y2){
//return distance in universe
  int x=abs(x1-x2),y=abs(y1-y2);//x and y distance
  //return result
  return (int)sqrt((double)x*x+(double)y*y);
}

int CObjectManager::distance(int first,int second){
//return distance between objects
  //bail if bad index
  if(first<0||first>=m_nMaxCount)return -1;
  if(second<0||second>=m_nMaxCount)return -1;
  //get coordinates of centers
  int x1,y1,x2,y2; //coordinates of objects
  x1=m_pObjectList[first]->m_nX;
  y1=m_pObjectList[first]->m_nY-
    m_pObjectList[first]->m_nHeight/2;
  x2=m_pObjectList[second]->m_nX;
  y2=m_pObjectList[second]->m_nY-
    m_pObjectList[second]->m_nHeight/2;
  //return distance between coordinates
  return distance(x1,y1,x2,y2);
}

void CObjectManager::kill(int index){ //remove object
  m_nCount--;
  delete m_pObjectList[index];
  m_pObjectList[index]=NULL;
}


void CObjectManager::replace(int index){
//replace object by next in series
  CObject *object=m_pObjectList[index]; //current object
  ObjectType newtype;
  //decide on new object type
  switch(object->m_eObject){
    case NORMALPLAYER:
		newtype=COLLIDEPLAYER;
	break;
    case COLLIDEPLAYER:
		newtype=NORMALPLAYER;

	break;

    case NORMALMONSTER:
		newtype=COLLIDEMONSTER;
	break;
    case COLLIDEMONSTER:
		newtype=NORMALMONSTER;
	break;

  }
  object->m_eObject=newtype;
  object->m_pSprite=g_pSprite[newtype];

}


void CObjectManager::collision_detection(){
//check for all collisions
	CObject *object=m_pObjectList[m_nCurrentObject], *obj2;

  int i=0; //index of object collided with
  BOOL mCollide=FALSE;
  while(i<m_nMaxCount){ // loop over potential objects
	  obj2=m_pObjectList[i];
	  if(obj2!=NULL && m_nCurrentObject != i) // make sure not player
	  { //if i is a valid object index
		if(IntersectObject(object,obj2))  //ck if intersects player
		{

			switch(obj2->m_eObject){
				case NORMALMONSTER:
                  		 replace(i); //replace object that is hit
                                             // notice no break
				case COLLIDEMONSTER:
				  mCollide = TRUE;
				  if(object->m_eObject==NORMALPLAYER)
				     replace(m_nCurrentObject);
				  break;
				case OBSTACLE:
				  stopCurrent(); //stop player
				  break;
			}
		}
		else if(obj2->m_eObject==COLLIDEMONSTER) //handle monster no
                                                         //colliding with player
			   replace(i);
	  }
	   ++i; //next object
  }
  if(object->m_eObject==COLLIDEPLAYER && mCollide==FALSE)
         replace(m_nCurrentObject); //revert player if not colliding with
                                    //monster
}

BOOL CObjectManager::objectIntersect(int j){ //check if object j's
                                             //bounding polygon intersects
                                             // anything
  int i=0; //index of object collided with

  CObject *object = m_pObjectList[j], *obj2;
  while(i<m_nMaxCount)
  {
	  obj2=m_pObjectList[i];
	  if(obj2!=NULL && i !=j)
	  { //if i is a valid object index
		if(IntersectObject(object, obj2))
				return TRUE;
	  }
	  i++;
  }
  return FALSE;
}

vector<CObject *> CObjectManager::polyIntersect(CPoint off, CPolygon poly,
				ObjectType type, vector<CPoint> *rem)
{ // return all the objects whose path polygon when the points in rem
  // have been deleted intersects poly+off

  CObject  *object;
  vector<CObject* > collisions;
  int i;

  for(i=0;i<m_nMaxCount;i++)
  {
	  object=m_pObjectList[i];

	  if(object!=NULL)
	  { //if i is a valid object index

		if(object->m_eObject == type)
        {
		  if(IntersectPolygon(off, poly, object->getLoc(), object->getPathPolygon(),
			  rem))
				collisions.push_back(object);
		}
	  }

  }

  return collisions;
}


int CObjectManager::drawCoordinates(CPoint p, LPDIRECTDRAWSURFACE surface)
{ //draw the coordinates of the point p to the surface given
	HDC xdc; //GDI device context
	char out[11];

	if(FAILED(surface->GetDC(&xdc))) //get the device context for surface
		return 0;

	SetTextColor(xdc,RGB(0,0,255)); //set the color to blue
	SetBkMode(xdc, TRANSPARENT); //don't draw backgroup of char's

	out[0] = 'x'; //make our char array
	out[1] = '=';
	out[2] = '0'+p.x/100;
	out[3] = '0'+(p.x/10)%10;
	out[4] = '0'+p.x%10;
	out[5] = ',';
	out[6] = 'y';
	out[7] = '=';
	out[8] = '0'+p.y/100;
	out[9] = '0'+(p.y/10)%10;
	out[10] = '0'+p.y%10;

	TextOut(xdc, p.x,p.y, out, 11); //print out char array of size 11
                                        // at p.x, p.y on screen

	surface->ReleaseDC(xdc); //release context when done

	return 1;
}

int CObjectManager::drawPath(CPoint p, CPoint p2,
			stack<CPoint> path, LPDIRECTDRAWSURFACE surface)
{ // draw the the path given in stack
	HDC xdc; // GDI device context

	HPEN blue_pen; // make a pen

	CPoint start, stop;

	if(path.size() == 0) return 0; // no path do nothing

	if(FAILED(surface->GetDC(&xdc))) //get context
		return 0;



	blue_pen = CreatePen(PS_SOLID,1, RGB(0,0,255)); // make a blue pen
	SelectObject(xdc,blue_pen); // set to use it

	stop = path.top();
	MoveToEx(xdc,p.x,p.y, NULL); //start at monster
	LineTo(xdc,p2.x,p2.y); // go to first goal pt
	LineTo(xdc, stop.x, stop.y); // go to next path point

	while(path.size()>1) //draw the rest of the edges of path
	{
		start=path.top();
		path.pop();
		stop=path.top();
		MoveToEx(xdc,start.x,start.y, NULL); //unneccessary
		LineTo(xdc, stop.x, stop.y);
	}

	DeleteObject(blue_pen);
	surface->ReleaseDC(xdc);

	return 1;
}

void CObjectManager::stopCurrent(){ //stop current object
		m_pObjectList[m_nCurrentObject]->m_nXspeed =0;
		m_pObjectList[m_nCurrentObject]->m_nYspeed =0;
}

//
// Filename: Polygon.h
//
// Purpose: contains definitions of point, edge and polygon structs/classes
//

#ifndef __POLYGON__
#define __POLYGON__

#include<windows.h>
#include<vector>
using namespace std;

struct CPoint //encapsulates notion of a pt on the screen
{
	int x; // coords
	int y;

	CPoint();
	void create(int xcoord, int ycoord); //sets the coords
	BOOL operator==(CPoint p2); //cks if two pts the same
	CPoint operator+(CPoint p2);  // adds points componentwise
	CPoint operator-(CPoint p2); //subtracts points component wise
	int length(); // returns Euclidean length of pts.

};

struct CEdge //encapsulates the notions of edge in a graph
{
	CPoint left; // vertices
	CPoint right;

	CEdge();
	void create(CPoint first, CPoint second); // sets vertices
	CEdge operator+(CPoint p2); // adds p2 to both left and right
	int length(); // returns length of edge
	CEdge boundRect(); // returns bounding rectangle of edge
};

int CrossProduct( CPoint p1, CPoint p2); // computes cross product of two
                                         // points viewed as 2d vectors

BOOL Straddle( CEdge e1, CEdge e2); //cks if edges straddle each other

BOOL EdgeCross( CPoint off1, CEdge e1, CPoint off2, CEdge e2);
                                    //cks if two edges cross

class CPolygon // encapsulates notions of a polygon
{

	protected:
		vector<CPoint> m_Vertices; // vertices in polygon

	public:
		CPolygon();
		~CPolygon();
		void create(vector<CPoint> vertices); //set vertices
		CEdge getEdge(int i);  // returns the ith edge
		CPoint getVertex(int i); // returns the i vertex
		void erase(int i); // deletes the i vertex. implement so
                                   // if done only once will not add any new
                                   //edge to polygon
		int size(); // returns number of vertices


};

BOOL IntersectPolygon( CPoint off1, CPolygon p1, CPoint off2, CPolygon p2,
					   vector<CPoint> *rem=NULL);
   // cks if when we offset two polygons and delete pts rem from the second
   // if there are any intersections.
#endif

//
// Filename: Polygon.cpp
//
// Purpose: contains implementation of point, edge and polygon structs/classes
//

#include <math.h>
#include<iostream>

#include "Polygon.h"

CPoint::CPoint() //point constructor
{
	x=0;
	y=0;
}

void CPoint::create(int xcoord, int ycoord) //set coords
{
	x=xcoord;
	y=ycoord;
}

CPoint CPoint::operator+(CPoint p2) //add point component-wise
{
	CPoint tmp;
	tmp.create(x+p2.x,y+p2.y);
	return tmp;
}

CPoint CPoint::operator-(CPoint p2) //subtract component-wise
{
	CPoint tmp;
	tmp.create(x-p2.x,y-p2.y);
	return tmp;
}

BOOL CPoint::operator==(CPoint p2) //ck equality component-wise
{
	if(x==p2.x && y==p2.y) return TRUE;
	return FALSE;
}

int CPoint::length() //return euclidean length of pt
{
  return (int)sqrt((double)x*x+(double)y*y);
}

CEdge::CEdge() //constrcutor
{
	left=CPoint();
	right=CPoint();
}

void CEdge::create(CPoint first, CPoint second) //set vertices
{
	left=first;
	right=second;
}

CEdge CEdge::operator +(CPoint p) //add p to each vertex in edge
{
	CEdge tmp;
	tmp.create(left + p, right+p);
	return tmp;
}

int CEdge::length() //return length of edge
{
	return (left-right).length();
}

CEdge CEdge::boundRect() //returns bounding rectangle of edge
{
	CPoint tmpL, tmpR;
	CEdge e;
	tmpL.create(min(left.x, right.x),min(left.y, right.y));
	tmpR.create(max(left.x, right.x),max(left.y, right.y));
	e.create(tmpL, tmpR);
	return e;
}

int CrossProduct( CPoint p1, CPoint p2) //view pts as vectors and take cross
                                        //product
{
	return p1.x*p2.y - p1.y*p2.x;
}

BOOL Straddle( CEdge e1, CEdge e2) //ck if edges straddle each other
{
	CPoint deltE1 =e1.right-e1.left;
	int tmp = CrossProduct(e2.left-e1.left,deltE1);
	int tmp2 = CrossProduct(e2.right-e1.left,deltE1);
	tmp *= tmp2;

	if (tmp<=0) return TRUE;

	else return FALSE;
}

BOOL EdgeCross( CPoint off1, CEdge e1, CPoint off2, CEdge e2)
//ck if edge+offset cross
{
	CEdge tmpE1 = (e1+off1).boundRect(), tmpE2=(e2+off2).boundRect();

	if(!((tmpE1.right.x >= tmpE2.left.x) && (tmpE2.right.x >= tmpE1.left.x)
		&& (tmpE1.right.y >= tmpE2.left.y) && (tmpE2.right.y >= tmpE1.left.y)))
		return FALSE;

	if( Straddle(tmpE1, tmpE2)
		&& Straddle(tmpE2, tmpE1)) return TRUE;
	else return FALSE;
}

CPolygon::CPolygon(){} //constructor


void CPolygon::create(vector<CPoint> vertices) //set vertices of polygon
{

	    m_Vertices=vertices;

}

CPolygon::~CPolygon(){}

CEdge CPolygon::getEdge(int i)  //return ith edge
{
	CEdge tmp;

	tmp.create(m_Vertices[i],m_Vertices[i+1]);
	return tmp;
}

CPoint CPolygon::getVertex(int i) //return ith vertex
{

	return m_Vertices[i];
}

void CPolygon::erase(int i) //erases ith edge so that if polygon's
                            // first vertex connected last
                            // then we won't add new edges
{
	int j;
	CPoint p;

	for(j=0; j<i; j++) // move edge before i to end of vertices
	{
	    p= m_Vertices[0];
	    m_Vertices.erase(m_Vertices.begin());
            m_Vertices.push_back(p);

	}
	m_Vertices.erase(m_Vertices.begin()); //erase i

}

int CPolygon::size() //return size of polygon
{
	return m_Vertices.size();

}

BOOL IntersectPolygon( CPoint off1, CPolygon p1, CPoint off2, CPolygon p2,
					  vector<CPoint> *rem)
// ck if two poly's+offset intersect after remove pts rem from 2nd one.
{
	int i,j;

	if(rem)
	{
	  for(i=0; i<(*rem).size(); i++)
	  {
		for(j=0; j<p2.size(); j++)
		{
			if((*rem)[i]==off2+p2.getVertex(j))
				p2.erase(j);
		}
	  }
	}

	for(i=0; i<p1.size()-1; i++)
	{
		for(j=0; j<p2.size()-1; j++)
		{
			if(EdgeCross(off1, p1.getEdge(i), off2, p2.getEdge(j)))
				return TRUE;
		}
	}
	return FALSE;
}