TGC Codebase Backup



DarkAI test example in C++ with sparky's collision by Anonymous Coder

16th Jan 2011 8:16
Summary

Simple implementation of the darkAI engine to test its functionality. Includes friendly AI that follow player and enemy AI which patrol the stage. AI's will engage when in contact.



Description

DarkAI test application

Summary
Simple example of the DarkAI engine in C++.

Full project download
http://www.mediafire.com/file/gmwmyrow1dd/AITest2.zip

Contains:
EnemyAI that follow a set patrol path unless they spot or hear the player. If heard they
will search for the player and engage them directly when found.

FriendlyAI follows the player when [F] is pressed or will hold when [H] is pressed.
FriendlyAI will fire at the enemy agents when in range.

Sparky's collision is used on the player to navigate the test stage. The player can jump
with the space key.


===================================================================================
===================================================================================
Class List:
Main.cpp
Game.cpp
friendlyBase.cpp
enemyBase.cpp
player.cpp

header files:
Game.h
friendlyBase.h
enemyBase.h
player.h

Additonal includes:
DarkAI.h
SC_Collision.h



Code
                                    ` This code was downloaded from The Game Creators
                                    ` It is reproduced here with full permission
                                    ` http://www.thegamecreators.com
                                    
                                    ===================================================================================
Main.cpp
===================================================================================

// Dark GDK - The Game Creators - www.thegamecreators.com

/**
This programs creates 2 teams of enemies and firendlies.
When ordered the friendlies will follow the player and engage
the enemies on contact.

The eneimes partol the level until they "hear" or see the player and/or any
of the friendlies.
*/

// whenever using Dark GDK you must ensure you include the header file
#include "DarkGDK.h"
#include "Game.h"
// the main entry point for the application is this function
void DarkGDK ( void )
{
	// set display mode
	dbSetDisplayMode    ( 800, 600, 32 );
	dbSetWindowPosition ( GetSystemMetrics ( SM_CXSCREEN ) / 2 - ( 800 / 2 ), GetSystemMetrics ( SM_CYSCREEN ) / 2 - ( 600 / 2 ) );


	// turn on sync rate and set maximum rate to 60 fps
	dbSyncOn   ( );
	dbSyncRate ( 60 );
	
	//set up view point
	dbAutoCamOff();
	dbPositionCamera(0, 300, -700);
	dbXRotateCamera ( 25 ); 

	Game game;
	game.startGame();
	// our main loop
	while ( LoopGDK ( ) )
	{
		
		//Create game call update
		game.updateGame();

		dbSync ( );
	}

	// return back to windows
	return;
}

===================================================================================
Game.cpp
===================================================================================
#include "Game.h"
#include "soundManager.h"

//This is our main game code


//Initialise the test area
void Game::startGame(){

ptimer = 0;
btimer = 0;
ftimer = 0;
stimer = 0;
numFriends = 3;
numEnemies = 3;

iObjNum = 1;

//Start the darkAI engine
AIStart ( );
  
//Start the collision engine
SC_Start();

	// this command sets the global radius that will be used by all entities.
	// tis controls how much space is allowed between the edge of obstacles
	// and the waypoints that define movement around them, since entities 
	// use the waypoints to navigate around their world they will attempt
	// to maintain at least radius# distance from all obstacles
	AISetRadius(  2.5 );
	
AIDebugShowSounds ( 30 ); 

makeLevel();

loadSounds();

//load the enemyAI

enemyBase temp;

temp.createEnemy(iObjNum);


// make a patrol path
	AIMakePath     ( 1 );
	AIPathAddPoint ( 1,  400, 400 );
	AIPathAddPoint ( 1, -400, 400 );



//create the instances of the enemies
	enemies = new enemyBase[numEnemies];

iObjNum = 5005;
for(int i = 0; i < numEnemies; i ++)
{

	enemies[i].cloneEnemy(iObjNum,i * 100, 2.5f, 40, 30);
	iObjNum++;

}

//make the player

player.createPlayer(1001);

	

//range of friendlies 3000-4000
iObjNum = 3000;

friends = new friendlyBase[numFriends];


for(int j = 0; j < numFriends; j ++)
{

	friends[j].clonefriendly(iObjNum, -50+(j*50),0, -450, 30);
	iObjNum++;
}


}

void Game::makeLevel()
{
	
//load the level
//Create box centered about the origin
dbMakeObjectBox(iObjNum, 1000, 10, 1000);
dbPositionObject(iObjNum, 0,-10,0);
dbColorObject(iObjNum, dbRGB(30,30,50));
SC_SetupObject( iObjNum,1,2 );


iObjNum++;

//load the barriers
dbMakeObjectBox(iObjNum, 400, 80, 50);
dbPositionObject(iObjNum,300,0,-300);
dbColorObject(iObjNum, dbRGB(255,0,50));

SC_SetupObject( iObjNum,1,2 );

AIAddStaticObstacle ( iObjNum );

iObjNum++;

//load the barriers
dbMakeObjectBox(iObjNum, 400, 80, 50);
dbPositionObject(iObjNum,-300,0,-300);
dbColorObject(iObjNum, dbRGB(255,0,50));

SC_SetupObject( iObjNum,1,2 );

AIAddStaticObstacle ( iObjNum );

iObjNum++;




AIStartNewObstacle ( );

	// add points in a anti-clockwise direction to
	// create a boundary (use clockwise to create an obstacle)
	AIAddObstacleVertex ( -500, -500 );
	AIAddObstacleVertex (  500, -500 );
	AIAddObstacleVertex (  500,  500 );
	AIAddObstacleVertex ( -500,  500 );

	// finish creating our boundary
	AIEndNewObstacle ( 0, 1 );

	// finalise our obstacles
	AICompleteObstacles ( );




}



void Game::updateGame(){


// hide or show paths
		if ( dbKeyState ( 2 ) && ptimer < dbTimer ( ) )
		{
			// update timer and mode
			ptimer = dbTimer ( ) + 300;
			pMode  = 1 - pMode;
			
			// decide what to do
			if ( pMode == 0 )
				AIDebugHidePaths ( );
			else
				AIDebugShowPaths ( 2.5 );	
				

		}
		
// hide or show obstacle bounds
if ( dbKeyState ( 3 ) && btimer < dbTimer ( ) )
		{
			// update timer and mode
			btimer = dbTimer ( ) + 300;
			bMode = 1 - bMode;
			
			// decide what to do
			if ( bMode == 0 )
				AIDebugHideObstacleBounds ( 0 );
			else
				AIDebugShowObstacleBounds ( 0, 2.5 );
				

		}

//update the player		
player.updatePlayer();


//control friendlies
	if ( dbKeyState ( 33 )&& stimer < dbTimer ( ) )
		{
			// update timer
			ftimer = dbTimer ( ) + 300;

			// follow player
			AITeamFollowPlayer( 20.0f);
		}
		
		// separate action && stimer < dbTimer ( )
		if ( dbKeyState ( 35 )&& stimer < dbTimer ( )  )
		{
			// update timer
			stimer = dbTimer ( ) + 300;

	
			for(int i = 0; i < numFriends; i++)
			{
				friends[i].hold();
			}
		
		}





// display information
		dbSetCursor ( 0,0 );
		dbPrint     ( "Enemies Are Red, Friendlies Are Green" );
		dbPrint     ( );
		dbPrint     ( "Controls: " );
		dbPrint		 ("[Mouse] Camera");
		dbPrint     ( "[ArrowKeys] Move Camera" );
		dbPrint		( "[Left-mouse] Simulates player firing weapons, attracts enemies in range");
		dbPrint     ( "[WASD] Move Player" );
		dbPrint     ( "[SPACE] Jump Player" );
		dbPrint     ( "[F] Make Friendlies Follow Player" );
		dbPrint     ( "[H] Hold Position" );
		dbPrint     ("[M] Move to Waypoint Maker ");
		dbPrint     ( );
		dbPrint     ( "Debug Controls: " );
		dbPrint     ( "[1] Toggle Entity Path" );
		dbPrint     ( "[2] Toggle Obstacle Bounds" );
		dbPrint     ( );
		dbPrintC    ( "FPS: " );
		dbPrint     ( ( LONGLONG ) dbScreenFPS ( ) );
	
	
	
//update all AI entities
for(int i = 0; i < numEnemies;i++)
{
enemies[i].updateEnemy();
}

for(int i =0; i < numFriends; i++)
{
		
		friends[i].updateFriendlies();
}
		//update the darkAI
		AIUpdate();



}


===================================================================================
Game.h
===================================================================================
#ifndef Game_H
#define Game_H

#include "DarkGDK.h"
#include "DarkAI.h"
#include "friendlyBase.h"
#include "SC_Collision.h"
#include "player.h"
#include "enemyBase.h"
#include "soundManager.h"


class Game
{
private:


	int iObjNum;

	int bMode, pMode;

	int ptimer, btimer, ftimer, stimer;

	player player;

	enemyBase *enemies;
	int numEnemies;

	friendlyBase *friends;
	int numFriends;

public:
	 
void startGame();
void makeLevel();
void updateGame();

};



#endif



===================================================================================
player.cpp
===================================================================================
#include "DarkGDK.h"
#include "DarkAI.h"
#include "player.h"
#include "SC_Collision.h"
#include "soundManager.h"

void player::createPlayer(int iObjNum)
{
//player variables used by sparkies collision
g_fSpeed = 5.0f;
g_fTurn = 0.03f;

gravity = -0.1f;
slope = 0.2f;
ground = 1;
jumptimer = 0;

//postion vector
float vx = 0;
float vy = 0;
float vz = 0;

iPlayerNum = iObjNum;


//create player object
dbMakeObjectSphere(iPlayerNum, 20);
dbPositionObject(iPlayerNum, 0, 0, -400);
dbColorObject(iPlayerNum, dbRGB(0,0,235));
AIAddPlayer(iPlayerNum);
SC_SetupObject( iPlayerNum,0,1 );

}

void player::movePlayer()
{
	dbYRotateObject( iPlayerNum,dbObjectAngleY(iPlayerNum) + dbMouseMoveX()/3.0f );
	dbXRotateObject( iPlayerNum,dbObjectAngleX(iPlayerNum) + dbMouseMoveY()/3.0f );

	float oldx = dbObjectPositionX(iPlayerNum);
	float oldy = dbObjectPositionY(iPlayerNum);
	float oldz = dbObjectPositionZ(iPlayerNum);

	//apply gravity, and user changes to movement
	float angy = dbObjectAngleY(iPlayerNum);
	vx = 0;
	vz = 0;

	//if player is jumping or falling then apply 'normal' gravity
	//if not attempt to keep the player stuck to the floor
	if ( vy == 0 && jumptimer == 0 ) vy = vy + 10*gravity; 
	else vy = vy + gravity;

	if (dbKeyState(32) == 1 ) { vx = vx + dbCos(angy); vz = vz - dbSin(angy); }
	if (dbKeyState(30) == 1 ) { vx = vx - dbCos(angy); vz = vz + dbSin(angy); }
	if (dbKeyState(31) == 1 ) { vx = vx - dbSin(angy); vz = vz - dbCos(angy); }
	if (dbKeyState(17) == 1 ) { vx = vx + dbSin(angy); vz = vz + dbCos(angy); }

	//only jump if on ground, and a certain time after last jump
	if ( ground == 1 ) 
	{
		if ( dbSpaceKey() == 1 && jumptimer == 0 ) 
		{
			vy = vy + 4.0f;
			jumptimer = 20;
		}
	}


	//can add value here to increase speed
	//this would be the player's final position without collision
	float x = oldx + vx;
	float y = oldy + vy;
	float z = oldz + vz;
	
	//first handle gravity - seperated from horizontal movement
	//to achieve a more realistic effect
	//Method: simple - cast a sphere vertically down, if it hits the level then
	//                 position the object there (sticky collision) then move 
	//                 on to horizontal movement
	//  more complex - if we were to only use the simple method the player would be 
	//                 allowed to climb almost vertical slopes. Alternative is to
	//                 get the normalY direction to work out how flat the gorund
	//                 below the player is, 0-slope# is flatter, slope#-1 is steeper.
	//                 if it's flat, use sticky collision, if it's steep slide the
	//                 player down the slope. Changing slope# determines how steep the 
	//                 player can climb. NOTE: this also effects stairs.
	int collide = SC_SphereCastGroup( 1, oldx,oldy,oldz, oldx,oldy+vy,oldz, 10,0 );
	
	if ( collide > 0 )
	{
		//how flat is this ground
		float ny = SC_GetCollisionNormalY();
		if ( dbAbs(ny) > slope )
		{
			//FLAT, stick
			oldy = SC_GetStaticCollisionY();
		}
		else
		{
			//STEEP, slide
			x = x - oldx; z = z - oldz;
			oldx = SC_GetCollisionSlideX();
			oldy = SC_GetCollisionSlideY();
			oldz = SC_GetCollisionSlideZ();
			x = x + oldx; z = z + oldz;
		}
		
		//ny#<0 means the player has hit a ceiling rather than a floor
		
		if ( ny > slope )
		{
			//only on ground if standing on flat ground
			ground = 1;
			vy = 0;
		}
		else 
		{
			ground = 0;
			//if player has hit a flat ceiling then stop vy# movement
			if ( ny < -slope ) vy = gravity;
		}
	}
	else
	{
		//nothing below player, not on ground, add vertical speed to player
		oldy = oldy + vy;
		ground = 0;
	}
	
	//jumptimer will decrease only when player is back on ground
	//creates a pause between two successive jumps
	if ( ground == 1 && jumptimer > 0 ) jumptimer--;
	
	//handle horizontal movement as sliding
	//player only collides with group 1 (level) objs and moves freely through others
	collide = SC_SphereSlideGroup( 1, oldx,oldy,oldz, x,oldy,z, 10,0 );
	
	if ( collide > 0 )
	{
		//if hit, reposition player, halt movement vector
		x = SC_GetCollisionSlideX();
		oldy = SC_GetCollisionSlideY();
		z = SC_GetCollisionSlideZ();
		vx = 0;
		vz = 0;
		
		//possible code for giving the player a jumping help up stairs...
		//might be useful if slope# is set very high but stairs are still required
		//dy = oldy - SC_GetStaticCollisionY()
		//if ( dy < slope && dy > 0 && ground == 1 ) vy = 0.5;
	}
	
	//position the player
	dbPositionObject( iPlayerNum,x,oldy,z );

	//create sound in the AI system at the player co-ords
	if(dbMouseClick() == 1 )
	{
	AICreateSound ( x, z, 1, 200 );
	playSound("shoot");
	
	}

	
	SC_UpdateObject( iPlayerNum );


}


void player::updatePlayer(){

	dbPositionMouse(dbScreenWidth()/2, dbScreenHeight()/2);

dbControlCameraUsingArrowKeys ( 0, g_fSpeed, g_fTurn );

	g_fOldCamAngleY = g_fCameraAngleY;
	g_fOldCamAngleX = g_fCameraAngleX;

	g_fCameraAngleY = dbWrapValue ( g_fCameraAngleY + dbMouseMoveX ( ) * 0.4f );
	g_fCameraAngleX = dbWrapValue ( g_fCameraAngleX + dbMouseMoveY ( ) * 0.4f );

	dbYRotateCamera ( dbCurveAngle ( g_fCameraAngleY, g_fOldCamAngleY, 24 ) );
	dbXRotateCamera ( dbCurveAngle ( g_fCameraAngleX, g_fOldCamAngleX, 24 ) );

//move the player object
	
	

	movePlayer();



}






===================================================================================
player.h
===================================================================================
#ifndef player_H
#define player_H


class player
{

protected:

float lasttime;

float g_fSpeed, g_fTurn, g_fOldCamAngleX,g_fOldCamAngleY,
g_fCameraAngleX,g_fCameraAngleY;

int iPlayerNum;

float gravity, slope;

int ground, jumptimer;

float vx,vy,vz;

int health;

public:
	
	void createPlayer(int iObjNum);
	void movePlayer();
	void updatePlayer();

};

#endif





===================================================================================
enemyBase.cpp
===================================================================================
#include "DarkGDK.h"
#include "DarkAI.h"
#include "enemyBase.h"
#include "soundManager.h"


enemyBase::enemyBase()
{

iAssociatedObject = 0;
shootTimer = 0;

}

//move object construction code to object manager

void enemyBase::createEnemy(int iObjNum)
{
	
//build an enemy
	dbMakeObjectCone     ( iObjNum,  20 );
	dbXRotateObject      ( iObjNum, 90 );
	dbFixObjectPivot     ( iObjNum );
	dbMakeMeshFromObject (5001, iObjNum);
	dbDeleteObject       ( iObjNum );

// attack template
	MakePlane            ( iObjNum+1500, 0.3f, 0.3f, 1 );
	dbXRotateObject      ( iObjNum+1500,90 );
	dbFixObjectPivot     ( iObjNum+1500 );
	dbMakeMeshFromObject ( 5002, iObjNum+1500 );
	dbDeleteObject       ( iObjNum+1500 );


}


//Create this enemy as a clone of an existing object
//creates a specific object to be associated with each instance of this class
void enemyBase::cloneEnemy(int iObjNum, int startPosX, int startPosY, int startPosZ,
		int speed)
{

		iAssociatedObject = iObjNum;
		health = 100;

		dbMakeObject     ( iObjNum, 5001, 0 );
		dbPositionObject ( iObjNum, startPosX, startPosY, startPosZ );
		dbColorObject    ( iObjNum, dbRgb ( 255, 0, 0 ) );
		//make fire solutiom
		dbMakeObject     ( iObjNum + 1000, 5002, 1 );
		dbSetObjectLight ( iObjNum + 1000, 0 );
		dbHideObject     ( iObjNum + 1000 );
		
		// add them to the enemy team
		AIAddEnemy              ( iObjNum );
		AISetEntitySpeed        ( iObjNum, speed );
		AISetEntityAggressive   ( iObjNum );
		AISetEntityViewArc      ( iObjNum,  90, 180 );
		AISetEntityViewRange    ( iObjNum,  200 );
		AISetEntityHearingRange ( iObjNum, 300 );

		AIEntityAssignPatrolPath ( iObjNum, 1 );


}

//Delete the objects associate with this instance of enemy
void enemyBase::DestroyEnemy()
{
AIKillEntity(iAssociatedObject);
dbDeleteObject(iAssociatedObject);
dbDeleteObject(iAssociatedObject+1000 );

}

//update the current enemy
 void enemyBase::updateEnemy(){

	 if(dbObjectExist(iAssociatedObject)) {
// hide attack lines
		
		 if ( dbObjectExist ( iAssociatedObject+1000 ) && shootTimer < 120 ) {
				dbPositionObject(iAssociatedObject+1000, dbObjectPositionX(iAssociatedObject+1000), 30.0, dbObjectPositionZ(iAssociatedObject+1000));
				dbHideObject( iAssociatedObject+1000 );
		 }


	 //fire control
	if ( AIEntityExist ( iAssociatedObject ) == 1 )
			{
				// check firing state
				if ( AIGetEntityCanFire ( iAssociatedObject ) && shootTimer <= 0 )
				{
					// determine new position
					float tx   = AIGetEntityTargetX ( iAssociatedObject );
					float tz   = AIGetEntityTargetZ ( iAssociatedObject );
					float x    = dbObjectPositionX  ( iAssociatedObject );
					float z    = dbObjectPositionZ  ( iAssociatedObject );
					float dx   = ( x + tx ) / 2.0f;
					float dz   = ( z + tz ) / 2.0f;
					float dist = sqrt ( ( tx - x ) * ( tx - x ) + ( tz - z ) * ( tz - z ) );
					float ang  = acos ( ( tz - z ) / dist ) * 57.29577951308f;
					
					if ( ( tx - x ) < 0 )
						ang = 360 - ang;

					dbPositionObject( iAssociatedObject + 1000, dx, 0.0f, dz );
					dbYRotateObject ( iAssociatedObject + 1000, ang + 0.1f );
					dbScaleObject   ( iAssociatedObject + 1000, 100, 100, 300 * dist );
					dbShowObject    ( iAssociatedObject + 1000 );
					
					shootTimer = 150;
					
					AICreateSound ( x, z, 1, 100 );
					playSound("shoot");
				}
			}

			// update timer
			if ( shootTimer > 0 )
				shootTimer -= 2;

		//has the eenmy been shot
		int collision = dbObjectCollision(iAssociatedObject,0);

		//has a friendly bullet hit the enemy
	if(collision >= 4000 && collision < 4020) {
					health-=0.1;
	}
	if(health == 0)
	{
DestroyEnemy();
	}



		}

 }



 void enemyBase::MakePlane(int iID, float fWidth, float fHeight, int iFlag)
 {

dbMakeObjectBox(iID, fWidth, fHeight, iFlag);
 }





===================================================================================
enemyBase.h
===================================================================================
#ifndef enemyBase_H
#define enemyBase_H


class enemyBase
{

protected:

int iAssociatedObject;
int shootTimer;
int health;
	

public:
	enemyBase();
	virtual void createEnemy(int iObjNum);
	virtual void cloneEnemy(int iObjNum, int startPosX, int startPosY, int startPosZ,
		int speed);
	virtual void DestroyEnemy();
	virtual void updateEnemy();
	void MakePlane      ( int iID, float fWidth, float fHeight, int iFlag );

};

#endif




===================================================================================
friendlyBase.cpp
===================================================================================
#include "DarkGDK.h"
#include "DarkAI.h"
#include "soundManager.h"
#include "friendlyBase.h"

friendlyBase::friendlyBase()
{

iAssociatedObject = 0;
}


void friendlyBase::clonefriendly(int iObjNum, int startPosX, int startPosY, int startPosZ, int speed)
{

	iAssociatedObject = iObjNum;
	int health = 100;
	//create firendly from standard template, replace code when model importer added
		dbMakeObject     ( iObjNum, 5001, 0 );
		dbPositionObject ( iObjNum, startPosX, startPosY, startPosZ );
		dbColorObject    ( iObjNum, dbRgb ( 0, 255, 0 ) );
		dbMakeObject     ( iObjNum + 1000, 5002, 2 );
		dbPositionObject ( iObjNum+1000, startPosX, startPosY, startPosZ );
		dbSetObjectLight ( iObjNum + 1000, 0 );
		dbHideObject     ( iObjNum + 1000 );
		
		// add them to the friendly team
		AIAddFriendly           ( iObjNum );
		AISetEntitySpeed        ( iObjNum, speed );
		AISetEntityViewArc      ( iObjNum, 90, 180 );
		AISetEntityViewRange    ( iObjNum, 200 );
		AISetEntityHearingRange ( iObjNum, 250 );
	

}

void friendlyBase::DestroyFriendly()
{
AIKillEntity(iAssociatedObject);
dbDeleteObject(iAssociatedObject);
dbDeleteObject(iAssociatedObject+1000 );

}


void friendlyBase::updateFriendlies()
{
	 if(dbObjectExist(iAssociatedObject)) {
// hide attack lines
		
		 if ( dbObjectExist ( iAssociatedObject+1000 ) && shootTimer < 120 ) {
				dbPositionObject(iAssociatedObject+1000, dbObjectPositionX(iAssociatedObject+1000), 30.0, dbObjectPositionZ(iAssociatedObject+1000));
				dbHideObject( iAssociatedObject+1000 );
		 }


	 //fire control
	if ( AIEntityExist ( iAssociatedObject ) == 1 )
			{
				// check firing state
				if ( AIGetEntityCanFire ( iAssociatedObject ) && shootTimer <= 0 )
				{
					// determine new position
					float tx   = AIGetEntityTargetX ( iAssociatedObject );
					float tz   = AIGetEntityTargetZ ( iAssociatedObject );
					float x    = dbObjectPositionX  ( iAssociatedObject );
					float z    = dbObjectPositionZ  ( iAssociatedObject );
					float dx   = ( x + tx ) / 2.0f;
					float dz   = ( z + tz ) / 2.0f;
					float dist = sqrt ( ( tx - x ) * ( tx - x ) + ( tz - z ) * ( tz - z ) );
					float ang  = acos ( ( tz - z ) / dist ) * 57.29577951308f;
					
					if ( ( tx - x ) < 0 )
						ang = 360 - ang;

					dbPositionObject( iAssociatedObject + 1000, dx, 0.0f, dz );
					dbYRotateObject ( iAssociatedObject + 1000, ang + 0.1f );
					dbScaleObject   ( iAssociatedObject + 1000, 100, 100, 300 * dist );
					dbShowObject    ( iAssociatedObject + 1000 );
					
					shootTimer = 150;
					
					AICreateSound ( x, z, 1, 100 );
					playSound("shoot2");
				}
			}

			// update timer
			if ( shootTimer > 0 )
				shootTimer -= 2;

		//has the eenmy been shot
		int collision = dbObjectCollision(iAssociatedObject,0);

		//has a friendly bullet hit the enemy
	if(collision >= 4000 && collision < 4020) {
					health-=0.1;
	}
	if(health == 0)
	{
DestroyFriendly();
	}



		}

 }

void friendlyBase::hold()
{
AIEntitySeparate(iAssociatedObject);
AIEntityHoldPosition(iAssociatedObject);


}






===================================================================================
friendlyBase.h
===================================================================================
#ifndef friendlyBase_H
#define friendlyBase_H


class friendlyBase
{
protected:

int iAssociatedObject;
int shootTimer;
int health;


public:
		friendlyBase();
		void DestroyFriendly();
		virtual void clonefriendly(int iObjNum, int startPosX, int startPosY, int startPosZ,
		int speed);
		void updateFriendlies();
		void MakePlane      ( int iID, float fWidth, float fHeight, int iFlag );
		void hold();

};

#endif