DRONES.C

/*********************************************************************** 
File: Drones.c


Abstract:

This file contains the functions which are associated with moving and
drawing drones.


Contents:

DeletePlayer() -- Free a record of type PlayerType
NewPlayer() -- Create & initialize a record of type PlayerType
RandRange() -- return a random number in a given range
InitDrones() -- Add/remove drone entries from the list of drones
MoveDrone() -- Timer callback to change drone positions in data struct


************************************************************************/

#include <stdlib.h>
#include "winmaze.h"
#include "mazproto.h"
#include <mmsystem.h>



/*=====================================================================
Function: DeletePlayer()

Inputs: Pointer to record to delete

Outputs: Returns pointer to next record in list

Abstract:
Returns pointer to next element to facilitate maintenance of
linked lists
======================================================================*/

PlayerType FAR *DeletePlayer(
PlayerType FAR *ptTrav
)
{
PlayerType FAR *ptHold;
HGLOBAL hMem;

ptHold = NULL;
if (ptTrav != NULL) {
ptHold = ptTrav->next;
hMem = (HGLOBAL) GlobalHandle(SELECTOROF( ptTrav));
GlobalUnlock(hMem);
GlobalFree(hMem);
}

return(ptHold);
}


/*=====================================================================
Function: NewPlayer()

Inputs: Data elements of PlayerType element to be created

Outputs: Pointer to newly created & initialize PlayerType record

Abstract:
Needs no explanation
======================================================================*/

PlayerType FAR *NewPlayer(
unsigned long ulID, // Unique identifier for this player struct
DWORD dwPID, // process id of window
LPSTR cUserName, // Name of the player
LPSTR cComputerName, // Name of the computer player is on
int ix, // Maze X- coordinate of player
int iy, // Maze Y- coordinate of player
BYTE Facing, // Facing of player in maze
int iScore, // Points player is reporting
int iPicNum, // Picture number to use for player
int iGridNum, // Home grid #
int iGridxPos, // Home grid x-position (in grid coordinates
int iGridyPos, // Home grid y-position (in grid coordinates
PlayerType FAR *next // for linked list maintenance
)
{
PlayerType FAR *ptRet;
HGLOBAL hMem;

hMem = GlobalAlloc(GHND,sizeof(PlayerType));
ptRet = (PlayerType FAR *) GlobalLock(hMem);

if (ptRet == NULL) {
MessageBox((HWND) NULL,GetStringRes(IDS_MALLOCFAILABT),"NewPlayer",
MB_APPLMODAL|MB_ICONEXCLAMATION);
PostMessage(hWndMain,WM_CLOSE,0,0);
}
else {
ptRet->ulID = ulID;
ptRet->dwPID = dwPID;
lstrcpy(ptRet->cUserName,cUserName);
lstrcpy(ptRet->cComputerName,cComputerName);
ptRet->Pos.ix = ix;
ptRet->Pos.iy = iy;
ptRet->Pos.Facing = Facing;
ptRet->Drawn = FALSE;
ptRet->iScore = iScore;
ptRet->iPicNum = iPicNum;
ptRet->iGridNum = iGridNum;
ptRet->pGridLoc.x = iGridxPos;
ptRet->pGridLoc.y = iGridyPos;

ptRet->next = next;
}

return(ptRet);
}



/*=====================================================================
Function: RandRange()

Inputs: Low, high end of range return value should be in

Outputs: returns number between iLow and iHigh inclusive

Abstract:
Since this is using rand() it is not so random for a new process.
======================================================================*/

int RandRange(
int iLow,
int iHigh
)
{
float f;

f = ((float) rand())/((float) RAND_MAX);

return((int) (iLow+f*(iHigh - iLow + 1)));
}



/*=====================================================================
Function: InitDrones()

Inputs: Globals only

Outputs: none

Abstract:
Performs PART of drone initialization whenever # of drones or their
speed is changed. This will add or delete drones from the drone list,
as required.
======================================================================*/

