/******************* ( Animation Construction Kit 3D ) ***********************/
/*		       Example using the ACK engine			     */
/*			   Author: Lary Myers				     */
/*****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
#include <mem.h>
#include <alloc.h>
#include <io.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <conio.h>
#include <sys\stat.h>

#include "ack3d.h"
#include "example1.h"


	ACKENG	    *ae;
	MOUSE	    mouse;

	char	    *MapFileName = "EXAMPLE1.MAP";
	char	    *PictureFile = "EXAMPLE1.LBM";
	char	    *PalFile	 = "EXAMPLE1.PAL";
	UCHAR far   *OverlayPic;

	BMTABLE bmTable[] = {
		1   ,TYPE_WALL	    ,"swall16.bbm",
		2   ,TYPE_WALL	    ,"swall17.bbm",
		3   ,TYPE_WALL	    ,"swall18.bbm",
		60  ,TYPE_WALL	    ,"sdoor.bbm",
		61  ,TYPE_WALL	    ,"swall16.bbm",
		62  ,TYPE_WALL	    ,"sdoor.bbm",
		1   ,TYPE_OBJECT    ,"sman1.bbm",
		2   ,TYPE_OBJECT    ,"can1.bbm",
		-1  ,-1		    ,""		    /* End of table */
		};


	ColorRange  ranges[64] = {
				16,16,
				32,16,
				48,16,
				64,16,
				80,16,
				96,8,
				104,8,
				112,8,
				120,8,
				128,8,
				136,8,
				144,8,
				152,8,
				160,8,
				168,8,
				176,8,
				184,8,
				192,16,
				208,16,
				224,8,
				232,8,
				0,0
				};


/* Entry point for application */
/****************************************************************************
**									   **
****************************************************************************/
int main(void)
{
    int	    result,done = 0;
    int	    i,j,Spin,SpinAngle;

result = AppInitialize();

if (result)
    {
    printf("Error initializing: ErrorCode = %d\n",result);
    return(1);
    }


result = AppSetupEngine();

if (result)
    {
    printf("Error setting up ACK engine: ErrorCode = %d\n",result);
    AckWrapUp(ae);
    return(1);
    }


result = AppSetupOverlay();

if (result)
    {
    printf("Error loading overlay: ErrorCode = %d\n",result);
    AckWrapUp(ae);
    return(1);
    }


result = AppLoadBitmaps();

if (result)
    {
    printf("Error loading bitmaps: ErrorCode = %d\n",result);
    AckWrapUp(ae);
    return(1);
    }

result = AppSetupObjects();

if (result)
    {
    printf("Error creating objects: ErrorCode = %d\n",result);
    AckWrapUp(ae);
    return(1);
    }

result = AppSetGraphics();

if (result)
    {
    AckSetTextmode();
    printf("Error loading palette: ErrorCode = %d\n",result);
    AckWrapUp(ae);
    return(1);
    }

AppSetupPalRanges();

Spin = SpinAngle = 0;

/*---------------------------------------------------------------------------*
*									     *
*----------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
* Main execution loop. Here we check for mouse movement and continuously     *
* display the 3D screen. Escape will exit this loop.			     *
*----------------------------------------------------------------------------*/
while (!done)
    {


/*---------------------------------------------------------------------------*
* Check if we have turned the POV with the mouse.			     *
*----------------------------------------------------------------------------*/
    if (Spin)
	{
	Spin >>= 1;
	ae->PlayerAngle += SpinAngle;
	if (ae->PlayerAngle >= INT_ANGLE_360)
	    ae->PlayerAngle -= INT_ANGLE_360;
	if (ae->PlayerAngle < 0)
	    ae->PlayerAngle += INT_ANGLE_360;

	}

/*---------------------------------------------------------------------------*
* See if any object animation is needed (none in this example)		     *
*----------------------------------------------------------------------------*/
    AckCheckObjectMovement(ae);

/*---------------------------------------------------------------------------*
* Build the 3D views and display both front and back views on the screen.    *
*----------------------------------------------------------------------------*/
    AppShow3D();


    if (kbhit())
	if (getch() == 27)  /* Check for the escape key */
	    break;


/*---------------------------------------------------------------------------*
* Get any mouse changes that may have occurred.				     *
*----------------------------------------------------------------------------*/
    CheckMouse(&mouse);


/*---------------------------------------------------------------------------*
* Turning left.								     *
*----------------------------------------------------------------------------*/
    if (mouse.mdx < 0)
	{
	Spin = -mouse.mdx;
	Spin >>= 3;
	SpinAngle = -INT_ANGLE_2 * Spin;
	Spin = 1;
	}

/*---------------------------------------------------------------------------*
* Turning right.							     *
*----------------------------------------------------------------------------*/
    if (mouse.mdx > 0)
	{
	Spin = mouse.mdx;
	Spin >>= 3;
	SpinAngle = INT_ANGLE_2 * Spin;
	Spin = 1;
	}

/*---------------------------------------------------------------------------*
* Moving forward.							     *
*----------------------------------------------------------------------------*/
    if (mouse.mdy < 0)
	{
	i = -mouse.mdy;
	i >>= 2;
	i += 16;
	AckMovePOV(ae,ae->PlayerAngle,i);
	}

/*---------------------------------------------------------------------------*
* Moving backward.							     *
*----------------------------------------------------------------------------*/
    if (mouse.mdy > 0)
	{
	i = mouse.mdy;
	i >>= 2;
	i += 16;
	j = ae->PlayerAngle + INT_ANGLE_180;
	if (j >= INT_ANGLE_360)
	    j -= INT_ANGLE_360;

	AckMovePOV(ae,j,i);
	}
    }

AckWrapUp(ae);

AckSetTextmode();

return(0);
}


