1 /* Catacomb Armageddon Source Code
\r
2 * Copyright (C) 1993-2014 Flat Rock Software
\r
4 * This program is free software; you can redistribute it and/or modify
\r
5 * it under the terms of the GNU General Public License as published by
\r
6 * the Free Software Foundation; either version 2 of the License, or
\r
7 * (at your option) any later version.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License along
\r
15 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
24 //#define DRAWEACH // draw walls one at a time for debugging
\r
27 unsigned mostwalls,numwalls;
\r
30 =============================================================================
\r
34 =============================================================================
\r
37 #define PI 3.141592657
\r
38 #define ANGLEQUAD (ANGLES/4)
\r
42 #define FINEANGLES 3600
\r
47 const unsigned MAXSCALEHEIGHT = (VIEWWIDTH/2);
\r
48 const unsigned MAXVISHEIGHT = (VIEWHEIGHT/2);
\r
49 const unsigned BASESCALE = 32;
\r
52 =============================================================================
\r
56 =============================================================================
\r
60 // calculate location of screens in video memory so they have the
\r
61 // maximum possible distance seperating them (for scaling overflow)
\r
64 unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START};
\r
65 unsigned freelatch = FREESTART;
\r
69 long scaleshapecalll;
\r
70 long scaletablecall;
\r
73 =============================================================================
\r
77 =============================================================================
\r
80 long bytecount,endcount; // for profiling
\r
82 int pixelangle[VIEWWIDTH];
\r
83 int far finetangent[FINEANGLES+1];
\r
85 unsigned viewxpix,viewypix;
\r
88 ============================================================================
\r
92 ============================================================================
\r
95 fixed tileglobal = TILEGLOBAL;
\r
96 fixed focallength = FOCALLENGTH;
\r
97 fixed mindist = MINDIST;
\r
98 int viewheight = VIEWHEIGHT;
\r
102 tilept tile,lasttile, // tile of wall being followed
\r
103 focal, // focal point in tiles
\r
104 left,mid,right; // rightmost tile in view
\r
108 int segstart[VIEWHEIGHT], // addline tracks line segment and draws
\r
109 segend[VIEWHEIGHT],
\r
110 segcolor[VIEWHEIGHT]; // only when the color changes
\r
113 walltype walls[MAXWALLS],*leftwall,*rightwall;
\r
116 //==========================================================================
\r
124 long lasttimecount;
\r
130 int firstangle,lastangle;
\r
134 fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4);
\r
136 fixed viewx,viewy; // the focal point
\r
138 fixed viewsin,viewcos;
\r
140 int zbuffer[VIEWXH+1]; // holds the height of the wall at that point
\r
142 //==========================================================================
\r
144 void DrawLine (int xl, int xh, int y,int color);
\r
145 void DrawWall (walltype *wallptr);
\r
146 void TraceRay (unsigned angle);
\r
147 fixed FixedByFrac (fixed a, fixed b);
\r
148 fixed FixedAdd (void);
\r
149 fixed TransformX (fixed gx, fixed gy);
\r
150 int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);
\r
151 int BackTrace (int finish);
\r
152 void ForwardTrace (void);
\r
153 int TurnClockwise (void);
\r
154 int TurnCounterClockwise (void);
\r
155 void FollowWall (void);
\r
157 void NewScene (void);
\r
158 void BuildTables (void);
\r
160 //==========================================================================
\r
169 = Must be in write mode 2 with all planes enabled
\r
170 = The bit mask is left set to the end value, so clear it after all lines are
\r
173 = draws a black dot at the left edge of the line
\r
178 unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1};
\r
179 unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};
\r
180 unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};
\r
182 void DrawLine (int xl, int xh, int y,int color)
\r
184 unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
\r
190 Quit("DrawLine: xh<xl");
\r
192 Quit("DrawLine: y<VIEWY");
\r
194 Quit("DrawLine: y>VIEWYH");
\r
197 maskleft = leftmask[xlp];
\r
198 maskright = rightmask[xh&7];
\r
201 dest = bufferofs+ylookup[y]+xlb;
\r
204 // set the GC index register to point to the bit mask register
\r
206 asm mov al,GC_BITMASK
\r
207 asm mov dx,GC_INDEX
\r
213 // entire line is in one byte
\r
216 maskleft&=maskright;
\r
218 asm mov es,[screenseg]
\r
220 asm mov dx,GC_INDEX+1
\r
222 asm mov al,[BYTE PTR maskleft]
\r
223 asm out dx,al // mask off pixels
\r
225 asm mov al,[BYTE PTR color]
\r
226 asm xchg al,[es:di] // load latches and write pixels
\r
231 asm mov es,[screenseg]
\r
233 asm mov dx,GC_INDEX+1
\r
234 asm mov bh,[BYTE PTR color]
\r
239 asm mov al,[BYTE PTR maskleft]
\r
240 asm out dx,al // mask off pixels
\r
243 asm xchg al,[es:di] // load latches and write pixels
\r
250 asm out dx,al // no masking
\r
259 asm mov al,[BYTE PTR maskright]
\r
260 asm out dx,al // mask off pixels
\r
261 asm xchg bh,[es:di] // load latches and write pixels
\r
265 //==========================================================================
\r
267 void DrawLineDot (int xl, int xh, int y,int color)
\r
269 unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
\r
275 Quit("DrawLine: xh<xl");
\r
277 Quit("DrawLine: y<VIEWY");
\r
279 Quit("DrawLine: y>VIEWYH");
\r
282 maskdot = dotmask[xlp];
\r
283 maskleft = leftmask[xlp];
\r
284 maskright = rightmask[xh&7];
\r
287 dest = bufferofs+ylookup[y]+xlb;
\r
290 // set the GC index register to point to the bit mask register
\r
292 asm mov al,GC_BITMASK
\r
293 asm mov dx,GC_INDEX
\r
299 // entire line is in one byte
\r
302 maskleft&=maskright;
\r
304 asm mov es,[screenseg]
\r
306 asm mov dx,GC_INDEX+1
\r
308 asm mov al,[BYTE PTR maskleft]
\r
309 asm out dx,al // mask off pixels
\r
311 asm mov al,[BYTE PTR color]
\r
312 asm xchg al,[es:di] // load latches and write pixels
\r
316 // write the black dot at the start
\r
318 asm mov al,[BYTE PTR maskdot]
\r
319 asm out dx,al // mask off pixels
\r
322 asm xchg al,[es:di] // load latches and write pixels
\r
328 asm mov es,[screenseg]
\r
330 asm mov dx,GC_INDEX+1
\r
331 asm mov bh,[BYTE PTR color]
\r
336 asm mov al,[BYTE PTR maskleft]
\r
337 asm out dx,al // mask off pixels
\r
340 asm xchg al,[es:di] // load latches and write pixels
\r
343 // write the black dot at the start
\r
345 asm mov al,[BYTE PTR maskdot]
\r
346 asm out dx,al // mask off pixels
\r
348 asm xchg al,[es:di] // load latches and write pixels
\r
355 asm out dx,al // no masking
\r
364 asm mov al,[BYTE PTR maskright]
\r
365 asm out dx,al // mask off pixels
\r
366 asm xchg bh,[es:di] // load latches and write pixels
\r
372 //==========================================================================
\r
375 long wallscalesource;
\r
379 ====================
\r
383 ====================
\r
386 void near ScaleOneWall (int xl, int xh)
\r
388 int x,pixwidth,height;
\r
390 *(((unsigned *)&wallscalesource)+1) = wallseg[xl];
\r
392 for (x=xl;x<=xh;x+=pixwidth)
\r
394 height = wallheight[x];
\r
395 pixwidth = wallwidth[x];
\r
396 (unsigned)wallscalesource = wallofs[x];
\r
398 *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height];
\r
399 (unsigned)scaletablecall = scaledirectory[height]->codeofs[0];
\r
402 // scale a byte wide strip of wall
\r
408 asm shr di,1 // X in bytes
\r
409 asm add di,[bufferofs]
\r
414 asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1
\r
416 asm mov al,BYTE PTR [bitmasks1+bx]
\r
417 asm mov dx,GC_INDEX+1
\r
418 asm out dx,al // set bit mask register
\r
419 asm mov es,[screenseg]
\r
420 asm lds si,[wallscalesource]
\r
421 asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels
\r
423 asm mov al,BYTE PTR [ss:bitmasks2+bx]
\r
428 // draw a second byte for vertical strips that cross two bytes
\r
431 asm out dx,al // set bit mask register
\r
432 asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels
\r
441 char wall_anim_pos[NUMFLOORS];
\r
443 // EAST / WEST WALLS
\r
445 int far walllight1[NUMFLOORS] = {0,
\r
447 CRYSTAL_LIGHT_1PIC,
\r
448 CRYSTAL_LIGHT_2PIC,
\r
449 CRYSTAL_LIGHT_3PIC,
\r
450 CRYSTAL_LIGHT_4PIC, //4
\r
455 FIRE_WALL_4PIC, //8
\r
458 BRN_STONE_WALL_1PIC,
\r
459 KUDZU_WEAK_LIGHTPIC,
\r
460 KUDZU_LIGHT_WALLPIC,
\r
462 HEDGE_EYESPIC, //14
\r
464 W_GEN_DOOR1PIC, //15
\r
465 BRN_WINDOW_LIGHTPIC,
\r
469 GRAY_LIGHT_WALLPIC,
\r
470 GRAY_LIGHT_SIGNPIC, //20
\r
472 MANICLE_LIGHT_WALLPIC,
\r
473 MANICLE_LIGHT_BLOODYPIC,
\r
475 LIGHT_CURTAIN_WINDOWPIC,
\r
476 LIGHT_CURTAIN_WALLPIC,
\r
477 BRN_LIGHT_SIGNPIC, //25
\r
479 LIGHT_STONE_WALLPIC,
\r
481 W_GEN_DOOR2PIC, //27
\r
483 TROLL_LIGHT_STONEPIC,
\r
485 BRN_FLAGSTONE_LIGHT_2PIC,
\r
489 DMG_BRN_FSTN_LTPIC,
\r
491 RUST_METAL_LIGHTPIC,
\r
492 GRAY_METAL_LIGHTPIC, //33
\r
494 WEAK_STONE_LIGHTPIC,
\r
496 DMG_FIN_FSTN_LTPIC,
\r
498 WEAK_GRAY_RFGSTN_LIGHTPIC,
\r
501 WEAK_CRYSTAL_LIGHTPIC,
\r
505 STEEL_DOOR1PIC, //40
\r
507 RED_MUD_WEAK_LIGHTPIC,
\r
509 STEEL_DOOR2PIC, //42
\r
512 TROLL_BLOODY_LT_STONEPIC,
\r
515 GRY_DOOR_LTPIC, //46
\r
517 BRN_DOOR_LTPIC, //47
\r
519 GRY_FGSTN_LTPIC, //48
\r
522 WATER_LIGHT_WEAK_1PIC,
\r
523 WATER_LIGHT_WEAK_2PIC,
\r
524 WATER_LIGHT_WEAK_3PIC, //52
\r
532 LIGHT_BREATH_3PIC, //58
\r
538 WATER_EXP_WALL_1PIC,
\r
539 WATER_EXP_WALL_2PIC,
\r
540 WATER_EXP_WALL_3PIC, //64
\r
561 // NORTH / SOUTH WALLS
\r
563 int far walldark1[NUMFLOORS] = {0,
\r
568 CRYSTAL_DARK_4PIC, //4
\r
573 FIRE_WALL_4PIC, //8
\r
576 BRN_STONE_WALL_2PIC,
\r
577 KUDZU_WEAK_DARKPIC,
\r
578 KUDZU_DARK_WALLPIC,
\r
580 HEDGE_EYESPIC, //14
\r
582 W_GEN_DOOR1PIC, //15
\r
583 BRN_WINDOW_DARKPIC,
\r
588 GRAY_DARK_SIGNPIC, //20
\r
590 MANICLE_DARK_WALLPIC,
\r
591 MANICLE_DARK_BLOODYPIC,
\r
593 DARK_CURTAIN_WINDOWPIC,
\r
594 DARK_CURTAIN_WALLPIC,
\r
597 DARK_STONE_WALLPIC,
\r
599 W_GEN_DOOR2PIC, //27
\r
601 TROLL_DARK_STONEPIC,
\r
603 BRN_FLAGSTONE_DARK_2PIC,
\r
605 W_CRYSTAL_DOORPIC, //30
\r
607 DMG_BRN_FSTN_DKPIC,
\r
609 RUST_METAL_DARKPIC,
\r
610 GRAY_METAL_DARKPIC,
\r
612 WEAK_STONE_DARKPIC,
\r
614 DMG_FIN_FSTN_DKPIC, //35
\r
616 WEAK_GRAY_RFGSTN_DARKPIC,
\r
619 WEAK_CRYSTAL_DARKPIC,
\r
623 STEEL_DOOR1PIC, //40
\r
625 BRN_MUD_WEAK_DARKPIC,
\r
630 TROLL_BLOODY_DK_STONEPIC,
\r
634 GRY_DOOR_DKPIC, //46
\r
635 BRN_DOOR_DKPIC, //47
\r
636 GRY_FGSTN_DKPIC, //48
\r
639 WATER_DARK_WEAK_1PIC,
\r
640 WATER_DARK_WEAK_2PIC,
\r
641 WATER_DARK_WEAK_3PIC,
\r
655 WATER_EXP_WALL_1PIC,
\r
656 WATER_EXP_WALL_2PIC,
\r
657 WATER_EXP_WALL_3PIC,
\r
680 =====================
\r
684 = Draws a wall by vertical segments, for texture mapping!
\r
686 = wallptr->side is true for east/west walls (constant x)
\r
688 = fracheight and fracstep are 16.16 bit fractions
\r
690 =====================
\r
693 void DrawVWall (walltype *wallptr)
\r
697 unsigned width,sourceint;
\r
698 unsigned wallpic,wallpicseg;
\r
700 long fracheight,fracstep,longheightchange;
\r
703 unsigned slope,distance;
\r
704 int traceangle,angle;
\r
706 unsigned lastpix,lastsource,lastwidth;
\r
708 if (wallptr->rightclip < wallptr->leftclip)
\r
709 Quit ("DrawVWall: Right < Left");
\r
712 // setup for height calculation
\r
714 wallptr->height1 >>= 1;
\r
715 wallptr->height2 >>= 1;
\r
716 wallptr->planecoord>>=10; // remove non significant bits
\r
718 width = wallptr->x2 - wallptr->x1;
\r
721 heightchange = wallptr->height2 - wallptr->height1;
\r
722 asm mov ax,[heightchange]
\r
723 asm mov WORD PTR [longheightchange+2],ax
\r
724 asm mov WORD PTR [longheightchange],0 // avoid long shift by 16
\r
725 fracstep = longheightchange/width;
\r
728 fracheight = ((long)wallptr->height1<<16)+0x8000;
\r
729 skip = wallptr->leftclip - wallptr->x1;
\r
731 fracheight += fracstep*skip;
\r
734 // setup for texture mapping
\r
736 // mapadd is 64*64 (to keep source positive) + the origin wall intercept
\r
737 // distance has 6 unit bits, and 6 frac bits
\r
738 // traceangle is the center view angle in FINEANGLES, moved to be in
\r
739 // the +-90 degree range (to thew right of origin)
\r
741 traceangle = fineviewangle;
\r
743 // find wall picture to map from
\r
746 { // east or west wall
\r
748 wallpic = walllight1[wallptr->color+wall_anim_pos[wallptr->color]];
\r
749 if (wallptr->planecoord < viewxpix)
\r
751 distance = viewxpix-wallptr->planecoord;
\r
752 traceangle -= FINEANGLES/2;
\r
753 mapadd = (64-viewypix&63); // the pixel spot of the origin
\r
757 distance = wallptr->planecoord-viewxpix;
\r
758 // traceangle is correct
\r
759 mapadd = viewypix&63; // the pixel spot of the origin
\r
763 { // north or south wall
\r
765 wallpic = walldark1[wallptr->color+wall_anim_pos[wallptr->color]];
\r
766 if (wallptr->planecoord < viewypix)
\r
768 distance = viewypix-wallptr->planecoord;
\r
769 traceangle -= FINEANGLES/4;
\r
770 mapadd = viewxpix&63; // the pixel spot of the origin
\r
774 distance = wallptr->planecoord-viewypix;
\r
775 traceangle -= FINEANGLES*3/4;
\r
776 mapadd = (64-viewxpix&63); // the pixel spot of the origin
\r
780 mapadd = 64*64-mapadd; // make sure it stays positive
\r
782 wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC];
\r
783 if (traceangle > FINEANGLES/2)
\r
784 traceangle -= FINEANGLES;
\r
787 // calculate everything
\r
789 // IMPORTANT! This loop is executed around 5000 times / second!
\r
791 lastpix = lastsource = (unsigned)-1;
\r
793 for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++)
\r
798 asm mov ax,WORD PTR [fracheight]
\r
799 asm mov dx,WORD PTR [fracheight+2]
\r
801 asm add ax,WORD PTR [fracstep]
\r
802 asm adc dx,WORD PTR [fracstep+2]
\r
803 asm mov WORD PTR [fracheight],ax
\r
804 asm mov WORD PTR [fracheight+2],dx
\r
807 asm cmp cx,MAXSCALEHEIGHT
\r
808 asm jbe storeheight
\r
809 asm mov cx,MAXSCALEHEIGHT
\r
811 asm mov WORD PTR [wallheight+bx],cx
\r
812 asm mov WORD PTR [zbuffer+bx],cx
\r
814 // height = fracheight>>16;
\r
815 // fracheight += fracstep;
\r
816 // if (height > MAXSCALEHEIGHT)
\r
817 // height = MAXSCALEHEIGHT;
\r
818 // wallheight[x] = zbuffer[x] = height;
\r
823 angle = pixelangle[x]+traceangle;
\r
827 slope = finetangent[angle];
\r
830 // distance is an unsigned 6.6 bit number (12 pixel bits)
\r
831 // slope is a signed 5.10 bit number
\r
832 // result is a signed 11.16 bit number
\r
836 source = distance*slope;
\r
840 source &= 63; // mask off the unused units
\r
841 source = 63-source;
\r
842 source <<= 6; // multiply by 64 for offset into pic
\r
844 asm mov ax,[distance]
\r
845 asm imul [slope] // ax is the source pixel
\r
848 asm shr al,1 // low 6 bits is now pixel number
\r
849 asm add ax,[mapadd]
\r
852 asm sub dx,ax // otherwise it is backwards
\r
858 asm shl dx,1 // *64 to index into shape
\r
859 asm mov [source],dx
\r
861 if (source != lastsource)
\r
863 if (lastpix != (unsigned)-1)
\r
865 wallofs[lastpix] = lastsource;
\r
866 wallseg[lastpix] = wallpicseg;
\r
867 wallwidth[lastpix] = lastwidth;
\r
870 lastsource = source;
\r
874 lastwidth++; // optimized draw, same map as last one
\r
876 wallofs[lastpix] = lastsource;
\r
877 wallseg[lastpix] = wallpicseg;
\r
878 wallwidth[lastpix] = lastwidth;
\r
882 //==========================================================================
\r
890 = Used to find the left and rightmost tile in the view area to be traced from
\r
891 = Follows a ray of the given angle from viewx,viewy in the global map until
\r
892 = it hits a solid tile
\r
894 = tile.x,tile.y : tile coordinates of contacted tile
\r
895 = tilecolor : solid tile's color
\r
902 void TraceRay (unsigned angle)
\r
904 long tracex,tracey,tracexstep,traceystep,searchx,searchy;
\r
906 int otx,oty,searchsteps;
\r
908 tracexstep = costable[angle];
\r
909 traceystep = sintable[angle];
\r
912 // advance point so it is even with the view plane before we start checking
\r
914 fixtemp = FixedByFrac(prestep,tracexstep);
\r
915 tracex = viewx+fixtemp;
\r
916 fixtemp = FixedByFrac(prestep,traceystep);
\r
917 tracey = viewy-fixtemp;
\r
919 tile.x = tracex>>TILESHIFT; // starting point in tiles
\r
920 tile.y = tracey>>TILESHIFT;
\r
923 if (tracexstep<0) // use 2's complement, not signed magnitude
\r
924 tracexstep = -(tracexstep&0x7fffffff);
\r
926 if (traceystep<0) // use 2's complement, not signed magnitude
\r
927 traceystep = -(traceystep&0x7fffffff);
\r
930 // we assume viewx,viewy is not inside a solid tile, so go ahead one step
\r
933 do // until a solid tile is hit
\r
937 spotvis[otx][oty] = true;
\r
938 tracex += tracexstep;
\r
939 tracey -= traceystep;
\r
940 tile.x = tracex>>TILESHIFT;
\r
941 tile.y = tracey>>TILESHIFT;
\r
943 if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) )
\r
946 // trace crossed two solid tiles, so do a binary search along the line
\r
947 // to find a spot where only one tile edge is crossed
\r
950 searchx = tracexstep;
\r
951 searchy = traceystep;
\r
956 if (tile.x!=otx && tile.y!=oty)
\r
964 // not far enough, no tiles crossed
\r
970 // if it is REAL close, go for the most clockwise intersection
\r
972 if (++searchsteps == 16)
\r
974 tracex = (long)otx<<TILESHIFT;
\r
975 tracey = (long)oty<<TILESHIFT;
\r
980 tracex += TILEGLOBAL-1;
\r
981 tracey += TILEGLOBAL;
\r
985 tracex += TILEGLOBAL;
\r
993 tracey += TILEGLOBAL-1;
\r
1002 tile.x = tracex>>TILESHIFT;
\r
1003 tile.y = tracey>>TILESHIFT;
\r
1005 } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) );
\r
1007 } while (!(tilecolor = tilemap[tile.x][tile.y]) );
\r
1011 //==========================================================================
\r
1015 ========================
\r
1019 = multiply a 16/16 bit, 2's complement fixed point number by a 16 bit
\r
1020 = fraction, passed as a signed magnitude 32 bit number
\r
1022 ========================
\r
1025 #pragma warn -rvl // I stick the return value in with ASMs
\r
1027 fixed FixedByFrac (fixed a, fixed b)
\r
1034 asm mov si,[WORD PTR b+2] // sign of result = sign of fraction
\r
1036 asm mov ax,[WORD PTR a]
\r
1037 asm mov cx,[WORD PTR a+2]
\r
1040 asm jns aok: // negative?
\r
1045 asm xor si,0x8000 // toggle sign of result
\r
1049 // multiply cx:ax by bx
\r
1051 asm mov bx,[WORD PTR b]
\r
1052 asm mul bx // fraction*fraction
\r
1053 asm mov di,dx // di is low word of result
\r
1055 asm mul bx // units*fraction
\r
1060 // put result dx:ax in 2's complement
\r
1062 asm test si,0x8000 // is the result negative?
\r
1077 =========================
\r
1081 = add two 16 bit fixed point numbers
\r
1082 = to subtract, invert the sign of B before invoking
\r
1084 =========================
\r
1087 fixed FixedAdd (fixed a, fixed b)
\r
1091 asm mov ax,[WORD PTR a]
\r
1092 asm mov dx,[WORD PTR a+2]
\r
1094 asm mov bx,[WORD PTR b]
\r
1095 asm mov cx,[WORD PTR b+2]
\r
1098 asm jns aok: // negative?
\r
1100 asm not ax // convert a from signed magnitude to 2's compl
\r
1107 asm jns bok: // negative?
\r
1109 asm not bx // convert b from signed magnitude to 2's compl
\r
1115 asm add ax,bx // perform the addition
\r
1119 asm and dx,0x7fff // value was negative
\r
1120 asm not ax // back to signed magnitude
\r
1127 asm mov [WORD PTR value],ax
\r
1128 asm mov [WORD PTR value+2],dx
\r
1134 //==========================================================================
\r
1138 ========================
\r
1142 = Takes paramaters:
\r
1143 = gx,gy : globalx/globaly of point
\r
1146 = viewx,viewy : point of view
\r
1147 = viewcos,viewsin : sin/cos of viewangle
\r
1151 = CENTERX : pixel location of center of view window
\r
1152 = TILEGLOBAL : size of one
\r
1153 = FOCALLENGTH : distance behind viewx/y for center of projection
\r
1154 = scale : conversion from global value to screen value
\r
1157 = screenx,screenheight: projected edge location and size
\r
1159 ========================
\r
1162 void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight)
\r
1165 fixed gxt,gyt,nx,ny;
\r
1168 // translate point to view centered coordinates
\r
1176 gxt = FixedByFrac(gx,viewcos);
\r
1177 gyt = FixedByFrac(gy,viewsin);
\r
1183 gxt = FixedByFrac(gx,viewsin);
\r
1184 gyt = FixedByFrac(gy,viewcos);
\r
1188 // calculate perspective ratio
\r
1193 ratio = nx*scale/FOCALLENGTH;
\r
1195 if (ratio<=MINRATIO)
\r
1198 *screenx = CENTERX + ny/ratio;
\r
1200 *screenheight = TILEGLOBAL/ratio;
\r
1206 // transform actor
\r
1208 void TransformActor (objtype *ob)
\r
1211 fixed gx,gy,gxt,gyt,nx,ny;
\r
1214 // translate point to view centered coordinates
\r
1222 gxt = FixedByFrac(gx,viewcos);
\r
1223 gyt = FixedByFrac(gy,viewsin);
\r
1224 nx = gxt-gyt-ob->size;
\r
1229 gxt = FixedByFrac(gx,viewsin);
\r
1230 gyt = FixedByFrac(gy,viewcos);
\r
1234 // calculate perspective ratio
\r
1239 ratio = nx*scale/FOCALLENGTH;
\r
1241 if (ratio<=MINRATIO)
\r
1244 ob->viewx = CENTERX + ny/ratio;
\r
1246 ob->viewheight = TILEGLOBAL/ratio;
\r
1249 //==========================================================================
\r
1251 fixed TransformX (fixed gx, fixed gy)
\r
1254 fixed gxt,gyt,nx,ny;
\r
1257 // translate point to view centered coordinates
\r
1265 gxt = FixedByFrac(gx,viewcos);
\r
1266 gyt = FixedByFrac(gy,viewsin);
\r
1271 //==========================================================================
\r
1274 ==================
\r
1280 = scale projection constant
\r
1281 = sintable/costable overlapping fractional tables
\r
1282 = firstangle/lastangle angles from focalpoint to left/right view edges
\r
1283 = prestep distance from focal point before checking for tiles
\r
1285 ==================
\r
1288 void BuildTables (void)
\r
1293 float angle,anglestep,radtoint;
\r
1298 // calculate the angle offset from view angle of each pixel's ray
\r
1300 radtoint = (float)FINEANGLES/2/PI;
\r
1301 for (i=0;i<VIEWWIDTH/2;i++)
\r
1303 // start 1/2 pixel over, so viewangle bisects two middle pixels
\r
1304 x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;
\r
1305 tang = (float)x/(FOCALLENGTH+MINDIST);
\r
1306 angle = atan(tang);
\r
1307 intang = angle*radtoint;
\r
1308 pixelangle[VIEWWIDTH/2-1-i] = intang;
\r
1309 pixelangle[VIEWWIDTH/2+i] = -intang;
\r
1313 // calculate fine tangents
\r
1314 // 1 sign bit, 5 units (clipped to), 10 fracs
\r
1316 #define MININT (-MAXINT)
\r
1318 for (i=0;i<FINEANGLES/4;i++)
\r
1320 intang = tan(i/radtoint)*(1l<<10);
\r
1323 // if the tangent is not reprentable in this many bits, bound the
\r
1324 // units part ONLY
\r
1326 if (intang>MAXINT)
\r
1327 intang = 0x8f00 | (intang & 0xff);
\r
1328 else if (intang<MININT)
\r
1329 intang = 0xff00 | (intang & 0xff);
\r
1331 finetangent[i] = intang;
\r
1332 // finetangent[FINEANGLES/2+i] = intang;
\r
1333 // finetangent[FINEANGLES/2-i-1] = -intang;
\r
1334 finetangent[FINEANGLES-i-1] = -intang;
\r
1338 // calculate scale value so one tile at mindist allmost fills the view horizontally
\r
1340 scale = GLOBAL1/VIEWWIDTH;
\r
1341 scale *= focallength;
\r
1342 scale /= (focallength+mindist);
\r
1345 // costable overlays sintable with a quarter phase shift
\r
1346 // ANGLES is assumed to be divisable by four
\r
1348 // The low word of the value is the fraction, the high bit is the sign bit,
\r
1349 // bits 16-30 should be 0
\r
1353 anglestep = PI/2/ANGLEQUAD;
\r
1354 for (i=0;i<=ANGLEQUAD;i++)
\r
1356 value=GLOBAL1*sin(angle);
\r
1358 sintable[i+ANGLES]=
\r
1359 sintable[ANGLES/2-i] = value;
\r
1360 sintable[ANGLES-i]=
\r
1361 sintable[ANGLES/2+i] = value | 0x80000000l;
\r
1362 angle += anglestep;
\r
1366 // figure trace angles for first and last pixel on screen
\r
1368 angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);
\r
1369 angle *= ANGLES/(PI*2);
\r
1371 intang = (int)angle+1;
\r
1372 firstangle = intang;
\r
1373 lastangle = -intang;
\r
1375 prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);
\r
1380 walls[0].x2 = VIEWX-1;
\r
1381 walls[0].height2 = 32000;
\r
1385 //==========================================================================
\r
1388 =====================
\r
1392 =====================
\r
1395 void ClearScreen (void)
\r
1397 unsigned topcolor=*skycolor, bottomcolor=*groundcolor;
\r
1398 unsigned topimage=topcolor&0xf0,bottomimage=bottomcolor&0xf0;
\r
1399 unsigned pfoffset=0;
\r
1403 if (topimage == 0x20) // special code for lightning
\r
1404 topimage = topcolor = 0;
\r
1406 // Manually wipe screen with solid color.
\r
1407 // If BOTH sky and ground are 'images' don't manually clear it!
\r
1409 if ((!topimage) || (!bottomimage))
\r
1414 // clear the screen
\r
1416 asm mov dx,GC_INDEX
\r
1417 asm mov ax,GC_MODE + 256*2 // read mode 0, write mode 2
\r
1419 asm mov ax,GC_BITMASK + 255*256
\r
1422 //asm mov dx,40-VIEWWIDTH/8 // dx = modulo
\r
1423 asm mov bl,VIEWWIDTH/16
\r
1424 asm mov bh,CENTERY+1
\r
1426 asm mov ax,topcolor
\r
1427 asm mov es,[screenseg]
\r
1428 asm mov di,[bufferofs]
\r
1429 asm add di,((SCREENWIDTH*VIEWY)+(VIEWX/8))
\r
1435 //asm add di,dx // no need to add "0" modulo
\r
1439 asm mov bh,CENTERY+1
\r
1440 asm mov ax,bottomcolor
\r
1446 //asm add di,dx // no need to add "0" modulo
\r
1448 asm jnz bottomloop
\r
1455 // code to test parallax turning
\r
1461 pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12);
\r
1462 while (pfoffset >= 640)
\r
1464 LatchDrawPicStrip(0,0,SKY1PIC+topimage,pfoffset+8);
\r
1469 //// pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12)+320;
\r
1470 // pfoffset += 320;
\r
1471 // while (pfoffset >= 640)
\r
1472 // pfoffset -= 640;
\r
1473 // LatchDrawPicStrip(0,64,SKY1PIC+topimage,pfoffset+8);
\r
1474 bottomimage -= 16;
\r
1475 LatchDrawPic(0,64,GND1PIC+bottomimage);
\r
1480 asm mov dx,GC_INDEX
\r
1481 asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2
\r
1483 asm mov al,GC_BITMASK
\r
1488 //==========================================================================
\r
1491 =====================
\r
1495 = Clips and draws all the walls traced this refresh
\r
1497 =====================
\r
1500 void DrawWallList (void)
\r
1502 int i,leftx,newleft,rightclip;
\r
1503 walltype *wall, *check;
\r
1507 asm mov di,OFFSET wallwidth
\r
1509 asm mov cx,VIEWWIDTH/2
\r
1514 rightwall->x1 = VIEWXH+1;
\r
1515 rightwall->height1 = 32000;
\r
1516 (rightwall+1)->x1 = 32000;
\r
1520 for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)
\r
1522 if (leftx >= wall->x2)
\r
1525 rightclip = wall->x2;
\r
1528 while (check->x1 <= rightclip && check->height1 >= wall->height2)
\r
1530 rightclip = check->x1-1;
\r
1534 if (rightclip>VIEWXH)
\r
1537 if (leftx < wall->x1 - 1)
\r
1538 newleft = wall->x1-1; // there was black space between walls
\r
1542 if (rightclip > newleft)
\r
1544 wall->leftclip = newleft+1;
\r
1545 wall->rightclip = rightclip;
\r
1547 leftx = rightclip;
\r
1552 ScaleWalls (); // draw all the walls
\r
1556 //==========================================================================
\r
1559 =====================
\r
1563 = Draws all objects that are visable
\r
1565 =====================
\r
1568 objtype *depthsort[MAXACTORS];
\r
1570 void DrawScaleds (void)
\r
1572 #if USE_INERT_LIST
\r
1573 extern inertobjtype inertobjlist[], *inert;
\r
1575 boolean inertlist=false;
\r
1577 int i,j,least,numvisable,height;
\r
1578 objtype *obj,**vislist,*farthest;
\r
1580 byte *tilespot,*visspot;
\r
1585 // calculate base positions of all objects
\r
1587 vislist = &depthsort[0];
\r
1589 obj = player->next;
\r
1592 tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley;
\r
1593 visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley;
\r
1595 // could be in any of the nine surrounding tiles
\r
1598 || ( *(visspot-1) && !*(tilespot-1) )
\r
1599 || ( *(visspot+1) && !*(tilespot+1) )
\r
1600 || ( *(visspot-65) && !*(tilespot-65) )
\r
1601 || ( *(visspot-64) && !*(tilespot-64) )
\r
1602 || ( *(visspot-63) && !*(tilespot-63) )
\r
1603 || ( *(visspot+65) && !*(tilespot+65) )
\r
1604 || ( *(visspot+64) && !*(tilespot+64) )
\r
1605 || ( *(visspot+63) && !*(tilespot+63) ) )
\r
1607 #if USE_INERT_LIST
\r
1610 if ((obj->active == noalways) || (obj->active == always))
\r
1611 obj->active = always;
\r
1613 obj->active = yes;
\r
1614 TransformActor (obj);
\r
1615 if (!obj->viewheight || obj->viewheight > VIEWWIDTH)
\r
1616 goto cont; // too close or far away
\r
1618 if (!obj->state->shapenum)
\r
1625 #if USE_INERT_LIST
\r
1628 if ((obj->active != always) && (obj->active != noalways))
\r
1633 #if USE_INERT_LIST
\r
1634 if ((!obj) && (!inertlist))
\r
1636 if (inert != inertobjlist)
\r
1637 obj = (objtype *)inertobjlist;
\r
1643 if (vislist == &depthsort[0])
\r
1644 return; // no visable objects
\r
1647 // draw from back to front
\r
1649 for (i = 0; i<numvisable; i++)
\r
1652 for (j=0;j<numvisable;j++)
\r
1654 height = depthsort[j]->viewheight;
\r
1655 if (height < least)
\r
1658 farthest = depthsort[j];
\r
1664 shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC];
\r
1665 ScaleShape(farthest->viewx,shape,farthest->viewheight);
\r
1666 farthest->viewheight = 32000;
\r
1670 //==========================================================================
\r
1674 =====================
\r
1678 =====================
\r
1681 void CalcTics (void)
\r
1683 long newtime,oldtimecount;
\r
1692 // calculate tics since last refresh for adaptive timing
\r
1694 if (lasttimecount > TimeCount)
\r
1695 TimeCount = lasttimecount; // if the game was paused a LONG time
\r
1698 if (DemoMode) // demo recording and playback needs
\r
1699 { // to be constant
\r
1701 // take DEMOTICS or more tics, and modify Timecount to reflect time taken
\r
1703 oldtimecount = lasttimecount;
\r
1704 while (TimeCount<oldtimecount+DEMOTICS*2)
\r
1706 lasttimecount = oldtimecount + DEMOTICS;
\r
1707 TimeCount = lasttimecount + DEMOTICS;
\r
1708 realtics = tics = DEMOTICS;
\r
1714 // non demo, so report actual time
\r
1716 newtime = TimeCount;
\r
1717 realtics = tics = newtime-lasttimecount;
\r
1718 lasttimecount = newtime;
\r
1720 #ifdef FILEPROFILE
\r
1721 strcpy (scratch,"\tTics:");
\r
1722 itoa (tics,str,10);
\r
1723 strcat (scratch,str);
\r
1724 strcat (scratch,"\n");
\r
1725 write (profilehandle,scratch,strlen(scratch));
\r
1730 TimeCount -= (tics-MAXTICS);
\r
1734 if (realtics>MAXREALTICS)
\r
1735 realtics = MAXREALTICS;
\r
1740 //==========================================================================
\r
1744 ========================
\r
1748 ========================
\r
1751 void DrawHand (void)
\r
1753 #define HAND_X_POS ((VIEWWIDTH/16)-(10/2)) // "10" = hand width in bytes
\r
1755 #define picnum HAND1PICM
\r
1758 unsigned dest,width,height;
\r
1760 // if (gamestate.shotpower || boltsleft)
\r
1761 // picnum += (((unsigned)TimeCount>>3)&1);
\r
1763 source = grsegs[picnum];
\r
1764 dest = ylookup[VIEWHEIGHT-handheight]+HAND_X_POS+bufferofs; // 12
\r
1765 width = picmtable[picnum-STARTPICM].width;
\r
1766 height = picmtable[picnum-STARTPICM].height;
\r
1768 VW_MaskBlock(source,0,dest,width,handheight,width*height);
\r
1772 //==========================================================================
\r
1776 ========================
\r
1780 ========================
\r
1783 void ThreeDRefresh (void)
\r
1788 aborttrace = false;
\r
1791 // clear out the traced array
\r
1795 asm mov di,OFFSET spotvis
\r
1797 asm mov cx,[mapwidth] // mapheight*32 words
\r
1807 // set up variables for this view
\r
1810 viewangle = player->angle;
\r
1811 fineviewangle = viewangle*(FINEANGLES/ANGLES);
\r
1812 viewsin = sintable[viewangle];
\r
1813 viewcos = costable[viewangle];
\r
1814 viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos);
\r
1815 viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin);
\r
1816 viewx &= 0xfffffc00; // stop on a pixel boundary
\r
1817 viewy &= 0xfffffc00;
\r
1820 viewxpix = viewx>>10;
\r
1821 viewypix = viewy>>10;
\r
1823 focal.x = viewx>>TILESHIFT;
\r
1824 focal.y = viewy>>TILESHIFT;
\r
1827 // find the rightmost visable tile in view
\r
1829 tracedir = viewangle + lastangle;
\r
1832 else if (tracedir>=ANGLES)
\r
1834 TraceRay( tracedir );
\r
1839 // find the leftmost visable tile in view
\r
1841 tracedir = viewangle + firstangle;
\r
1844 else if (tracedir>=ANGLES)
\r
1846 TraceRay( tracedir );
\r
1849 // follow the walls from there to the right
\r
1851 rightwall = &walls[1];
\r
1858 // actually draw stuff
\r
1860 if (++screenpage == 3)
\r
1863 bufferofs = screenloc[screenpage];
\r
1869 // draw the wall list saved be FollowWalls ()
\r
1871 // animframe = (TimeCount&8)>>3;
\r
1874 // draw all the scaled images
\r
1876 asm mov dx,GC_INDEX
\r
1878 asm mov ax,GC_COLORDONTCARE
\r
1879 asm out dx,ax // don't look at any of the planes
\r
1881 asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2
\r
1884 asm mov al,GC_BITMASK
\r
1887 AnimateWallList();
\r
1901 // show screen and time last cycle
\r
1906 FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true);
\r
1907 lasttimecount = TimeCount;
\r
1908 if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement
\r
1912 asm mov cx,[bufferofs]
\r
1913 asm mov dx,3d4h // CRTC address register
\r
1914 asm mov al,0ch // start address high register
\r
1918 asm out dx,al // set the high byte
\r
1920 asm mov al,0dh // start address low register
\r
1924 asm out dx,al // set the low byte
\r
1927 displayofs = bufferofs;
\r