void InitDrones(
)
{
int i,iCurNumDrones;
BYTE b;
PlayerType FAR *ptTrav;
char buff[20];


b = NORTH;

//
// If the number of drones has changed, but drones were
// already active, we need to take a different course of
// action, ie add or delete drones from active drones,
// rather than creating completely new ones.
//

//
// Find number of drones currently active.
//
iCurNumDrones = 0;
ptTrav = &ptDrones;
while (ptTrav->next != NULL) {
iCurNumDrones++;
ptTrav = ptTrav->next;
}

//
// read in/ remove the drone bitmaps, as necessary
//
if (iNumDrones > 0) {
AddPic(PIC_DRONE);
}
else {
DelPic(PIC_DRONE);
}

//
// If more drones have been added, we need to create them
//
for(i=iCurNumDrones;i<iNumDrones;i++) {
sprintf(buff,GetStringRes(IDS_FMT_DRONE),i+1);
ptTrav->next = NewPlayer(i,i,buff,ptSelf.cComputerName,
RandRange(5*X_CELLS_PER_SUBGRID,8*X_CELLS_PER_SUBGRID-1),
RandRange(7*Y_CELLS_PER_SUBGRID,8*Y_CELLS_PER_SUBGRID-1),
b,0,PIC_DRONE,0,0,0,ptTrav->next);
b = (BYTE) ((b*2 > 0x8) ? 0x1 : b*2);
}

//
// Or, delete drones as needed
//
for(i=iNumDrones; i < iCurNumDrones;i++) {
ptDrones.next = DeletePlayer(ptDrones.next);
}

return;
}



/*=====================================================================
Function: MoveDrone()

Inputs: Ignored except for hWnd, handle to current window

Outputs:returns success/failure

Abstract:
This is a timer callback function which modifies the position of the
drones in their data structures. After their positions have been changed
in their record, a message is sent to the 3-d window telling it that
drones need to be redrawn.
======================================================================*/