/****************************************************************************
**									   **
****************************************************************************/
/* This could be a routine that the application uses to initialize things */
int AppInitialize(void)
{
    int	    result = 0;

if (mouse_installed() != -1)
    {
    printf("Mouse required to run.\n");
    return(-2);
    }

mouse_hide_cursor();

ae = malloc(sizeof(ACKENG));	/* We first get memory for the structure */

if (ae == NULL)			/* Whoops, we didn't get the memory */
    return(-1);			/* So return an error */

memset(ae,0,sizeof(ACKENG));	/* Now we clear out the entire structure */

/* Perform other initialization here */

return(result);
}

/****************************************************************************
** Perform the initial setup of the ACK engine. The parameters below tell  **
** the engine how big the viewport is going to be, etc. Next the map file  **
** is read in as well as a pre-drawn background buffer.			   **
**									   **
** NOTE: Ceiling and floor colors are not really needed with this example. **
**									   **
****************************************************************************/
int AppSetupEngine(void)
{
    int	    result;

ae->WinStartX = VIEW_X;
ae->WinStartY = VIEW_Y;			    /* Plug in the size we want */
ae->WinEndX   = VIEW_X1;		    /* for our viewport		*/
ae->WinEndY   = VIEW_Y1;

ae->DoorSpeed	= DOORSPEED;
ae->xPlayer	= PLAYER_X;		    /* Setup intial coordinates */
ae->yPlayer	= PLAYER_Y;		    /* for the POV		*/
ae->PlayerAngle = PLAYER_ANGLE;

result = AckInitialize(ae);		    /* Then initialize the engine! */

if (result)
    return(result);			    /* Error, so get out now	*/

result = AckReadMapFile(ae,MapFileName);

if (result)
    {
    AckWrapUp(ae);
    return(result);			    /* Error, so get out now	*/
    }

ae->TopColor	= CEILING_COLOR;	    /* Setup our colors for the */
ae->BottomColor = FLOOR_COLOR;		    /* background....		*/

ae->LightFlag = SHADING_ON;		    /* Yes, we want light shading */

/* Not using the default background */
#if 0
result = AckBuildBackground(ae);	    /* Build the ceiling, floor	  */
#endif

if (ae->BkgdBuffer)
    free(ae->BkgdBuffer);

ae->BkgdBuffer = AckReadiff("exback.lbm");

if (result)
    AckWrapUp(ae);

return(result);				    /* 0 if no error, else errorcode */
}

/****************************************************************************
** Read in the full screen picture.					   **
**									   **
****************************************************************************/
int AppSetupOverlay(void)
{
    int		result = 0;

OverlayPic = AckReadiff(PictureFile);	/* Load a Deluxe Paint picture	*/

if (OverlayPic == NULL)			/* Whoops, got a problem	*/
    return(-1);				/* So return with an error	*/

/* Not using an overlay for this example */
#if 0
result = AckCreateOverlay(ae,&OverlayPic[4]);	/* Compile the overlay	*/
#endif

return(result);
}

/****************************************************************************
** Using our table above, read in all the bitmaps needed for this example. **
**									   **
****************************************************************************/
int AppLoadBitmaps(void)
{
    int	    result;
    int	    i = 0;

while (bmTable[i].Number != -1)
    {
    result = AckLoadBitmap(ae,
			   bmTable[i].Number,
			   bmTable[i].Type,
			   bmTable[i].Name);

    if (result)					/* Error during load */
	break;					/* so get out now    */

    i++;					/* Next index in table */
    }


return(result);
}

