_C PROGRAMMING COLUMN_ by Al Stevens Listing One #include #include #include "pcx.h" #include "utils.h" static void create_gfxlib(int argc, char *argv[]); static PCXBitmap* bitmaps[bitmapcount]; static SHORT count; int main(int argc, char *argv[]) { if (argc<3) { cout << "\nUSAGE : GFXMAKE @\n" " Builds graphics library file \n" " from the files listed on the command line or in\n" " . GFXMAKE constructs the GFX library\n" " with the entries in the order in which they appear\n" " on the command file and/or in .\n"; return -1; } create_gfxlib(argc, argv); return 0; } static void bld_gfxlib(const char *file); static void create_gfxlib(int argc, char *argv[]) { ofstream ofile(argv[1],ios::binary); if (ofile.fail()) { cout << "Cannot open " << argv[1] << endl; return; } count = 0; parse_cmdline(argc-2, argv+2, bld_gfxlib); // ----- write the signature ofile.write("GFX", 3); // --- record the common palette ofile.write(bitmaps[0]->GetPalette(), palettelength); // --- record number of bitmaps in GFX ofile.write((char*)&count,sizeof(count)); // --- record the bitmaps for (SHORT i = 0; i < count; i++) { Bitmap& bm = *bitmaps[i]; WORD height = bm.Height(); WORD width = bm.Width(); const char* buf = bm.GetPixelmap(); const char* name = bm.Name(); ofile.write(name, namelength); ofile.write((char*)&height, sizeof(height)); ofile.write((char*)&width, sizeof(width)); ofile.write(buf, height * width); delete bitmaps[i]; } } static void bld_gfxlib(const char *file) { if (count < bitmapcount) bitmaps[count++] = new PCXBitmap(file); } Listing Two // ------------ cmdline.cpp // Parse command lines for file names (no wildcards allowed // in order to preserve file sequence control) // Recognize a response file by @filename // Call a callback function for each name parsed #include #include #include #include #include #include int checkfile(const char *fname) { if (access(fname, 0) != 0) { cerr << "\nNo such file as " << fname; return 0; } return 1; } int process_responsefile(const char *rfname, void (*func)(const char*)) { if (!checkfile(rfname)) return 0; ifstream list(rfname); char fname[MAXPATH]; while (!list.eof()) { list.getline(fname,MAXPATH); if (*fname) { if (!checkfile(fname)) return 0; (*func)(fname); } } return 1; } int parse_cmdline(int argc, char *argv[], void (*func)(const char*)) { int n = 0; while (argc--) { if (*argv[n] == '@') { if (!process_responsefile(argv[n]+1, func)) return 0; } else { char path[MAXPATH]; char drive[MAXDRIVE]; char dir[MAXDIR]; _splitpath(argv[n], drive, dir, 0, 0); _makepath(path, drive, dir, 0, 0); char *cp = path+strlen(path); ffblk ff; int ax = findfirst(argv[n], &ff, 0); if (ax == -1) return 0; do { strcpy(cp, ff.ff_name); (*func)(path); ax = findnext(&ff); } while (ax != -1); } n++; } return 1; } Listing Three // -------- bldmaze.cpp #include int main(int argc, char* argv[]) { if (argc > 2) { ifstream ifile(argv[1]); if (ifile.fail()) return 1; ofstream ofile(argv[2], ios::binary); if (ofile.fail()) return 1; unsigned char ch, holdch; ifile.get(holdch); while (!ifile.eof()) { char rlectr = 0; for (;;) { ifile.get(ch); if (ch != holdch) break; rlectr++; } if (!ifile.eof()) { if (rlectr) ofile.put((char)(rlectr | 0x80)); ofile.put(holdch); } holdch = ch; } } return 0; } Listing Four // ---------- main.cpp #include #include #include #include #include #include "raycast.h" #include "keyboard.h" #include "sprite.h" const int stepincrement = 4; // maze coordinates per step // ------ sprite class class TubaPlayer : public Sprite { SHORT xincr; SHORT yincr; SHORT stepctr; public: TubaPlayer(SHORT tx, SHORT ty); void StepForward(); void AboutFace(); SHORT CurrXIncrement() const { return xincr; } }; TubaPlayer::TubaPlayer(SHORT tx, SHORT ty) : Sprite('Z', "sprites.gfx") { SetPosition(tx, ty); SetPose(Sprite::walking); xincr = stepincrement; yincr = 0; stepctr = 0; } inline void TubaPlayer::StepForward() { if (stepctr & 1) Step(); stepctr++; } inline void TubaPlayer::AboutFace() { RotateRight(); RotateRight(); RotateRight(); RotateRight(); xincr = -xincr; } // ----------- game class class TubasOfTerror : public RayCaster { TubaPlayer* tp; SHORT spriteno; SHORT stepctr; public: TubasOfTerror(SHORT px, SHORT py, SHORT pangle, ViewPort vp); ~TubasOfTerror(); void StepSpriteForward(); void SpriteAboutFace() { tp->AboutFace(); } }; // ----- initial tuba player sprite position within maze static SHORT tx1 = 14*64, ty1 = 28*64-16; TubasOfTerror::TubasOfTerror(SHORT px, SHORT py, SHORT pangle, ViewPort vp) : RayCaster("maze.dat", "tiles.gfx", px, py, pangle, vp) { if (!isLoaded()) throw("TILES.GFX load failure"); tp = new TubaPlayer(tx1, ty1); if (!tp->isLoaded()) { VGA::ResetVideo(); throw("SPRITES.GFX load failure"); } spriteno = AddSprite(tp); stepctr = 0; } TubasOfTerror::~TubasOfTerror() { delete tp; VGA::ResetVideo(); } // ----- walk the sprite through its path void TubasOfTerror::StepSpriteForward() { if (stepctr == 20 * (tilewidth / stepincrement)) tp->AboutFace(); if (stepctr == 40 * (tilewidth / stepincrement)) { tp->AboutFace(); stepctr = 0; } tp->StepForward(); MoveSpriteRelative(spriteno, tp->CurrXIncrement(), 0); stepctr++; } // ---- view ports: changed by pressing + and - static ViewPort vps[] = { // x y ht wd (position and size) // --- -- --- --- { 120, 75, 80, 50 }, { 110, 69, 100, 62 }, { 100, 62, 120, 74 }, { 90, 57, 140, 86 }, { 80, 50, 160, 100 }, { 70, 52, 180, 112 }, { 60, 45, 200, 124 }, { 50, 37, 220, 136 }, { 40, 30, 240, 150 }, { 30, 23, 260, 164 }, { 20, 15, 280, 174 }, { 40, 30, 240, 120 }, // typical { 0, 0, 320, 200 }, // full screen }; const int nbrvps = sizeof vps / sizeof(ViewPort); int vpctr = nbrvps - 2; // viewport subscript int main() { TubasOfTerror* ttp = 0; // ----- player's starting position and view angle SHORT x = 2163; SHORT y = 1730; SHORT angle = 180; // ---- for computing frames/per/second long framect = 0; clock_t start = clock(); // ---- error message to catch char* errcatch = 0; try { // ----- ray caster object ttp = new TubasOfTerror(x, y, angle, vps[vpctr]); // ---- keyboard object Keyboard kb; while (!kb.wasPressed(esckey)) { // ----- draw a frame ttp->DrawFrame(); framect++; // ----- test for player movement commands if (kb.isKeyDown(uparrow)) ttp->MoveForward(); if (kb.isKeyDown(dnarrow)) ttp->MoveBackward(); if (kb.isKeyDown(rtarrow)) { if (kb.isKeyDown(altkey)) ttp->MoveRightward(); else ttp->RotateRight(); } if (kb.isKeyDown(lfarrow)) { if (kb.isKeyDown(altkey)) ttp->MoveLeftward(); else ttp->RotateLeft(); } // -------- open and close door commands if (kb.wasPressed(' ')) ttp->OpenCloseDoor(); // ----- command to turn the map on and off if (kb.wasPressed(inskey)) ttp->ToggleMap(); // ----- commands to change player movement speed if (kb.wasPressed('f')) ttp->Faster(); if (kb.wasPressed('s')) ttp->Slower(); // ----- commands to change the size of the viewport if (kb.wasPressed(pluskey)) { if (vpctr < nbrvps-1) { ttp->GetPosition(x, y, angle); delete ttp; ttp = new TubasOfTerror(x, y, angle, vps[++vpctr]); } } if (kb.wasPressed(minuskey)) { if (vpctr > 0) { ttp->GetPosition(x, y, angle); delete ttp; ttp = new TubasOfTerror(x, y, angle, vps[--vpctr]); } } // ----- walk the sprite ttp->StepSpriteForward(); } } catch (char* errmsg) { errcatch = errmsg; } catch (xalloc xa) { static char msg[50]; sprintf(msg, "Out of memory (%d)", xa.requested()); errcatch = msg; } // ---- get report player's final position if (ttp) ttp->GetPosition(x, y, angle); // --------- get current time to compute frame rate clock_t stop = clock(); delete ttp; if (errcatch) cerr << "\aRuntime error: " << errcatch << endl; else { cout << "-------------------------------" << endl; cout << "Frames/sec: " << (int) ((CLK_TCK*framect)/(stop-start)) << endl; cout << "-------------------------------" << endl; cout << "Position (x, y, angle) :" << x << ' ' << y << ' ' << angle << endl; } return 0; }