add new simple example project code (no assets file yet)

This commit is contained in:
Gered 2019-11-03 17:28:02 -05:00
parent 4321b7751d
commit e35ecef214
4 changed files with 777 additions and 0 deletions

492
example/ackinfo.c Normal file
View file

@ -0,0 +1,492 @@
// Function to process and ACK resource file
#include <stdio.h>
#include <io.h>
#include "ack3d.h"
#include "ackeng.h"
#include "ackext.h"
// The application MUST have a global variable called ae for use by this
// routine.
extern ACKENG *ae;
// Globals
int LineNumber;
char LineBuffer[200];
short LastObjectIndex;
int MapResource;
int PalResource;
int ResScreenBack;
int ResScrollBack;
// Creates the correct arrays for displaying a rotating background
void ProcessBackDrop(UCHAR *bPtr) {
int i, j, pos;
UCHAR *aPtr;
for (i = 0; i < 320; i++) {
pos = i + 4;
aPtr = BackArray[i];
for (j = 0; j < 100; j++) {
*aPtr++ = bPtr[pos];
pos += 320;
}
}
for (i = 320; i < 640; i++) {
pos = (i - 320) + 32004;
aPtr = BackArray[i];
for (j = 0; j < 100; j++) {
*aPtr++ = bPtr[pos];
pos += 320;
}
}
}
// Loads a background image and processes the image into the
// separate slices for use at display time. Currently a background image
// can be 640 columns wide. This number can be made dynamic if needbe and would
// need to be changed in the routine below and in the DrawBackground routine
// in the ACK engine.
int LoadBackDrop(void) {
UCHAR *bPtr;
if (ResScrollBack) {
bPtr = AckReadiff((char*)ResScrollBack);
if (bPtr == NULL)
return 8;
ProcessBackDrop(bPtr);
AckFree(bPtr);
}
return 0;
}
// Reads a text line from the resource file
int ReadLine(void) {
int len;
char ch;
len = 0;
while (len < 200) {
if (read(rsHandle, &LineBuffer[len], 1) != 1)
break;
ch = LineBuffer[len];
if (ch == 10)
continue;
if (ch == 13)
break;
len++;
}
LineBuffer[len] = '\0';
return len;
}
// Skips to the next parameter in a text line
char *GetNextParm(char *s) {
char ch;
while (*s) {
ch = *s++;
if (ch == ',') {
while (*s) {
ch = *s;
if (ch != ',' && ch != ' ' && ch != '\t')
return s;
s++;
}
return NULL;
}
}
return NULL;
}
// Loads a wall bitmap specified in info file
int LoadWall(void) {
int wnum, rnum, result;
long pos;
char *lb;
lb = LineBuffer; // Info file buffer
wnum = atoi(lb); // Wall number to load into
lb = GetNextParm(lb);
if (lb == NULL)
return -1;
rnum = atoi(lb); // Resource number
pos = lseek(rsHandle, 0L, SEEK_CUR);
result = AckLoadWall(ae, (short)wnum, (char *)rnum);
lseek(rsHandle, pos, SEEK_SET);
return result;
}
// Loads an object bitmap specified in info file
int LoadObject(void) {
int onum, rnum, result;
long pos;
char *lb;
lb = LineBuffer;
onum = atoi(lb); // Object bitmap number
lb = GetNextParm(lb);
if (lb == NULL)
return -2;
rnum = atoi(lb); // Resource number
pos = lseek(rsHandle, 0L, SEEK_CUR);
result = AckLoadObject(ae, (short)onum, (char *)rnum);
lseek(rsHandle, pos, SEEK_SET);
return result;
}
// Skip any leading spaces in the string
// NOTE: Actually modifies the string passed!
char *SkipSpaces(char *s) {
while (*s == ' ' || *s == '\t' || *s == ',')
strcpy(s, &s[1]);
return s;
}
// Creates and object of the desired style
int CreateObject(void) {
short onum, vnum;
short result, oType;
short NumViews, bmPerView;
USHORT flags;
char *lb;
OBJSEQ os;
lb = LineBuffer;
if (!strnicmp(lb, "NUMBER:", 7)) {
lb = &lb[7];
onum = (short)atoi(lb);
if (onum < 1 || onum > MAX_OBJECTS)
return -3;
result = AckCreateObject(ae, (short)onum);
if (result)
return result;
LastObjectIndex = onum;
lb = GetNextParm(lb);
if (lb == NULL)
return -4;
ae->ObjList[onum]->Speed = (short)atoi(lb);
return 0;
}
onum = LastObjectIndex; // Object number
oType = 0;
if (!strnicmp(lb, "CREATE:", 7)) {
oType = NO_CREATE;
lb = &lb[7];
}
if (!strnicmp(lb, "DESTROY:", 8)) {
oType = NO_DESTROY;
lb = &lb[8];
}
if (!strnicmp(lb, "WALK:", 5)) {
oType = NO_WALK;
lb = &lb[5];
}
if (!strnicmp(lb, "ATTACK:", 7)) {
oType = NO_ATTACK;
lb = &lb[7];
}
if (!strnicmp(lb, "INTERACT:", 9)) {
oType = NO_INTERACT;
lb = &lb[9];
}
if (!oType)
return -5;
lb = SkipSpaces(lb);
if (lb == NULL)
return -6;
flags = 0;
strupr(lb);
if (strstr(lb, "ANIMATE") != NULL)
flags |= OF_ANIMATE;
if (strstr(lb, "MOVEABLE") != NULL)
flags |= OF_MOVEABLE;
if (strstr(lb, "PASSABLE") != NULL)
flags |= OF_PASSABLE;
if (strstr(lb, "MULTIVIEW") != NULL)
flags |= OF_MULTIVIEW;
if (strstr(lb, "SHOWONCE") != NULL)
flags |= OF_ANIMONCE;
lb = GetNextParm(lb);
if (lb == NULL)
return-5;
NumViews = (short)atoi(lb);
if (NumViews < 1)
return -6;
lb = GetNextParm(lb);
if (lb == NULL)
return -7;
bmPerView = (short)atoi(lb);
if (bmPerView < 1)
return -7;
vnum = (short)(NumViews * bmPerView);
if (vnum > MAX_OBJ_BITMAPS)
return -8;
lb = GetNextParm(lb);
if (lb == NULL)
return -9;
vnum = 0;
while (lb != NULL && vnum < MAX_OBJ_BITMAPS) {
os.bitmaps[vnum++] = (short)atoi(lb);
lb = GetNextParm(lb);
}
os.bmBitmapsPerView = bmPerView;
os.flags = flags;
os.MaxBitmaps = bmPerView;
os.bmSides = NumViews;
result = (short)AckSetupObject(ae, onum, oType, &os);
return result;
}
// Reads the ASCII info file and processes the commands.
int ProcessInfoFile(void) {
int result;
int mode;
long pos;
LineNumber = 0;
// Position to start of info file within resource file
lseek(rsHandle, rbaTable[0], SEEK_SET);
mode = result = 0;
while (!result) {
LineNumber++;
if (!ReadLine())
continue;
if (*LineBuffer == ';')
continue;
if (!strnicmp(LineBuffer, "END:", 4))
break;
printf(".");
switch (mode) {
case 1: // Read walls
if (!strnicmp(LineBuffer, "LOADTYPE:", 9)) {
ae->bmLoadType = (short)atoi(&LineBuffer[9]); // Sets for GIF or BBM
break;
}
if (!strnicmp(LineBuffer, "ENDBITMAPS:", 11))
mode = 4;
else
result = LoadWall();
break;
case 2: // Object bitmaps
if (!strnicmp(LineBuffer, "LOADTYPE:", 9)) { // Sets for GIF or BBM
ae->bmLoadType = (short)atoi(&LineBuffer[9]);
break;
}
if (!strnicmp(LineBuffer, "ENDBITMAPS:", 11))
mode = 5;
else
result = LoadObject();
break;
case 3: // Create Object
if (!strnicmp(LineBuffer, "ENDDESC:", 8))
mode = 5;
else
result = CreateObject();
break;
case 4: // Walls topic
if (!strnicmp(LineBuffer, "BITMAPS:", 8))
mode = 1;
if (!strnicmp(LineBuffer, "ENDWALLS:", 9))
mode = 0;
break;
case 5: // Objects topic
if (!strnicmp(LineBuffer, "BITMAPS:", 8))
mode = 2;
if (!strnicmp(LineBuffer, "OBJDESC:", 8))
mode = 3;
if (!strnicmp(LineBuffer, "ENDOBJECTS:", 11))
mode = 0;
break;
default:
if (!strnicmp(LineBuffer, "WALLS:", 6)) {
mode = 4;
break;
}
if (!strnicmp(LineBuffer, "OBJECTS:", 8)) {
mode = 5;
break;
}
if (!strnicmp(LineBuffer, "MAPFILE:", 8)) {
MapResource = atoi(&LineBuffer[8]);
pos = lseek(rsHandle, 0L, SEEK_CUR);
result = AckReadMapFile(ae, (char*)MapResource);
lseek(rsHandle, pos, SEEK_SET);
break;
}
if (!strnicmp(LineBuffer, "PALFILE:", 8)) {
PalResource = atoi(&LineBuffer[8]);
break;
}
if (!strnicmp(LineBuffer, "XPLAYER:", 8)) {
ae->xPlayer = (short)atoi(&LineBuffer[8]);
break;
}
if (!strnicmp(LineBuffer, "YPLAYER:", 8)) {
ae->yPlayer = (short)atoi(&LineBuffer[8]);
break;
}
if (!strnicmp(LineBuffer, "PLAYERANGLE:", 12)) {
ae->PlayerAngle = (short)atoi(&LineBuffer[12]);
break;
}
if (!strnicmp(LineBuffer, "SCREENBACK:", 11)) {
ResScreenBack = atoi(&LineBuffer[11]);
break;
}
if (!strnicmp(LineBuffer, "SCROLLBACK:", 11)) {
ResScrollBack = atoi(&LineBuffer[11]);
break;
}
if (!strnicmp(LineBuffer, "TOPCOLOR:", 9)) {
ae->TopColor = (short)atoi(&LineBuffer[9]);
break;
}
if (!strnicmp(LineBuffer, "BOTTOMCOLOR:", 12)) {
ae->BottomColor = (short)atoi(&LineBuffer[12]);
break;
}
if (!strnicmp(LineBuffer, "SHADING:", 8)) {
strupr(LineBuffer);
if (strstr(&LineBuffer[8], "OFF") != NULL)
ae->LightFlag = SHADING_OFF;
else
ae->LightFlag = SHADING_ON;
break;
}
if (!strnicmp(LineBuffer, "FLOORS:", 7)) {
strupr(LineBuffer);
if (strstr(&LineBuffer[7], "OFF") != NULL)
ae->SysFlags |= SYS_SOLID_FLOOR;
else
ae->SysFlags &= ~SYS_SOLID_FLOOR;
break;
}
if (!strnicmp(LineBuffer, "CEILING:", 8)) {
strupr(LineBuffer);
if (strstr(&LineBuffer[8], "OFF") != NULL)
ae->SysFlags |= SYS_SOLID_CEIL;
else
ae->SysFlags &= ~SYS_SOLID_CEIL;
break;
}
if (!strnicmp(LineBuffer, "SINGLEBITMAP:", 13)) {
strupr(LineBuffer);
if (strstr(&LineBuffer[13], "ON") != NULL)
ae->SysFlags |= SYS_SINGLE_BMP;
else
ae->SysFlags &= ~SYS_SINGLE_BMP;
break;
}
if (!strnicmp(LineBuffer, "CEILBITMAP:", 11)) {
ae->CeilBitmap = (short)atoi(&LineBuffer[11]);
break;
}
if (!strnicmp(LineBuffer, "FLOORBITMAP:", 12)) {
ae->FloorBitmap = (short)atoi(&LineBuffer[12]);
break;
}
if (!strnicmp(LineBuffer, "RESOLUTION:", 11)) {
Resolution = (short)atoi(&LineBuffer[11]);
break;
}
if (!strnicmp(LineBuffer, "LOADTYPE:", 9)) {
ae->bmLoadType = (short)atoi(&LineBuffer[9]); // Sets for GIF or BBM
break;
}
break;
}
fflush(stdout);
}
printf("\n");
return result;
}