/****************************************************************************
** Call the ACK engine to initialize our objects. Each object can have more**
** than 1 bitmap associated with it.					   **
****************************************************************************/
int AppSetupObjects(void)
{
    int		result;
    UCHAR	BitmapNumbers[2];

ae->ObjList[1].Dir   = 0;	/* Direction doesn't matter */
ae->ObjList[1].Speed = 0;	/* is a stationary object   */

BitmapNumbers[0] = 1;		/* Bitmap to use with object (spaceman) */

result = AckCreateObject(ae,1,1,BitmapNumbers);

if (result)			/* An error occurred */
    return(result);		/* so get out now    */

ae->ObjList[2].Dir   = 0;	/* Again a direction is irrelavent */
ae->ObjList[2].Speed = 0;	/* Because speed 0 is stationary   */

BitmapNumbers[0] = 2;		/* Bitmap to use (canister) */

result = AckCreateObject(ae,2,1,BitmapNumbers);

return(result);
}

/****************************************************************************
** Go into normal VGA mode 13h graphics mode and display our full screen   **
** picture. Read in and set the palette we want to use.			   **
**									   **
****************************************************************************/
int AppSetGraphics(void)
{
    int	    result;
    UCHAR far *Video;


AckSetVGAmode();	/* Go into graphics */

if (OverlayPic != NULL)
    {
    Video = MK_FP(0xA000,0);
    memmove(Video,&OverlayPic[4],64000);
    }

result = AckLoadAndSetPalette(PalFile);

return(result);
}


/****************************************************************************
** This routine sets up a range of palette values that ACK will use for	   **
** light shading. The ranges[] table above demonstrates one form of these  **
** palette values.							   **
****************************************************************************/
void AppSetupPalRanges(void)
{
	int	i,j,k,found;
	int	rangenos;
	UCHAR	plotcolor;

for (rangenos = 0; rangenos < 64; rangenos++)
    {
    if (ranges[rangenos].start == 0)
	break;
    }


for ( i = 0;i<16;i++)
    {
    for (j=0;j<256;j++)
	{
	found = 0;
	// find the range the color is in.
	for ( k = 0; k < rangenos; k++ )
	    {
	    if (j >= ranges[k].start && j < ranges[k].start+ranges[k].length)
		{
		found = 1;
		break;
		}
	    }
	if (found)
	    {
	    // add color + i;
	    // if color + i > color+range then plot color = 0;
	    // otherwise plotcolor = color+i
	    if (j+i >= ranges[k].start+ranges[k].length)
		   plotcolor = 0;
	    else
		   plotcolor = j+i;
	    }
	else
	    {
	    plotcolor = j;
	    }
	ae->PalTable[j+(i*256)] = plotcolor;
	}
    }


}

/****************************************************************************
** Display both a front and back view of the POV. The user will essentially**
** have "eyes in the back of thier heads!".				   **
**									   **
****************************************************************************/
void AppShow3D(void)
{
    int		row,angle,oldangle;
    UINT	offset,offset1;
    UINT	bufoffset;
    UCHAR   far *Video;
    UCHAR   far *Src;

/* Any preprocessing the application wishes to do can go here */

AckBuildView(ae);	/* Tell the ACK engine to construct the POV */

#if 0
AckDisplayScreen(ae);	/* Display the POV on the video screen */
#endif

bufoffset = (320*VIEW_Y) + VIEW_X;
offset = (320*7) + 10;
offset1 = (320*110)+10;

Video = MK_FP(0xA000,offset);

Src = ae->ScreenBuffer + bufoffset;

for (row = 7; row < 89; row++)
    {
    memmove(Video,Src,152);
    Video += 320;
    Src += 320;
    }

oldangle = ae->PlayerAngle;
angle = oldangle + INT_ANGLE_180;
if (angle >= INT_ANGLE_360)
    angle -= INT_ANGLE_360;

/* Set an angle 180 degrees from the current POV angle */

ae->PlayerAngle = angle;

/* Build a new view to display */

AckBuildView(ae);

Video = MK_FP(0xA000,offset1);

Src = ae->ScreenBuffer + bufoffset;

for (row = 110; row < 192; row++)
    {
    memmove(Video,Src,152);
    Video += 320;
    Src += 320;
    }

ae->PlayerAngle = oldangle;

}

/****************************************************************************
** Check the current position of the mouse to see if any changes have	   **
** occurred since the last time. These changes are then used to move the   **
** POV around the map.							   **
****************************************************************************/
void CheckMouse(MOUSE *m)
{
    int	    dx,dy;
    int	    x,y,buttons;

mouse_read_cursor(&buttons,&y,&x);
dx = x - 160;
dy = y - 120;
m->mButtons = buttons;
mouse_set_cursor(120,160);

if (abs(dy) > 10 && abs(dx) < 32)
    dx >>= 2;

m->mdx = dx;
m->mdy = dy;

}

/** End of text **/