LRESULT CALLBACK MoveDrone(
HWND hWnd,
UINT wMsg,
UINT idTimer,
DWORD dwTime
)
{
int x,y;
BYTE b1,b2,dForward,dBack,dLeft,dRight;
BOOL blocked;
PlayerType FAR *ptTrav;
PositionType ptPos;
char cBuff[132];
LPRECT rUpd;
HANDLE hMem;
static DWORD dwCallCount=0;

dwCallCount++;

//
// If we're in demo mode, use this callback to move US!
//
if (bDemoMode) {

x=ptSelf.Pos.ix;
y=ptSelf.Pos.iy;
dForward = ptSelf.Pos.Facing;
dBack = BACK_TO_ABS(dForward);
dLeft = LEFT_TO_ABS(dForward);
dRight = RIGHT_TO_ABS(dForward);
b1 = bMaze[x][y];
b2 = bMaze[ADJ_X(x,dForward)][ADJ_Y(y,dForward)];
blocked = FALSE;
if ((b1&dForward)||(b2&dBack)) {
blocked = TRUE;
b2 = bMaze[ADJ_X(x,dRight)][ADJ_Y(y,dRight)];
if ((b1&dLeft)||(b2&dRight)) {
b2 = bMaze[ADJ_X(x,dLeft)][ADJ_Y(y,dLeft)];
if ((b1&dLeft)||(b2&dRight)) {
x = RIGHT;
}
else {
x = LEFT;
}
}
else {
x = RIGHT;
}
}
else {
x = FORWARD;
}

y = RandRange(1,11);

switch (y) {
case 8:
x = LEFT;
break;
case 9:
x = RIGHT;
break;
case 10:
x = FORWARD;
break;
case 11:
x = BACK;
break;
}

switch (x) {
case LEFT:
PostMessage(hWndMaze,WM_KEYDOWN,VK_LEFT,0);
break;
case RIGHT:
PostMessage(hWndMaze,WM_KEYDOWN,VK_RIGHT,0);
break;
case FORWARD:
PostMessage(hWndMaze,WM_KEYDOWN,VK_UP,0);
break;
}

if ((RandRange(1,3) == 3)&&(!blocked)) {
PostMessage(hWndMaze,WM_KEYDOWN,VK_SPACE,0);
}
}

hMem = GlobalAlloc(GHND,sizeof(RECT));
rUpd = (LPRECT) GlobalLock(hMem);
if (rUpd == NULL) {
MessageBox((HWND)NULL,GetStringRes(IDS_RECTALLOCFAIL),"MoveDrone",
MB_APPLMODAL);
PostMessage(hWndMain,WM_CLOSE,0,0);
}

rUpd->right = rMaze.left;
rUpd->left = rMaze.right;
rUpd->top = rMaze.bottom;
rUpd->bottom = rMaze.top;

ptTrav = &ptDrones;
while ((ptTrav->next != NULL)&&(!GamePaused)) {
//
// Only use every 2nd drone move in demo mode
//
if (bDemoMode && (dwCallCount % 2)) {
break;
}

ptTrav = ptTrav->next;

if (ptTrav->Drawn) {
rUpd->right = (rUpd->right > ptTrav->rDrawn.right) ? rUpd->right : ptTrav->rDrawn.right;
rUpd->left = (rUpd->left < ptTrav->rDrawn.left) ? rUpd->left : ptTrav->rDrawn.left;
rUpd->top = (rUpd->top < ptTrav->rDrawn.top) ? rUpd->top : ptTrav->rDrawn.top;
rUpd->bottom = (rUpd->bottom > ptTrav->rDrawn.bottom) ? rUpd->bottom : ptTrav->rDrawn.bottom;
ptTrav->Drawn=FALSE;
}
x=ptTrav->Pos.ix;
y=ptTrav->Pos.iy;
dForward = ptTrav->Pos.Facing;
dBack = BACK_TO_ABS(dForward);
dLeft = LEFT_TO_ABS(dForward);
dRight = RIGHT_TO_ABS(dForward);
ptPos.ix = x;
ptPos.iy = y;
ptPos.Facing = dForward;
b1 = bMaze[x][y];
b2 = bMaze[ADJ_X(x,dForward)][ADJ_Y(y,dForward)];
if ((b1&dForward)||(b2&dBack)) {
b2 = bMaze[ADJ_X(x,dLeft)][ADJ_Y(y,dLeft)];
if ((b1&dLeft)||(b2&dRight)) {
b2 = bMaze[ADJ_X(x,dRight)][ADJ_Y(y,dRight)];
if ((b1&dRight)||(b2&dLeft)) {
ptPos.Facing = LEFT_TO_ABS(ptTrav->Pos.Facing);
}
else {
ptPos.Facing = RIGHT_TO_ABS(ptTrav->Pos.Facing);
}
}
else {
ptPos.Facing = LEFT_TO_ABS(ptTrav->Pos.Facing);
}
}
else {
if (RandRange(1,10) == 1) {
ptPos.Facing = LEFT_TO_ABS(ptTrav->Pos.Facing);
}
else {
if (RandRange(1,10) == 1) {
ptPos.Facing = RIGHT_TO_ABS(ptTrav->Pos.Facing);
}
else {
ptPos.ix = ADJ_X(ptTrav->Pos.ix,dForward);
ptPos.iy = ADJ_Y(ptTrav->Pos.iy,dForward);
}
}
}

ptTrav->Pos = ptPos;

//
// If the drone has homed in on us, we are dead!
//
if ((!InSanctuary(&ptSelf.Pos))&&
(ptSelf.Pos.ix == ptPos.ix)&&(ptSelf.Pos.iy == ptPos.iy)
) {
sprintf(cBuff,GetStringRes(IDS_FMT_RUNDOWN),ptTrav->cUserName);
PrintTextLine(cBuff);
ptSelf.iScore -=(iDroneSpeed) ? 36/iDroneSpeed/iDroneSpeed : 72;
KillSelf();
SendNetMessage(0,0,NULL,NP_SCORE);
iKilledByDrones++;
break;
}
}

PostMessage(hWndMaze,WM_COMMAND,IDM_DRAWDRONES,(DWORD) rUpd);

return(0);
}