227
example/example.c Normal file
View file

@ -0,0 +1,227 @@
/*
* Example ACK-3D Application
* --------------------------
*
* This is just a bare-bones simple "walkaround" demo application that lets
* you walk through a map.
*
* The ACK-3D demo programs, FDEMO, MALL, and also the Windows demo game
* "Station Escape", all include a fair bit more complexity and a bunch of
* hard-coded magic numbers, and other messyness.
*
* This example program is intended to be _super_ simple to get into.
* Unfortunately, ACK-3D requires a non-trivial amount of game resource file
* loading to be performed to get even a simple level up and running, but
* those details are mostly re-usable and is kept in a separate source file.
* This source file just contains the basics to initialize the engine and
* move a player around a game world.
*/
#include <stdio.h>
#include <conio.h>
#include <malloc.h>
#include <string.h>
#include "ack3d.h"
#include "ackeng.h"
#include "ackext.h"
#define KEY_RIGHT 77
#define KEY_UP 72
#define KEY_LEFT 75
#define KEY_DOWN 80
#define KEY_SPACE 57
#define KEY_ESCAPE 1
#define TURN_SPEED 5
#define TURN_SPEED_DECAY 3
#define MOVE_SPEED 5
#define MOVE_SPEED_DECAY 3
#define BASE_MOVE_AMOUNT 8
#define BASE_TURN_AMOUNT INT_ANGLE_4
// ACK3D bitmap loading routines, such as AckReadiff(), will automatically
// populate (and re-populate) this array with palette found in that image
// file. still, this must be manually set with AckSetPalette()
extern unsigned char colordat[];
int ProcessInfoFile(void);
// this is the main ACK3D engine structure. holds the map, bitmaps, objects,
// and also the current state of the game world (as far as ACK3D is concerned)
ACKENG *ae;
// this game loop made semi-overcomplicated by rudimentary turn/movement
// acceleration logic
void GameLoop(void) {
int newAngle;
int moveSpeed = 0;
int moveAmount = 0;
int turnSpeed = 0;
int turnAmount = 0;
AckBuildView();
AckDisplayScreen();
while (1) {
moveAmount = BASE_MOVE_AMOUNT + moveSpeed;
turnAmount = BASE_TURN_AMOUNT + turnSpeed;
if (AckKeys[KEY_ESCAPE]) {
break;
}
if (AckKeys[KEY_LEFT]) {
turnSpeed += TURN_SPEED;
ae->PlayerAngle -= turnAmount;
if (ae->PlayerAngle < 0)
ae->PlayerAngle += INT_ANGLE_360;
}
if (AckKeys[KEY_RIGHT]) {
turnSpeed += TURN_SPEED;
ae->PlayerAngle += turnAmount;
if (ae->PlayerAngle >= INT_ANGLE_360)
ae->PlayerAngle -= INT_ANGLE_360;
}
if (AckKeys[KEY_UP]) {
moveSpeed += MOVE_SPEED;
// this function handles collision detection for you reasonably
// well actually. pretty handy
AckMovePOV(ae->PlayerAngle, moveAmount);
}
if (AckKeys[KEY_DOWN]) {
moveSpeed += MOVE_SPEED;
newAngle = ae->PlayerAngle + INT_ANGLE_180;
if (newAngle >= INT_ANGLE_360)
newAngle -= INT_ANGLE_360;
AckMovePOV(newAngle, moveAmount);
}
if (AckKeys[KEY_SPACE]) {
AckCheckDoorOpen(ae->xPlayer, ae->yPlayer, ae->PlayerAngle);
}
// updates object animation and various object state things
AckCheckObjectMovement();
// renders the ack3d world to an offscreen buffer. also updates some
// ack3d state (such as door opening/closing)
AckBuildView();
// copies offscreen buffer to vga framebuffer
AckDisplayScreen();
moveSpeed -= MOVE_SPEED_DECAY;
if (moveSpeed < 0)
moveSpeed = 0;
if (moveSpeed > 16)
moveSpeed = 16;
turnSpeed -= TURN_SPEED_DECAY;
if (turnSpeed < 0)
turnSpeed = 0;
if (turnSpeed > 16)
turnSpeed = 16;
}
}
int main(int argc, char *argv[]) {
int result;
printf("Example ACK-3D application\n");
ae = (ACKENG*)malloc(sizeof(ACKENG));
memset(ae, 0, sizeof(ACKENG));
// ack3d renderer viewport size. on 486 and lower-spec machines, you
// probably do not want this to be at full 320x200 for performance reasons
// even reducing 320x200 by 20% makes a nice, noticeable difference!
ae->WinStartX = 0;
ae->WinStartY = 0;
ae->WinEndX = 319;
ae->WinEndY = 199;
// various other flags that can be set too
ae->LightFlag = SHADING_ON;
ae->DoorSpeed = 3;
printf("Initializing ACK-3D\n");
// main ack3d initialization. AckInitialize builds up a bunch of lookup
// tables and other global engine things like that. kit.ovl contains a
// bunch of pre-calculate trig lookup tables. if it was not opened before
// AckInitialize is called, then AckInitialize tries to find a file called
// trig.dat in the current directory.
result = AckOpenResource("kit.ovl");
if (result) {
printf("Error opening kit.ovl\n");
return 1;
}
result = AckInitialize(ae);
if (result) {
printf("AckInitialize failed\n");
return 1;
}
AckCloseResource();
// the big whammy. pics.dtf has basically all your game assets and takes
// the longest. ProcessInfoFile walks through a ".inf" file that should
// have been packed in pics.dtf at the first slot. that file (which is
// just a text file) has directives to load everything else (map, bitmaps,
// object definitions, etc)
printf("Processing pics.dtf\n");
result = AckOpenResource("pics.dtf");
if (result) {
printf("Error opening pics.dtf\n");
return 1;
}
result = ProcessInfoFile();
if (result) {
printf("Error while processing. Result code %d\n", result);
return 1;
}
printf("Processing scrolling backdrop\n");
// processes any (optional) scrolling backdrop that was loaded so it is
// ready to be rendered
LoadBackDrop();
AckCloseResource();
printf("Finished initializing\n");
// the main ack3d rendering functions all assume that the various fields
// in ACKENG have been copied into a whole bunch of global variables. this
// architecture was used for performance reasons.
// ACKENG pointer fields are simply copied, so modifying the actual data
// should be fine during runtime (if you were so inclined) without needing
// to call this function again.
// other flags/number fields from ACKENG are copied, so if you needed to
// change this type of thing during runtime, you would need to call this
// function again for the change to take effect!
AckRegisterStructure(ae);
AckSetupKeyboard();
AckSetVGAmode();
AckSetPalette(colordat);
GameLoop();
AckWrapUp(ae);
AckSetTextMode();
if (kbhit())
getch();
return 0;
}

