/* Hovertank 3-D Source Code * Copyright (C) 1993-2014 Flat Rock Software * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define CATALOG #include "HOVERDEF.H" #pragma hdrstop /* NOTICE TO ANYONE READING THIS: This is the last gasp of our old routines! Everything is being rewritten from scratch to work with new graphic modes and utilities. This code stinks! */ /* ============================================================================= GLOBALS ============================================================================= */ int SNDstarted,KBDstarted; // whether int handlers were started int grhandle,levelhandle,soundhandle; typedef struct { int headersize; long dictionary; long dataoffsets; } grheadtype; grheadtype *grhead; // sets grhuffman and grstarts from here huffnode *grhuffman; // huffman dictionary for egagraph long *grstarts; // array of offsets in egagraph, -1 for sparse int grhandle; // handle to egagraph, kept open allways long grposition; // current seek position in file long chunkcomplen; // compressed length of a chunk int soundblaster; // present? int levelhandle,soundhandle; #define BUFFERSIZE 1024 memptr bufferseg; // small general purpose memory block memptr levelseg; int tedlevel,ingame,resetgame; memptr scalesegs[NUMPICS]; /* ============================================================================= */ /* ============= = = DoHelpText = ============= */ void DoHelpText(void) { CenterWindow (38,14); fontcolor = 13; CPPrint ("HoverTank Commands\n"); fontcolor = 15; py+=6; PPrint ( "F2 : Sound on / off\n" "F3 : Keyboard mode / custom\n" "F4 : Joystick mode\n" "F5 : Reset game\n" "ESC : Quit\n"); py+=6; PPrint ( "UP / DOWN : forward/reverse\n" "LEFT / RIGHT : turn\n"); py+=6; CPPrint (""); Ack(); EraseWindow (); CPPrint ("Button 1 / Ctrl\n"); CPPrint ("---------------\n\n"); PPrint ( /*.....................................*/ "Charge cannon. A fully charged cannon\n" "can shoot through multiple targets.\n" "While the cannon is charging, your\n" "tanks turning speed is halved for fine\n" "aiming adjustments.\n\n"); CPPrint (""); Ack(); EraseWindow (); CPPrint ("Button 2 / Alt\n"); CPPrint ("---------------\n\n"); PPrint ( /*.....................................*/ "Afterburner thrusting. Doubles your\n" "forward or backwards speed, but does\n" "not effect your turning speed.\n"); Ack(); } /* ================== = = DebugMemory = ================== */ void DebugMemory (void) { CenterWindow (16,7); CPPrint ("Memory Usage\n"); CPPrint ("------------"); PPrint ("\nTotal :"); PPrintUnsigned (totalmem/64); PPrint ("k\nFree :"); PPrintUnsigned (MMUnusedMemory()/64); PPrint ("k\nWith purge:"); PPrintUnsigned (MMTotalFree()/64); PPrint ("k\n"); CPPrint (""); PGet(); } /* ================ = = DebugKeys = ================ */ void DebugKeys (void) { int i; if (keydown[0x22]) // G = god mode { ExpWin (12,1); if (godmode) CPPrint ("God mode off"); else CPPrint ("God mode on"); Ack(); godmode ^= 1; } else if (keydown[0x32]) // M = memory info { DebugMemory(); } if (keydown[0x19]) // P = pause with no screen disruptioon { singlestep=1; } if (keydown[0x1f]) // S = shield point { screenofs = 0; HealPlayer(); } else if (keydown[0x14]) // T = free time { if (timestruct.min<9) timestruct.min++; screenofs = 0; DrawPic (6,48,DIGIT0PIC+timestruct.min); } else if (keydown[0x11]) // W = warp to level { ExpWin(26,1); PPrint("Warp to which level(1-20):"); i = InputInt(); if (i>=1 && i<=21) { level = i-1; leveldone=1; } } } /*=========================================================================*/ /* ============= = = CheckKeys = = Checks to see if an F-key is being pressed and handles it = ============= */ int CheckKeys(void) { if (!NBKscan) return 0; switch (NBKscan&0x7f) { case 0x3b: // F1 = help ClearKeys (); DoHelpText (); break; case 0x3c: // F2 = sound on/off ClearKeys (); ExpWin (13,1); PPrint ("Sound (Y/N)?"); ch=toupper(PGet()); if (ch=='N') soundmode = false; else if (ch=='Y') soundmode = true; break; case 0x3d: // F3 = keyboard mode ClearKeys (); calibratekeys (); break; case 0x3e: // F4 = joystick mode ClearKeys (); CalibrateJoy (1); break; case 0x3f: // F5 = reset game ClearKeys (); ExpWin (18,1); PPrint ("RESET GAME (Y/N)?"); ch=toupper(PGet()); if (ch=='Y') { resetgame = 1; leveldone = -99; } break; case 0x58: // F12 + ? = debug keys DebugKeys(); break; case 1: // ESC = quit ClearKeys (); ExpWin (12,1); PPrint ("QUIT (Y/N)?"); ch=toupper(PGet()); if (ch=='Y') Quit (""); break; default: return 0; } ClearKeys (); return 1; } //========================================================================== /* ============================ = = GetChunkLength = = Seeks into the igrab data file at the start of the given chunk and = reads the uncompressed length (first four bytes). The file pointer is = positioned so the compressed data can be read in next. = ChunkCompLen is set to the calculated compressed length = ============================ */ long GetChunkLength (int chunk) { long len; lseek(grhandle,grstarts[chunk],SEEK_SET); read(grhandle,&len,sizeof(len)); chunkcomplen = grstarts[chunk+1]-grstarts[chunk]-4; return len; } //========================================================================== /* ============================ = = LoadNearData = = Load stuff into data segment before memory manager is = started (which takes all available memory, near and far) = ============================ */ void LoadNearData (void) { int handle; long length; // // load egahead.ext (offsets and dictionary for graphics file) // if ((handle = open("EGAHEAD."EXTENSION, O_RDONLY | O_BINARY, S_IWRITE | S_IREAD)) == -1) Quit ("Can't open EGAHEAD."EXTENSION"!"); length = filelength(handle); grhead = malloc(length); read(handle, grhead, length); close(handle); } //========================================================================== /* ========================== = = SegRead = = Read from a file to a segment pointer = ========================== */ void SegRead (int handle, memptr dest, long length) { if (length>0xffffl) Quit ("SegRead doesn't support 64K reads yet!"); asm push ds asm mov bx,[handle] asm mov cx,[WORD PTR length] asm mov dx,0 // segment aligned asm mov ds,[dest] asm mov ah,3fh // READ w/handle asm int 21h asm pop ds } //========================================================================== ///////////////////////////////////////////////////////// // // InitGrFile // ///////////////////////////////////////////////////////// void InitGrFile (void) { memptr buffer; // // calculate some offsets in the header // grhuffman = (huffnode *)( ((char *)grhead)+grhead->dictionary); grstarts = (long *)( ((char *)grhead)+grhead->dataoffsets); OptimizeNodes (grhuffman); // // Open the graphics file, leaving it open until the game is finished // grhandle = open("EGAGRAPH."EXTENSION, O_RDONLY | O_BINARY); if (grhandle == -1) Quit ("Cannot open EGAGRAPH."EXTENSION"!"); // // load the pic and sprite headers into the data segment // #if NUMPICS>0 needgr[STRUCTPIC] = 1; // make sure this chunk never reloads grsegs[STRUCTPIC] = (memptr)0xffff; GetChunkLength(STRUCTPIC); // position file pointer MMGetPtr(&buffer, chunkcomplen); SegRead (grhandle,buffer,chunkcomplen); HuffExpand ((unsigned char huge *)buffer, (unsigned char huge *)pictable, sizeof(pictable),grhuffman); MMFreePtr(&buffer); #endif #if NUMPICM>0 needgr[STRUCTPICM] = 1; // make sure this chunk never reloads grsegs[STRUCTPICM] = (memptr)0xffff; GetChunkLength(STRUCTPICM); // position file pointer MMGetPtr(&buffer, chunkcomplen); SegRead (grhandle,buffer,chunkcomplen); HuffExpand (buffer, (unsigned char huge *)picmtable, sizeof(picmtable),grhuffman); MMFreePtr(&buffer); #endif #if NUMSPRITES>0 needgr[STRUCTSPRITE] = 1; // make sure this chunk never reloads grsegs[STRUCTSPRITE] = (memptr)0xffff; GetChunkLength(STRUCTSPRITE); // position file pointer MMGetPtr(&buffer, chunkcomplen); SegRead (grhandle,buffer,chunkcomplen); HuffExpand (buffer, (unsigned char huge *)spritetable, sizeof(spritetable),grhuffman); MMFreePtr(&buffer); #endif } //========================================================================== /* ========================== = = CacheGrFile = = Goes through grneeded and grsegs, and makes sure = everything needed is in memory = ========================== */ // base tile sizes for EGA mode #define BLOCK 32 #define MASKBLOCK 40 void CacheGrFile (void) { int i; long filepos,newpos; // current seek position in file long expanded,compressed; // chunk lengths memptr bigbufferseg; // for compressed // // make unneeded chunks purgable // for (i=0;i=STARTTILE8) { // // tiles are of a known size // if (i=STARTTILE8) { // // tiles are of a known size // if (picnum0) close(grhandle); if (levelhandle>0) close(levelhandle); if (soundhandle>0) close(soundhandle); _AX = 3; geninterrupt (0x10); // text mode if (!(*error)) { #if 0 movedata (FP_SEG(&PIRACY),FP_OFF(&PIRACY),0xb800,0,4000); bioskey (0); clrscr(); #endif #ifndef CATALOG _argc = 2; _argv[1] = "LAST.SHL"; _argv[2] = "ENDSCN.SCN"; _argv[3] = NULL; if (execv("LOADSCN.EXE", _argv) == -1) { clrscr(); puts("Couldn't find executable LOADSCN.EXE.\n"); exit(1); } #endif } else puts (error); exit (0); // quit to DOS } //========================================================================== /* ====================== = = LoadLevel = = Loads LEVEL00.EXT (00 = global variable level) = ====================== */ void LoadLevel(void) { unsigned far *planeptr; int loop,x,y,i,j; unsigned length; char filename[30]; char num[3]; memptr bufferseg; // // load the new level in and decompress // if (level<10) { itoa (level,num,10); strcpy (filename,"LEVEL0"); } else { itoa (level,num,10); strcpy (filename,"LEVEL"); } strcat (filename,num); strcat (filename,"."EXTENSION); BloadinMM(filename,&bufferseg); length = *(unsigned _seg *)bufferseg; if (levelseg) MMFreePtr (&levelseg); MMGetPtr (&levelseg,length); RLEWExpand ((unsigned far *)bufferseg,(unsigned far *)levelseg); MMFreePtr (&bufferseg); levelheader = (LevelDef far *)levelseg; // // copy plane 0 to tilemap // planeptr= (unsigned far *)((char _seg *)levelseg+32); for (y=0;yheight;y++) for (x=0;xwidth;x++) tilemap[x][y]=*planeptr++; // // spawn tanks // planeptr= (unsigned far *)((char _seg *)levelseg+32+levelheader->planesize); StartLevel (planeptr); MMFreePtr (&levelseg); } //========================================================================== /* ================= = = CacheDrawPic = ================= */ void CacheDrawPic(int picnum) { int i; CachePic (STARTPICS+picnum); EGASplitScreen(200); SetScreen(0,0); SetLineWidth(80); screenofs = 0; EGAWRITEMODE(0); DrawPic (0,0,picnum); EGAWRITEMODE(1); EGAMAPMASK(15); CopyEGA(80,200,0,0x4000); EGAWRITEMODE(0); MMSetPurge (&grsegs[STARTPICS+picnum],3); } //========================================================================== int SoundPlaying (void) { if (soundblaster) return jmSamplePlaying(); else return sndptr; } #if 0 /* ===================== = = PlaySound = = Dispatches to either pc speaker sound routines or sound blaster = digitized routines = ===================== */ void PlaySound (int num) { if (soundblaster) jmPlaySample(num); else PlaySoundSPK(num); } #endif //========================================================================== /* ===================== = = Intro = ===================== */ void Intro (void) { memptr shapeseg; int i,f,sx,sy,page; unsigned pageptr[2],pagewidth[2],pageheight[2]; float x,y,z,angle,step,sn,cs,maxz,sizescale,maxy,coordscale,scale; float ytop,xmid,minz,worldycenter,worldxcenter; FadeOut(); SetLineWidth(SCREENWIDTH); screenofs=0; CacheDrawPic (STARSPIC); pxl=0; pxh=320; py=180; #ifndef CATALOG CPPrint ("Copyright (c) 1991-93 Softdisk Publishing\n"); // CPPrint ("'I' for information"); #endif EGAWRITEMODE(1); EGAMAPMASK(15); CopyEGA(40,200,0,0x4000); CopyEGA(40,200,0,0x8000); CopyEGA(40,200,0,0xc000); StopDrive(); CachePic (STARTPICS+LOGOPIC); SC_MakeShape( grsegs[STARTPICS+LOGOPIC], 0, 0, &shapeseg); // SC_MakeShape( // grsegs[STARTPICS+LOGOPIC], // pictable[LOGOPIC].width, // pictable[LOGOPIC].height, // &shapeseg); MMFreePtr(&grsegs[STARTPICS+LOGOPIC]); FadeIn(); sx=160; sy=180; // memset (zbuffer,0,sizeof(zbuffer)); /* ============================================================================= SCALED PICTURE DIRECTOR ============================================================================= */ #define PICHEIGHT 64 // full size height of scaled pic #define NUMFRAMES 300.0 #define MAXANGLE (3.141592657*0.6) // go from 0 to this in numframes #define RADIUS 1000.0 // world coordinates #define DISTANCE 1000.0 // center point z distance minz = cos(MAXANGLE)*RADIUS; // closest point minz += DISTANCE; sizescale = 256*minz; // closest point will be full size ytop = 80 - (PICHEIGHT/2)*(sizescale/DISTANCE)/256; z = sizescale/(DISTANCE*256); ytop = ytop/z; // world coordinates worldycenter=ytop-RADIUS; xmid=sin(MAXANGLE)*RADIUS/2; worldxcenter=-xmid; f=1; page = inttime = screenofs = pagewidth[0] = pagewidth[1] = 0; do { step = f/NUMFRAMES; angle=MAXANGLE*step; sn=sin(angle); cs=cos(angle); x=worldxcenter+sn*RADIUS/2; y=worldycenter+sn*RADIUS; z=DISTANCE+cs*RADIUS; scale = sizescale/z; sx=160+ (int)(x*scale/256); sy=100- (int)(y*scale/256); inttime=0; sound((int)(sn*1500)); // // erase old position // if (pagewidth[page]) { EGAWRITEMODE(1); EGAMAPMASK(15); CopyEGA(pagewidth[page],pageheight[page], pageptr[page]+0x8000,pageptr[page]); } // // draw new position // EGAWRITEMODE(2); if (SC_ScaleShape(sx,sy,(int)scale<40 ? 10 : scale/4,shapeseg)) { pagewidth[page]=scaleblockwidth; pageheight[page]=scaleblockheight; pageptr[page]=scaleblockdest; } else pagewidth[page]=0; EGAWRITEMODE(0); EGABITMASK(255); // // display it // SetScreen(screenofs,0); page^=1; screenofs = 0x4000*page; f++; if (fNUMFRAMES) f=NUMFRAMES; } else f++; // last frame is shown if (NBKscan>0x7f) break; } while (f<=NUMFRAMES); nosound(); for (i=0;i<200;i++) { WaitVBL(1); if (NBKscan>0x7f) { #if 0 if (NBKscan==0x97) //'I' for info { screenofs^=0x4000; CenterWindow(24,10); py+=2; CPPrint ("Hovertank v1.17\n\n"); CPPrint ("Softdisk Publishing delivers a\n"); CPPrint ("high quality EGA game to\n"); CPPrint ("your door every month!\n"); CPPrint ("Call 1-800-831-2694 for\n"); CPPrint ("subscription rates and\n"); CPPrint ("back issues.\n"); ClearKeys(); Ack(); } ClearKeys(); break; #endif } } MMFreePtr(&shapeseg); } //========================================================================== /* ===================== == == DemoLoop == ===================== */ #define PAUSE 300 void DemoLoop (void) { int i,originx; ControlStruct c; FadeOut(); CacheDrawPic (TITLEPIC); StopDrive(); // make floppy motors turn off FadeIn (); originx=0; i=100; while (1) { if (i>PAUSE && i<=PAUSE+80) originx+=4; if (i>PAUSE*2 && i<=PAUSE*2+80) originx-=4; if (i>PAUSE*2+80) i=0; SetScreen(originx/8,originx%8); i++; screenofs = originx/8; if (CheckKeys()) { EGAWRITEMODE(1); EGAMAPMASK(15); CopyEGA(80,200,0x4000,0); } c=ControlPlayer(1); if (c.button1 || c.button2) break; if (keydown[0x39]) break; } ClearKeys(); } //========================================================================== /* ==================== = = SetupGraphics = ==================== */ void SetupGraphics (void) { int i; InitGrFile (); // load the graphic file header // // go through the pics and make scalable shapes, the discard the pic // for (i=MAN1PIC;i