+// WL_DRAW.C\r
+\r
+#include "WL_DEF.H"\r
+#include <DOS.H>\r
+#pragma hdrstop\r
+\r
+//#define DEBUGWALLS\r
+//#define DEBUGTICS\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+// the door is the last picture before the sprites\r
+#define DOORWALL (PMSpriteStart-8)\r
+\r
+#define ACTORSIZE 0x4000\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+#ifdef DEBUGWALLS\r
+unsigned screenloc[3]= {0,0,0};\r
+#else\r
+unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START};\r
+#endif\r
+unsigned freelatch = FREESTART;\r
+\r
+long lasttimecount;\r
+long frameon;\r
+\r
+unsigned wallheight[MAXVIEWWIDTH];\r
+\r
+fixed tileglobal = TILEGLOBAL;\r
+fixed mindist = MINDIST;\r
+\r
+\r
+//\r
+// math tables\r
+//\r
+int pixelangle[MAXVIEWWIDTH];\r
+long far finetangent[FINEANGLES/4];\r
+fixed far sintable[ANGLES+ANGLES/4],far *costable = sintable+(ANGLES/4);\r
+\r
+//\r
+// refresh variables\r
+//\r
+fixed viewx,viewy; // the focal point\r
+int viewangle;\r
+fixed viewsin,viewcos;\r
+\r
+\r
+\r
+fixed FixedByFrac (fixed a, fixed b);\r
+void TransformActor (objtype *ob);\r
+void BuildTables (void);\r
+void ClearScreen (void);\r
+int CalcRotate (objtype *ob);\r
+void DrawScaleds (void);\r
+void CalcTics (void);\r
+void FixOfs (void);\r
+void ThreeDRefresh (void);\r
+\r
+\r
+\r
+//\r
+// wall optimization variables\r
+//\r
+int lastside; // true for vertical\r
+long lastintercept;\r
+int lasttilehit;\r
+\r
+\r
+//\r
+// ray tracing variables\r
+//\r
+int focaltx,focalty,viewtx,viewty;\r
+\r
+int midangle,angle;\r
+unsigned xpartial,ypartial;\r
+unsigned xpartialup,xpartialdown,ypartialup,ypartialdown;\r
+unsigned xinttile,yinttile;\r
+\r
+unsigned tilehit;\r
+unsigned pixx;\r
+\r
+int xtile,ytile;\r
+int xtilestep,ytilestep;\r
+long xintercept,yintercept;\r
+long xstep,ystep;\r
+\r
+int horizwall[MAXWALLTILES],vertwall[MAXWALLTILES];\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+void AsmRefresh (void); // in WL_DR_A.ASM\r
+\r
+/*\r
+============================================================================\r
+\r
+ 3 - D DEFINITIONS\r
+\r
+============================================================================\r
+*/\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= FixedByFrac\r
+=\r
+= multiply a 16/16 bit, 2's complement fixed point number by a 16 bit\r
+= fraction, passed as a signed magnitude 32 bit number\r
+=\r
+========================\r
+*/\r
+\r
+#pragma warn -rvl // I stick the return value in with ASMs\r
+\r
+fixed FixedByFrac (fixed a, fixed b)\r
+{\r
+//\r
+// setup\r
+//\r
+asm mov si,[WORD PTR b+2] // sign of result = sign of fraction\r
+\r
+asm mov ax,[WORD PTR a]\r
+asm mov cx,[WORD PTR a+2]\r
+\r
+asm or cx,cx\r
+asm jns aok: // negative?\r
+asm neg cx\r
+asm neg ax\r
+asm sbb cx,0\r
+asm xor si,0x8000 // toggle sign of result\r
+aok:\r
+\r
+//\r
+// multiply cx:ax by bx\r
+//\r
+asm mov bx,[WORD PTR b]\r
+asm mul bx // fraction*fraction\r
+asm mov di,dx // di is low word of result\r
+asm mov ax,cx //\r
+asm mul bx // units*fraction\r
+asm add ax,di\r
+asm adc dx,0\r
+\r
+//\r
+// put result dx:ax in 2's complement\r
+//\r
+asm test si,0x8000 // is the result negative?\r
+asm jz ansok:\r
+asm neg dx\r
+asm neg ax\r
+asm sbb dx,0\r
+\r
+ansok:;\r
+\r
+}\r
+\r
+#pragma warn +rvl\r
+\r
+//==========================================================================\r
+\r
+/*\r
+========================\r
+=\r
+= TransformActor\r
+=\r
+= Takes paramaters:\r
+= gx,gy : globalx/globaly of point\r
+=\r
+= globals:\r
+= viewx,viewy : point of view\r
+= viewcos,viewsin : sin/cos of viewangle\r
+= scale : conversion from global value to screen value\r
+=\r
+= sets:\r
+= screenx,transx,transy,screenheight: projected edge location and size\r
+=\r
+========================\r
+*/\r
+\r
+\r
+//\r
+// transform actor\r
+//\r
+void TransformActor (objtype *ob)\r
+{\r
+ int ratio;\r
+ fixed gx,gy,gxt,gyt,nx,ny;\r
+ long temp;\r
+\r
+//\r
+// translate point to view centered coordinates\r
+//\r
+ gx = ob->x-viewx;\r
+ gy = ob->y-viewy;\r
+\r
+//\r
+// calculate newx\r
+//\r
+ gxt = FixedByFrac(gx,viewcos);\r
+ gyt = FixedByFrac(gy,viewsin);\r
+ nx = gxt-gyt-ACTORSIZE; // fudge the shape forward a bit, because\r
+ // the midpoint could put parts of the shape\r
+ // into an adjacent wall\r
+\r
+//\r
+// calculate newy\r
+//\r
+ gxt = FixedByFrac(gx,viewsin);\r
+ gyt = FixedByFrac(gy,viewcos);\r
+ ny = gyt+gxt;\r
+\r
+//\r
+// calculate perspective ratio\r
+//\r
+ ob->transx = nx;\r
+ ob->transy = ny;\r
+\r
+ if (nx<mindist) // too close, don't overflow the divide\r
+ {\r
+ ob->viewheight = 0;\r
+ return;\r
+ }\r
+\r
+ ob->viewx = centerx + ny*scale/nx; // DEBUG: use assembly divide\r
+\r
+//\r
+// calculate height (heightnumerator/(nx>>8))\r
+//\r
+ asm mov ax,[WORD PTR heightnumerator]\r
+ asm mov dx,[WORD PTR heightnumerator+2]\r
+ asm idiv [WORD PTR nx+1] // nx>>8\r
+ asm mov [WORD PTR temp],ax\r
+ asm mov [WORD PTR temp+2],dx\r
+\r
+ ob->viewheight = temp;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+========================\r
+=\r
+= TransformTile\r
+=\r
+= Takes paramaters:\r
+= tx,ty : tile the object is centered in\r
+=\r
+= globals:\r
+= viewx,viewy : point of view\r
+= viewcos,viewsin : sin/cos of viewangle\r
+= scale : conversion from global value to screen value\r
+=\r
+= sets:\r
+= screenx,transx,transy,screenheight: projected edge location and size\r
+=\r
+= Returns true if the tile is withing getting distance\r
+=\r
+========================\r
+*/\r
+\r
+boolean TransformTile (int tx, int ty, int *dispx, int *dispheight)\r
+{\r
+ int ratio;\r
+ fixed gx,gy,gxt,gyt,nx,ny;\r
+ long temp;\r
+\r
+//\r
+// translate point to view centered coordinates\r
+//\r
+ gx = ((long)tx<<TILESHIFT)+0x8000-viewx;\r
+ gy = ((long)ty<<TILESHIFT)+0x8000-viewy;\r
+\r
+//\r
+// calculate newx\r
+//\r
+ gxt = FixedByFrac(gx,viewcos);\r
+ gyt = FixedByFrac(gy,viewsin);\r
+ nx = gxt-gyt-0x2000; // 0x2000 is size of object\r
+\r
+//\r
+// calculate newy\r
+//\r
+ gxt = FixedByFrac(gx,viewsin);\r
+ gyt = FixedByFrac(gy,viewcos);\r
+ ny = gyt+gxt;\r
+\r
+\r
+//\r
+// calculate perspective ratio\r
+//\r
+ if (nx<mindist) // too close, don't overflow the divide\r
+ {\r
+ *dispheight = 0;\r
+ return false;\r
+ }\r
+\r
+ *dispx = centerx + ny*scale/nx; // DEBUG: use assembly divide\r
+\r
+//\r
+// calculate height (heightnumerator/(nx>>8))\r
+//\r
+ asm mov ax,[WORD PTR heightnumerator]\r
+ asm mov dx,[WORD PTR heightnumerator+2]\r
+ asm idiv [WORD PTR nx+1] // nx>>8\r
+ asm mov [WORD PTR temp],ax\r
+ asm mov [WORD PTR temp+2],dx\r
+\r
+ *dispheight = temp;\r
+\r
+//\r
+// see if it should be grabbed\r
+//\r
+ if (nx<TILEGLOBAL && ny>-TILEGLOBAL/2 && ny<TILEGLOBAL/2)\r
+ return true;\r
+ else\r
+ return false;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= CalcHeight\r
+=\r
+= Calculates the height of xintercept,yintercept from viewx,viewy\r
+=\r
+====================\r
+*/\r
+\r
+#pragma warn -rvl // I stick the return value in with ASMs\r
+\r
+int CalcHeight (void)\r
+{\r
+ int transheight;\r
+ int ratio;\r
+ fixed gxt,gyt,nx,ny;\r
+ long gx,gy;\r
+\r
+ gx = xintercept-viewx;\r
+ gxt = FixedByFrac(gx,viewcos);\r
+\r
+ gy = yintercept-viewy;\r
+ gyt = FixedByFrac(gy,viewsin);\r
+\r
+ nx = gxt-gyt;\r
+\r
+ //\r
+ // calculate perspective ratio (heightnumerator/(nx>>8))\r
+ //\r
+ if (nx<mindist)\r
+ nx=mindist; // don't let divide overflow\r
+\r
+ asm mov ax,[WORD PTR heightnumerator]\r
+ asm mov dx,[WORD PTR heightnumerator+2]\r
+ asm idiv [WORD PTR nx+1] // nx>>8\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= ScalePost\r
+=\r
+===================\r
+*/\r
+\r
+long postsource;\r
+unsigned postx;\r
+unsigned postwidth;\r
+\r
+void near ScalePost (void) // VGA version\r
+{\r
+ asm mov ax,SCREENSEG\r
+ asm mov es,ax\r
+\r
+ asm mov bx,[postx]\r
+ asm shl bx,1\r
+ asm mov bp,WORD PTR [wallheight+bx] // fractional height (low 3 bits frac)\r
+ asm and bp,0xfff8 // bp = heightscaler*4\r
+ asm shr bp,1\r
+ asm cmp bp,[maxscaleshl2]\r
+ asm jle heightok\r
+ asm mov bp,[maxscaleshl2]\r
+heightok:\r
+ asm add bp,OFFSET fullscalefarcall\r
+ //\r
+ // scale a byte wide strip of wall\r
+ //\r
+ asm mov bx,[postx]\r
+ asm mov di,bx\r
+ asm shr di,1 // X in bytes\r
+ asm shr di,1\r
+ asm add di,[bufferofs]\r
+\r
+ asm and bx,3\r
+ /* begin 8086 hack\r
+ asm shl bx,3\r
+ */\r
+ asm push cx\r
+ asm mov cl,3\r
+ asm shl bx,cl\r
+ asm pop cx\r
+ /* end 8086 hack */\r
+ asm add bx,[postwidth]\r
+\r
+ asm mov al,BYTE PTR [mapmasks1-1+bx] // -1 because no widths of 0\r
+ asm mov dx,SC_INDEX+1\r
+ asm out dx,al // set bit mask register\r
+ asm lds si,DWORD PTR [postsource]\r
+ asm call DWORD PTR [bp] // scale the line of pixels\r
+\r
+ asm mov al,BYTE PTR [ss:mapmasks2-1+bx] // -1 because no widths of 0\r
+ asm or al,al\r
+ asm jz nomore\r
+\r
+ //\r
+ // draw a second byte for vertical strips that cross two bytes\r
+ //\r
+ asm inc di\r
+ asm out dx,al // set bit mask register\r
+ asm call DWORD PTR [bp] // scale the line of pixels\r
+\r
+ asm mov al,BYTE PTR [ss:mapmasks3-1+bx] // -1 because no widths of 0\r
+ asm or al,al\r
+ asm jz nomore\r
+ //\r
+ // draw a third byte for vertical strips that cross three bytes\r
+ //\r
+ asm inc di\r
+ asm out dx,al // set bit mask register\r
+ asm call DWORD PTR [bp] // scale the line of pixels\r
+\r
+\r
+nomore:\r
+ asm mov ax,ss\r
+ asm mov ds,ax\r
+}\r
+\r
+void FarScalePost (void) // just so other files can call\r
+{\r
+ ScalePost ();\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= HitVertWall\r
+=\r
+= tilehit bit 7 is 0, because it's not a door tile\r
+= if bit 6 is 1 and the adjacent tile is a door tile, use door side pic\r
+=\r
+====================\r
+*/\r
+\r
+void HitVertWall (void)\r
+{\r
+ int wallpic;\r
+ unsigned texture;\r
+\r
+ texture = (yintercept>>4)&0xfc0;\r
+ if (xtilestep == -1)\r
+ {\r
+ texture = 0xfc0-texture;\r
+ xintercept += TILEGLOBAL;\r
+ }\r
+ wallheight[pixx] = CalcHeight();\r
+\r
+ if (lastside==1 && lastintercept == xtile && lasttilehit == tilehit)\r
+ {\r
+ // in the same wall type as last time, so check for optimized draw\r
+ if (texture == (unsigned)postsource)\r
+ {\r
+ // wide scale\r
+ postwidth++;\r
+ wallheight[pixx] = wallheight[pixx-1];\r
+ return;\r
+ }\r
+ else\r
+ {\r
+ ScalePost ();\r
+ (unsigned)postsource = texture;\r
+ postwidth = 1;\r
+ postx = pixx;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // new wall\r
+ if (lastside != -1) // if not the first scaled post\r
+ ScalePost ();\r
+\r
+ lastside = true;\r
+ lastintercept = xtile;\r
+\r
+ lasttilehit = tilehit;\r
+ postx = pixx;\r
+ postwidth = 1;\r
+\r
+ if (tilehit & 0x40)\r
+ { // check for adjacent doors\r
+ ytile = yintercept>>TILESHIFT;\r
+ if ( tilemap[xtile-xtilestep][ytile]&0x80 )\r
+ wallpic = DOORWALL+3;\r
+ else\r
+ wallpic = vertwall[tilehit & ~0x40];\r
+ }\r
+ else\r
+ wallpic = vertwall[tilehit];\r
+\r
+ *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);\r
+ (unsigned)postsource = texture;\r
+\r
+ }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= HitHorizWall\r
+=\r
+= tilehit bit 7 is 0, because it's not a door tile\r
+= if bit 6 is 1 and the adjacent tile is a door tile, use door side pic\r
+=\r
+====================\r
+*/\r
+\r
+void HitHorizWall (void)\r
+{\r
+ int wallpic;\r
+ unsigned texture;\r
+\r
+ texture = (xintercept>>4)&0xfc0;\r
+ if (ytilestep == -1)\r
+ yintercept += TILEGLOBAL;\r
+ else\r
+ texture = 0xfc0-texture;\r
+ wallheight[pixx] = CalcHeight();\r
+\r
+ if (lastside==0 && lastintercept == ytile && lasttilehit == tilehit)\r
+ {\r
+ // in the same wall type as last time, so check for optimized draw\r
+ if (texture == (unsigned)postsource)\r
+ {\r
+ // wide scale\r
+ postwidth++;\r
+ wallheight[pixx] = wallheight[pixx-1];\r
+ return;\r
+ }\r
+ else\r
+ {\r
+ ScalePost ();\r
+ (unsigned)postsource = texture;\r
+ postwidth = 1;\r
+ postx = pixx;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // new wall\r
+ if (lastside != -1) // if not the first scaled post\r
+ ScalePost ();\r
+\r
+ lastside = 0;\r
+ lastintercept = ytile;\r
+\r
+ lasttilehit = tilehit;\r
+ postx = pixx;\r
+ postwidth = 1;\r
+\r
+ if (tilehit & 0x40)\r
+ { // check for adjacent doors\r
+ xtile = xintercept>>TILESHIFT;\r
+ if ( tilemap[xtile][ytile-ytilestep]&0x80 )\r
+ wallpic = DOORWALL+2;\r
+ else\r
+ wallpic = horizwall[tilehit & ~0x40];\r
+ }\r
+ else\r
+ wallpic = horizwall[tilehit];\r
+\r
+ *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);\r
+ (unsigned)postsource = texture;\r
+ }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= HitHorizDoor\r
+=\r
+====================\r
+*/\r
+\r
+void HitHorizDoor (void)\r
+{\r
+ unsigned texture,doorpage,doornum;\r
+\r
+ doornum = tilehit&0x7f;\r
+ texture = ( (xintercept-doorposition[doornum]) >> 4) &0xfc0;\r
+\r
+ wallheight[pixx] = CalcHeight();\r
+\r
+ if (lasttilehit == tilehit)\r
+ {\r
+ // in the same door as last time, so check for optimized draw\r
+ if (texture == (unsigned)postsource)\r
+ {\r
+ // wide scale\r
+ postwidth++;\r
+ wallheight[pixx] = wallheight[pixx-1];\r
+ return;\r
+ }\r
+ else\r
+ {\r
+ ScalePost ();\r
+ (unsigned)postsource = texture;\r
+ postwidth = 1;\r
+ postx = pixx;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (lastside != -1) // if not the first scaled post\r
+ ScalePost (); // draw last post\r
+ // first pixel in this door\r
+ lastside = 2;\r
+ lasttilehit = tilehit;\r
+ postx = pixx;\r
+ postwidth = 1;\r
+\r
+ switch (doorobjlist[doornum].lock)\r
+ {\r
+ case dr_normal:\r
+ doorpage = DOORWALL;\r
+ break;\r
+ case dr_lock1:\r
+ case dr_lock2:\r
+ case dr_lock3:\r
+ case dr_lock4:\r
+ doorpage = DOORWALL+6;\r
+ break;\r
+ case dr_elevator:\r
+ doorpage = DOORWALL+4;\r
+ break;\r
+ }\r
+\r
+ *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage);\r
+ (unsigned)postsource = texture;\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= HitVertDoor\r
+=\r
+====================\r
+*/\r
+\r
+void HitVertDoor (void)\r
+{\r
+ unsigned texture,doorpage,doornum;\r
+\r
+ doornum = tilehit&0x7f;\r
+ texture = ( (yintercept-doorposition[doornum]) >> 4) &0xfc0;\r
+\r
+ wallheight[pixx] = CalcHeight();\r
+\r
+ if (lasttilehit == tilehit)\r
+ {\r
+ // in the same door as last time, so check for optimized draw\r
+ if (texture == (unsigned)postsource)\r
+ {\r
+ // wide scale\r
+ postwidth++;\r
+ wallheight[pixx] = wallheight[pixx-1];\r
+ return;\r
+ }\r
+ else\r
+ {\r
+ ScalePost ();\r
+ (unsigned)postsource = texture;\r
+ postwidth = 1;\r
+ postx = pixx;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (lastside != -1) // if not the first scaled post\r
+ ScalePost (); // draw last post\r
+ // first pixel in this door\r
+ lastside = 2;\r
+ lasttilehit = tilehit;\r
+ postx = pixx;\r
+ postwidth = 1;\r
+\r
+ switch (doorobjlist[doornum].lock)\r
+ {\r
+ case dr_normal:\r
+ doorpage = DOORWALL;\r
+ break;\r
+ case dr_lock1:\r
+ case dr_lock2:\r
+ case dr_lock3:\r
+ case dr_lock4:\r
+ doorpage = DOORWALL+6;\r
+ break;\r
+ case dr_elevator:\r
+ doorpage = DOORWALL+4;\r
+ break;\r
+ }\r
+\r
+ *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage+1);\r
+ (unsigned)postsource = texture;\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= HitHorizPWall\r
+=\r
+= A pushable wall in action has been hit\r
+=\r
+====================\r
+*/\r
+\r
+void HitHorizPWall (void)\r
+{\r
+ int wallpic;\r
+ unsigned texture,offset;\r
+\r
+ texture = (xintercept>>4)&0xfc0;\r
+ offset = pwallpos<<10;\r
+ if (ytilestep == -1)\r
+ yintercept += TILEGLOBAL-offset;\r
+ else\r
+ {\r
+ texture = 0xfc0-texture;\r
+ yintercept += offset;\r
+ }\r
+\r
+ wallheight[pixx] = CalcHeight();\r
+\r
+ if (lasttilehit == tilehit)\r
+ {\r
+ // in the same wall type as last time, so check for optimized draw\r
+ if (texture == (unsigned)postsource)\r
+ {\r
+ // wide scale\r
+ postwidth++;\r
+ wallheight[pixx] = wallheight[pixx-1];\r
+ return;\r
+ }\r
+ else\r
+ {\r
+ ScalePost ();\r
+ (unsigned)postsource = texture;\r
+ postwidth = 1;\r
+ postx = pixx;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // new wall\r
+ if (lastside != -1) // if not the first scaled post\r
+ ScalePost ();\r
+\r
+ lasttilehit = tilehit;\r
+ postx = pixx;\r
+ postwidth = 1;\r
+\r
+ wallpic = horizwall[tilehit&63];\r
+\r
+ *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);\r
+ (unsigned)postsource = texture;\r
+ }\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= HitVertPWall\r
+=\r
+= A pushable wall in action has been hit\r
+=\r
+====================\r
+*/\r
+\r
+void HitVertPWall (void)\r
+{\r
+ int wallpic;\r
+ unsigned texture,offset;\r
+\r
+ texture = (yintercept>>4)&0xfc0;\r
+ offset = pwallpos<<10;\r
+ if (xtilestep == -1)\r
+ {\r
+ xintercept += TILEGLOBAL-offset;\r
+ texture = 0xfc0-texture;\r
+ }\r
+ else\r
+ xintercept += offset;\r
+\r
+ wallheight[pixx] = CalcHeight();\r
+\r
+ if (lasttilehit == tilehit)\r
+ {\r
+ // in the same wall type as last time, so check for optimized draw\r
+ if (texture == (unsigned)postsource)\r
+ {\r
+ // wide scale\r
+ postwidth++;\r
+ wallheight[pixx] = wallheight[pixx-1];\r
+ return;\r
+ }\r
+ else\r
+ {\r
+ ScalePost ();\r
+ (unsigned)postsource = texture;\r
+ postwidth = 1;\r
+ postx = pixx;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // new wall\r
+ if (lastside != -1) // if not the first scaled post\r
+ ScalePost ();\r
+\r
+ lasttilehit = tilehit;\r
+ postx = pixx;\r
+ postwidth = 1;\r
+\r
+ wallpic = vertwall[tilehit&63];\r
+\r
+ *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);\r
+ (unsigned)postsource = texture;\r
+ }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+=====================\r
+=\r
+= ClearScreen\r
+=\r
+=====================\r
+*/\r
+\r
+void ClearScreen (void)\r
+{\r
+ unsigned floor=egaFloor[gamestate.episode*10+mapon],\r
+ ceiling=egaCeiling[gamestate.episode*10+mapon];\r
+\r
+ //\r
+ // clear the screen\r
+ //\r
+asm mov dx,GC_INDEX\r
+asm mov ax,GC_MODE + 256*2 // read mode 0, write mode 2\r
+asm out dx,ax\r
+asm mov ax,GC_BITMASK + 255*256\r
+asm out dx,ax\r
+\r
+asm mov dx,40\r
+asm mov ax,[viewwidth]\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm sub dx,ax // dx = 40-viewwidth/8\r
+\r
+asm mov bx,[viewwidth]\r
+asm shr bx,1 // bl = viewwidth/16\r
+asm shr bx,1\r
+asm shr bx,1\r
+asm shr bx,1\r
+asm mov bh,BYTE PTR [viewheight]\r
+asm shr bh,1 // half height\r
+\r
+asm mov ax,[ceiling]\r
+asm mov es,[screenseg]\r
+asm mov di,[bufferofs]\r
+\r
+toploop:\r
+asm mov cl,bl\r
+asm rep stosw\r
+asm add di,dx\r
+asm dec bh\r
+asm jnz toploop\r
+\r
+asm mov bh,BYTE PTR [viewheight]\r
+asm shr bh,1 // half height\r
+asm mov ax,[floor]\r
+\r
+bottomloop:\r
+asm mov cl,bl\r
+asm rep stosw\r
+asm add di,dx\r
+asm dec bh\r
+asm jnz bottomloop\r
+\r
+\r
+asm mov dx,GC_INDEX\r
+asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2\r
+asm out dx,ax\r
+asm mov al,GC_BITMASK\r
+asm out dx,al\r
+\r
+}\r
+#endif\r
+//==========================================================================\r
+\r
+unsigned vgaCeiling[]=\r
+{\r
+#ifndef SPEAR\r
+ 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0xbfbf,\r
+ 0x4e4e,0x4e4e,0x4e4e,0x1d1d,0x8d8d,0x4e4e,0x1d1d,0x2d2d,0x1d1d,0x8d8d,\r
+ 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x2d2d,0xdddd,0x1d1d,0x1d1d,0x9898,\r
+\r
+ 0x1d1d,0x9d9d,0x2d2d,0xdddd,0xdddd,0x9d9d,0x2d2d,0x4d4d,0x1d1d,0xdddd,\r
+ 0x7d7d,0x1d1d,0x2d2d,0x2d2d,0xdddd,0xd7d7,0x1d1d,0x1d1d,0x1d1d,0x2d2d,\r
+ 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0xdddd,0xdddd,0x7d7d,0xdddd,0xdddd,0xdddd\r
+#else\r
+ 0x6f6f,0x4f4f,0x1d1d,0xdede,0xdfdf,0x2e2e,0x7f7f,0x9e9e,0xaeae,0x7f7f,\r
+ 0x1d1d,0xdede,0xdfdf,0xdede,0xdfdf,0xdede,0xe1e1,0xdcdc,0x2e2e,0x1d1d,0xdcdc\r
+#endif\r
+};\r
+\r
+/*\r
+=====================\r
+=\r
+= VGAClearScreen\r
+=\r
+=====================\r
+*/\r
+\r
+void VGAClearScreen (void)\r
+{\r
+ unsigned ceiling=vgaCeiling[gamestate.episode*10+mapon];\r
+\r
+ //\r
+ // clear the screen\r
+ //\r
+asm mov dx,SC_INDEX\r
+asm mov ax,SC_MAPMASK+15*256 // write through all planes\r
+asm out dx,ax\r
+\r
+asm mov dx,80\r
+asm mov ax,[viewwidth]\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm sub dx,ax // dx = 40-viewwidth/2\r
+\r
+asm mov bx,[viewwidth]\r
+asm shr bx,1 // bl = viewwidth/8\r
+asm shr bx,1\r
+asm shr bx,1\r
+asm mov bh,BYTE PTR [viewheight]\r
+asm shr bh,1 // half height\r
+\r
+asm mov es,[screenseg]\r
+asm mov di,[bufferofs]\r
+asm mov ax,[ceiling]\r
+\r
+toploop:\r
+asm mov cl,bl\r
+asm rep stosw\r
+asm add di,dx\r
+asm dec bh\r
+asm jnz toploop\r
+\r
+asm mov bh,BYTE PTR [viewheight]\r
+asm shr bh,1 // half height\r
+asm mov ax,0x1919\r
+\r
+bottomloop:\r
+asm mov cl,bl\r
+asm rep stosw\r
+asm add di,dx\r
+asm dec bh\r
+asm jnz bottomloop\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= CalcRotate\r
+=\r
+=====================\r
+*/\r
+\r
+int CalcRotate (objtype *ob)\r
+{\r
+ int angle,viewangle;\r
+\r
+ // this isn't exactly correct, as it should vary by a trig value,\r
+ // but it is close enough with only eight rotations\r
+\r
+ viewangle = player->angle + (centerx - ob->viewx)/8;\r
+\r
+ if (ob->obclass == rocketobj || ob->obclass == hrocketobj)\r
+ angle = (viewangle-180)- ob->angle;\r
+ else\r
+ angle = (viewangle-180)- dirangle[ob->dir];\r
+\r
+ angle+=ANGLES/16;\r
+ while (angle>=ANGLES)\r
+ angle-=ANGLES;\r
+ while (angle<0)\r
+ angle+=ANGLES;\r
+\r
+ if (ob->state->rotate == 2) // 2 rotation pain frame\r
+ return 4*(angle/(ANGLES/2)); // seperated by 3 (art layout...)\r
+\r
+ return angle/(ANGLES/8);\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= DrawScaleds\r
+=\r
+= Draws all objects that are visable\r
+=\r
+=====================\r
+*/\r
+\r
+#define MAXVISABLE 50\r
+\r
+typedef struct\r
+{\r
+ int viewx,\r
+ viewheight,\r
+ shapenum;\r
+} visobj_t;\r
+\r
+visobj_t vislist[MAXVISABLE],*visptr,*visstep,*farthest;\r
+\r
+void DrawScaleds (void)\r
+{\r
+ int i,j,least,numvisable,height;\r
+ memptr shape;\r
+ byte *tilespot,*visspot;\r
+ int shapenum;\r
+ unsigned spotloc;\r
+\r
+ statobj_t *statptr;\r
+ objtype *obj;\r
+\r
+ visptr = &vislist[0];\r
+\r
+//\r
+// place static objects\r
+//\r
+ for (statptr = &statobjlist[0] ; statptr !=laststatobj ; statptr++)\r
+ {\r
+ if ((visptr->shapenum = statptr->shapenum) == -1)\r
+ continue; // object has been deleted\r
+\r
+ if (!*statptr->visspot)\r
+ continue; // not visable\r
+\r
+ if (TransformTile (statptr->tilex,statptr->tiley\r
+ ,&visptr->viewx,&visptr->viewheight) && statptr->flags & FL_BONUS)\r
+ {\r
+ GetBonus (statptr);\r
+ continue;\r
+ }\r
+\r
+ if (!visptr->viewheight)\r
+ continue; // to close to the object\r
+\r
+ if (visptr < &vislist[MAXVISABLE-1]) // don't let it overflow\r
+ visptr++;\r
+ }\r
+\r
+//\r
+// place active objects\r
+//\r
+ for (obj = player->next;obj;obj=obj->next)\r
+ {\r
+ if (!(visptr->shapenum = obj->state->shapenum))\r
+ continue; // no shape\r
+\r
+ spotloc = (obj->tilex<<6)+obj->tiley; // optimize: keep in struct?\r
+ visspot = &spotvis[0][0]+spotloc;\r
+ tilespot = &tilemap[0][0]+spotloc;\r
+\r
+ //\r
+ // could be in any of the nine surrounding tiles\r
+ //\r
+ if (*visspot\r
+ || ( *(visspot-1) && !*(tilespot-1) )\r
+ || ( *(visspot+1) && !*(tilespot+1) )\r
+ || ( *(visspot-65) && !*(tilespot-65) )\r
+ || ( *(visspot-64) && !*(tilespot-64) )\r
+ || ( *(visspot-63) && !*(tilespot-63) )\r
+ || ( *(visspot+65) && !*(tilespot+65) )\r
+ || ( *(visspot+64) && !*(tilespot+64) )\r
+ || ( *(visspot+63) && !*(tilespot+63) ) )\r
+ {\r
+ obj->active = true;\r
+ TransformActor (obj);\r
+ if (!obj->viewheight)\r
+ continue; // too close or far away\r
+\r
+ visptr->viewx = obj->viewx;\r
+ visptr->viewheight = obj->viewheight;\r
+ if (visptr->shapenum == -1)\r
+ visptr->shapenum = obj->temp1; // special shape\r
+\r
+ if (obj->state->rotate)\r
+ visptr->shapenum += CalcRotate (obj);\r
+\r
+ if (visptr < &vislist[MAXVISABLE-1]) // don't let it overflow\r
+ visptr++;\r
+ obj->flags |= FL_VISABLE;\r
+ }\r
+ else\r
+ obj->flags &= ~FL_VISABLE;\r
+ }\r
+\r
+//\r
+// draw from back to front\r
+//\r
+ numvisable = visptr-&vislist[0];\r
+\r
+ if (!numvisable)\r
+ return; // no visable objects\r
+\r
+ for (i = 0; i<numvisable; i++)\r
+ {\r
+ least = 32000;\r
+ for (visstep=&vislist[0] ; visstep<visptr ; visstep++)\r
+ {\r
+ height = visstep->viewheight;\r
+ if (height < least)\r
+ {\r
+ least = height;\r
+ farthest = visstep;\r
+ }\r
+ }\r
+ //\r
+ // draw farthest\r
+ //\r
+ ScaleShape(farthest->viewx,farthest->shapenum,farthest->viewheight);\r
+\r
+ farthest->viewheight = 32000;\r
+ }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==============\r
+=\r
+= DrawPlayerWeapon\r
+=\r
+= Draw the player's hands\r
+=\r
+==============\r
+*/\r
+\r
+int weaponscale[NUMWEAPONS] = {SPR_KNIFEREADY,SPR_PISTOLREADY\r
+ ,SPR_MACHINEGUNREADY,SPR_CHAINREADY};\r
+\r
+void DrawPlayerWeapon (void)\r
+{\r
+ int shapenum;\r
+\r
+#ifndef SPEAR\r
+ if (gamestate.victoryflag)\r
+ {\r
+ if (player->state == &s_deathcam && (TimeCount&32) )\r
+ SimpleScaleShape(viewwidth/2,SPR_DEATHCAM,viewheight+1);\r
+ return;\r
+ }\r
+#endif\r
+\r
+ if (gamestate.weapon != -1)\r
+ {\r
+ shapenum = weaponscale[gamestate.weapon]+gamestate.weaponframe;\r
+ SimpleScaleShape(viewwidth/2,shapenum,viewheight+1);\r
+ }\r
+\r
+ if (demorecord || demoplayback)\r
+ SimpleScaleShape(viewwidth/2,SPR_DEMO,viewheight+1);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= CalcTics\r
+=\r
+=====================\r
+*/\r
+\r
+void CalcTics (void)\r
+{\r
+ long newtime,oldtimecount;\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+ if (lasttimecount > TimeCount)\r
+ TimeCount = lasttimecount; // if the game was paused a LONG time\r
+\r
+ do\r
+ {\r
+ newtime = TimeCount;\r
+ tics = newtime-lasttimecount;\r
+ } while (!tics); // make sure at least one tic passes\r
+\r
+ lasttimecount = newtime;\r
+\r
+#ifdef FILEPROFILE\r
+ strcpy (scratch,"\tTics:");\r
+ itoa (tics,str,10);\r
+ strcat (scratch,str);\r
+ strcat (scratch,"\n");\r
+ write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+ if (tics>MAXTICS)\r
+ {\r
+ TimeCount -= (tics-MAXTICS);\r
+ tics = MAXTICS;\r
+ }\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= FixOfs\r
+=\r
+========================\r
+*/\r
+\r
+void FixOfs (void)\r
+{\r
+ VW_ScreenToScreen (displayofs,bufferofs,viewwidth/8,viewheight);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= WallRefresh\r
+=\r
+====================\r
+*/\r
+\r
+void WallRefresh (void)\r
+{\r
+//\r
+// set up variables for this view\r
+//\r
+ viewangle = player->angle;\r
+ midangle = viewangle*(FINEANGLES/ANGLES);\r
+ viewsin = sintable[viewangle];\r
+ viewcos = costable[viewangle];\r
+ viewx = player->x - FixedByFrac(focallength,viewcos);\r
+ viewy = player->y + FixedByFrac(focallength,viewsin);\r
+\r
+ focaltx = viewx>>TILESHIFT;\r
+ focalty = viewy>>TILESHIFT;\r
+\r
+ viewtx = player->x >> TILESHIFT;\r
+ viewty = player->y >> TILESHIFT;\r
+\r
+ xpartialdown = viewx&(TILEGLOBAL-1);\r
+ xpartialup = TILEGLOBAL-xpartialdown;\r
+ ypartialdown = viewy&(TILEGLOBAL-1);\r
+ ypartialup = TILEGLOBAL-ypartialdown;\r
+\r
+ lastside = -1; // the first pixel is on a new wall\r
+ AsmRefresh ();\r
+ ScalePost (); // no more optimization on last post\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+========================\r
+=\r
+= ThreeDRefresh\r
+=\r
+========================\r
+*/\r
+\r
+void ThreeDRefresh (void)\r
+{\r
+ int tracedir;\r
+\r
+// this wouldn't need to be done except for my debugger/video wierdness\r
+ outportb (SC_INDEX,SC_MAPMASK);\r
+\r
+//\r
+// clear out the traced array\r
+//\r
+asm mov ax,ds\r
+asm mov es,ax\r
+asm mov di,OFFSET spotvis\r
+asm xor ax,ax\r
+asm mov cx,2048 // 64*64 / 2\r
+asm rep stosw\r
+\r
+ bufferofs += screenofs;\r
+\r
+//\r
+// follow the walls from there to the right, drawwing as we go\r
+//\r
+ VGAClearScreen ();\r
+\r
+ WallRefresh ();\r
+\r
+//\r
+// draw all the scaled images\r
+//\r
+ DrawScaleds(); // draw scaled stuff\r
+ DrawPlayerWeapon (); // draw player's hands\r
+\r
+//\r
+// show screen and time last cycle\r
+//\r
+ if (fizzlein)\r
+ {\r
+ FizzleFade(bufferofs,displayofs+screenofs,viewwidth,viewheight,20,false);\r
+ fizzlein = false;\r
+\r
+ lasttimecount = TimeCount = 0; // don't make a big tic count\r
+\r
+ }\r
+\r
+ bufferofs -= screenofs;\r
+ displayofs = bufferofs;\r
+\r
+ asm cli\r
+ asm mov cx,[displayofs]\r
+ asm mov dx,3d4h // CRTC address register\r
+ asm mov al,0ch // start address high register\r
+ asm out dx,al\r
+ asm inc dx\r
+ asm mov al,ch\r
+ asm out dx,al // set the high byte\r
+ asm sti\r
+\r
+ bufferofs += SCREENSIZE;\r
+ if (bufferofs > PAGE3START)\r
+ bufferofs = PAGE1START;\r
+\r
+ frameon++;\r
+ PM_NextFrame();\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r