BIN
example/kit.ovl Normal file

Binary file not shown.

58
example/makefile Normal file
View file

@ -0,0 +1,58 @@
target_config = debug
target_name = example
acklib_incdir = ..\ack_lib
acklib_lib = ..\ack_lib\acklib.lib
object_files = &
example.obj &
ackinfo.obj
cc_flags_debug = /d2 /zp1 /4r /fp3 /j
cc_flags_release = /d1+ /zp1 /4r /fp3 /onetx /j
cc_flags = /mf /i=$(acklib_incdir) $(cc_flags_$(target_config))
link_flags_debug = debug all
link_flags_release = debug all
link_flags = $(link_flags_$(target_config))
asm_flags_debug = /zi
asm_flags_release = /zi
asm_flags = /m /ml $(asm_flags_$(target_config))
.NOCHECK
build : $(target_name).exe
.c.obj: .AUTODEPEND
wcc386 $[. /zq $(cc_flags)
.asm.obj: .AUTODEPEND
tasm $[. /t $(asm_flags)
$(target_name).lnk: $(object_files)
%create $^@
%append $^@ NAME $(target_name).exe
%append $^@ SYSTEM DOS4G
%append $^@ OPTION QUIET
%append $^@ OPTION STACK=16k
%append $^@ LIBRARY $(acklib_lib)
@for %i in ($(object_files)) do %append $^@ FILE %i
$(target_name).exe: $(object_files) $(target_name).lnk
wlink $(link_flags) @$(target_name).lnk
clean : .SYMBOLIC
del *.obj
del *.err
del $(target_name).exe
del $(target_name).lnk
.NOCHECK
run : $(target_name).exe
$(target_name).exe
.NOCHECK
debug : $(target_name).exe
wd /swap /trap=rsi $(target_name).exe