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
22 =============================================================================
\r
27 scrolling more than one tile / refresh forces a total redraw
\r
29 two overlapping sprites of equal priority can change drawing order when
\r
32 =============================================================================
\r
35 #include "ID_HEADS.H"
\r
39 =============================================================================
\r
43 =============================================================================
\r
46 #define SCREENTILESWIDE 20
\r
47 #define SCREENTILESHIGH 13
\r
49 #define SCREENSPACE (SCREENWIDTH*240)
\r
50 #define FREEEGAMEM (0x10000l-3l*SCREENSPACE)
\r
53 // the update array must have enough space for two screens that can float
\r
54 // up two two tiles each way
\r
56 // (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared
\r
57 // by word width instructions
\r
59 #define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)
\r
60 #define UPDATESPARESIZE (UPDATEWIDE*2+4)
\r
61 #define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)
\r
63 #define G_EGASX_SHIFT 7 // global >> ?? = screen x
\r
64 #define G_CGASX_SHIFT 6 // global >> ?? = screen x
\r
65 #define G_SY_SHIFT 4 // global >> ?? = screen y
\r
67 unsigned SX_T_SHIFT; // screen x >> ?? = tile EGA = 1, CGA = 2;
\r
68 #define SY_T_SHIFT 4 // screen y >> ?? = tile
\r
71 #define EGAPORTSCREENWIDE 42
\r
72 #define CGAPORTSCREENWIDE 84
\r
73 #define PORTSCREENHIGH 224
\r
75 #define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)
\r
76 #define UPDATESPARESIZE (UPDATEWIDE*2+4)
\r
77 #define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)
\r
79 #define MAXSCROLLEDGES 6
\r
82 =============================================================================
\r
86 =============================================================================
\r
89 typedef struct spriteliststruct
\r
91 int screenx,screeny;
\r
94 unsigned grseg,sourceofs,planesize;
\r
96 unsigned tilex,tiley,tilewide,tilehigh;
\r
97 int priority,updatecount;
\r
98 struct spriteliststruct **prevptr,*nextsprite;
\r
104 int screenx,screeny;
\r
111 unsigned current; // foreground tiles have high bit set
\r
116 typedef struct animtilestruct
\r
120 unsigned far *mapplane;
\r
121 struct animtilestruct **prevptr,*nexttile;
\r
125 =============================================================================
\r
129 =============================================================================
\r
133 long lasttimecount;
\r
135 boolean compatability; // crippled refresh for wierdo SVGAs
\r
137 unsigned mapwidth,mapheight,mapbyteswide,mapwordswide
\r
138 ,mapbytesextra,mapwordsextra;
\r
139 unsigned mapbwidthtable[MAXMAPHEIGHT];
\r
142 // Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow
\r
143 // for fractional movement and acceleration.
\r
145 // Tiles : Tile offsets from the upper left corner of the current map.
\r
147 // Screen : Graphics level offsets from map origin, x in bytes, y in pixels.
\r
148 // originxscreen is the same spot as originxtile, just with extra precision
\r
149 // so graphics don't need to be done in tile boundaries.
\r
152 unsigned originxglobal,originyglobal;
\r
153 unsigned originxtile,originytile;
\r
154 unsigned originxscreen,originyscreen;
\r
155 unsigned originmap;
\r
156 unsigned originxmin,originxmax,originymin,originymax;
\r
158 unsigned masterofs;
\r
161 // Table of the offsets from bufferofs of each tile spot in the
\r
162 // view port. The extra wide tile should never be drawn, but the space
\r
163 // is needed to account for the extra 0 in the update arrays. Built by
\r
167 unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];
\r
168 unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH];
\r
170 unsigned uwidthtable[PORTTILESHIGH]; // lookup instead of multiply
\r
172 byte update[2][UPDATESIZE];
\r
173 byte *updateptr,*baseupdateptr, // current start of update window
\r
175 *baseupdatestart[2];
\r
178 =============================================================================
\r
182 =============================================================================
\r
185 static char scratch[20],str[80];
\r
187 tiletype allanims[MAXANIMTYPES];
\r
188 unsigned numanimchains;
\r
190 void (*refreshvector) (void);
\r
192 unsigned screenstart[3] =
\r
193 {0,SCREENSPACE,SCREENSPACE*2};
\r
195 unsigned xpanmask; // prevent panning to odd pixels
\r
197 unsigned screenpage; // screen currently being displayed
\r
198 unsigned otherpage;
\r
201 spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],
\r
204 animtiletype animarray[MAXANIMTILES],*animhead,*animfreeptr;
\r
208 eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];
\r
210 int hscrollblocks,vscrollblocks;
\r
211 int hscrolledge[MAXSCROLLEDGES],vscrolledge[MAXSCROLLEDGES];
\r
214 =============================================================================
\r
218 =============================================================================
\r
221 void RFL_NewTile (unsigned updateoffset);
\r
222 void RFL_MaskForegroundTiles (void);
\r
223 void RFL_UpdateTiles (void);
\r
225 void RFL_BoundScroll (int x, int y);
\r
226 void RFL_CalcOriginStuff (long x, long y);
\r
227 void RFL_ClearScrollBlocks (void);
\r
228 void RFL_InitSpriteList (void);
\r
229 void RFL_InitAnimList (void);
\r
230 void RFL_CheckForAnimTile (unsigned x, unsigned y);
\r
231 void RFL_AnimateTiles (void);
\r
232 void RFL_RemoveAnimsOnX (unsigned x);
\r
233 void RFL_RemoveAnimsOnY (unsigned y);
\r
234 void RFL_EraseBlocks (void);
\r
235 void RFL_UpdateSprites (void);
\r
239 =============================================================================
\r
241 GRMODE INDEPENDANT ROUTINES
\r
243 =============================================================================
\r
248 =====================
\r
252 =====================
\r
255 static char *ParmStrings[] = {"comp",""};
\r
257 void RF_Startup (void)
\r
260 unsigned *blockstart;
\r
262 if (grmode == EGAGR)
\r
263 for (i = 1;i < _argc;i++)
\r
264 if (US_CheckParm(_argv[i],ParmStrings) == 0)
\r
266 compatability = true;
\r
270 for (i=0;i<PORTTILESHIGH;i++)
\r
271 uwidthtable[i] = UPDATEWIDE*i;
\r
273 originxmin = originymin = MAPBORDER*TILEGLOBAL;
\r
275 eraselistptr[0] = &eraselist[0][0];
\r
276 eraselistptr[1] = &eraselist[1][0];
\r
280 if (grmode == EGAGR)
\r
284 baseupdatestart[0] = &update[0][UPDATESPARESIZE];
\r
285 baseupdatestart[1] = &update[1][UPDATESPARESIZE];
\r
289 displayofs = screenstart[screenpage];
\r
290 bufferofs = screenstart[otherpage];
\r
291 masterofs = screenstart[2];
\r
293 updateptr = baseupdatestart[otherpage];
\r
295 blockstart = &blockstarts[0];
\r
296 for (y=0;y<UPDATEHIGH;y++)
\r
297 for (x=0;x<UPDATEWIDE;x++)
\r
298 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
300 xpanmask = 6; // dont pan to odd pixels
\r
303 else if (grmode == CGAGR)
\r
307 updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];
\r
310 masterofs = 0x8000;
\r
312 blockstart = &blockstarts[0];
\r
313 for (y=0;y<UPDATEHIGH;y++)
\r
314 for (x=0;x<UPDATEWIDE;x++)
\r
315 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
323 =====================
\r
327 =====================
\r
330 void RF_Shutdown (void)
\r
335 //===========================================================================
\r
339 =====================
\r
343 = Sets bufferofs,displayofs, and masterofs to regular values, for the
\r
344 = occasions when you have moved them around manually
\r
346 =====================
\r
349 void RF_FixOfs (void)
\r
351 if (grmode == EGAGR)
\r
355 panx = pany = pansx = pansy = panadjust = 0;
\r
356 displayofs = screenstart[screenpage];
\r
357 bufferofs = screenstart[otherpage];
\r
358 masterofs = screenstart[2];
\r
359 VW_SetScreen (displayofs,0);
\r
364 masterofs = 0x8000;
\r
369 //===========================================================================
\r
372 =====================
\r
376 = Makes some convienient calculations based on maphead->
\r
378 =====================
\r
381 void RF_NewMap (void)
\r
384 unsigned spot,*table;
\r
386 mapwidth = mapheaderseg[mapon]->width;
\r
387 mapbyteswide = 2*mapwidth;
\r
388 mapheight = mapheaderseg[mapon]->height;
\r
389 mapwordsextra = mapwidth-PORTTILESWIDE;
\r
390 mapbytesextra = 2*mapwordsextra;
\r
393 // make a lookup table for the maps left edge
\r
395 if (mapheight > MAXMAPHEIGHT)
\r
396 Quit ("RF_NewMap: Map too tall!");
\r
398 for (i=0;i<mapheight;i++)
\r
400 mapbwidthtable[i] = spot;
\r
401 spot += mapbyteswide;
\r
405 // fill in updatemapofs with the new width info
\r
407 table = &updatemapofs[0];
\r
408 for (y=0;y<PORTTILESHIGH;y++)
\r
409 for (x=0;x<UPDATEWIDE;x++)
\r
410 *table++ = mapbwidthtable[y]+x*2;
\r
413 // the y max value clips off the bottom half of a tile so a map that is
\r
414 // 13 + MAPBORDER*2 tile high will not scroll at all vertically
\r
416 originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;
\r
417 originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;
\r
418 if (originxmax<originxmin) // for very small maps
\r
419 originxmax=originxmin;
\r
420 if (originymax<originymin)
\r
421 originymax=originymin;
\r
424 // clear out the lists
\r
426 RFL_InitSpriteList ();
\r
427 RFL_InitAnimList ();
\r
428 RFL_ClearScrollBlocks ();
\r
429 RF_SetScrollBlock (0,MAPBORDER-1,true);
\r
430 RF_SetScrollBlock (0,mapheight-MAPBORDER,true);
\r
431 RF_SetScrollBlock (MAPBORDER-1,0,false);
\r
432 RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);
\r
435 lasttimecount = TimeCount; // setup for adaptive timing
\r
439 //===========================================================================
\r
442 ==========================
\r
444 = RF_MarkTileGraphics
\r
446 = Goes through mapplane[0/1] and marks all background/foreground tiles
\r
447 = needed, then follows all animation sequences to make sure animated
\r
448 = tiles get all the stages. Every unique animating tile is given an
\r
449 = entry in allanims[], so every instance of that tile will animate at the
\r
450 = same rate. The info plane for each animating tile will hold a pointer
\r
451 = into allanims[], therefore you can't have both an animating foreground
\r
452 = and background tile in the same spot!
\r
454 ==========================
\r
457 void RF_MarkTileGraphics (void)
\r
460 int tile,next,anims,change;
\r
461 unsigned far *start,far *end,far *info;
\r
462 unsigned i,tilehigh;
\r
463 char str[80],str2[10];
\r
465 memset (allanims,0,sizeof(allanims));
\r
468 size = mapwidth*mapheight;
\r
471 // background plane
\r
473 start = mapsegs[0];
\r
479 if (tile>=0) // <0 is a tile that is never drawn
\r
481 CA_MarkGrChunk(STARTTILE16+tile);
\r
482 if (tinf[ANIM+tile])
\r
484 // this tile will animated
\r
486 if (tinf[SPEED+tile])
\r
488 if (!tinf[ANIM+tile])
\r
490 strcpy (str,"RF_MarkTileGraphics: Background anim of 0:");
\r
491 itoa (tile,str2,10);
\r
495 for (i=0;i<numanimchains;i++)
\r
496 if (allanims[i].current == tile)
\r
498 *info = (unsigned)&allanims[i];
\r
502 // new chain of animating tiles
\r
504 if (i>=MAXANIMTYPES)
\r
505 Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");
\r
506 allanims[i].current = tile;
\r
507 allanims[i].count = tinf[SPEED+tile];
\r
508 *info = (unsigned)&allanims[i];
\r
513 change = (signed char)(tinf[ANIM+tile]);
\r
514 next = tile+change;
\r
515 while (change && next != tile)
\r
517 CA_MarkGrChunk(STARTTILE16+next);
\r
518 change = (signed char)(tinf[ANIM+next]);
\r
522 strcpy (str,"RF_MarkTileGraphics: Unending background animation:");
\r
523 itoa (next,str2,10);
\r
533 } while (start<end);
\r
536 // foreground plane
\r
538 start = mapsegs[1];
\r
544 if (tile>=0) // <0 is a tile that is never drawn
\r
546 CA_MarkGrChunk(STARTTILE16M+tile);
\r
547 if (tinf[MANIM+tile])
\r
549 // this tile will animated
\r
551 if (tinf[MSPEED+tile])
\r
553 if (!tinf[MANIM+tile])
\r
555 strcpy (str,"RF_MarkTileGraphics: Foreground anim of 0:");
\r
556 itoa (tile,str2,10);
\r
560 tilehigh = tile | 0x8000; // foreground tiles have high bit
\r
561 for (i=0;i<numanimchains;i++)
\r
562 if (allanims[i].current == tilehigh)
\r
564 *info = (unsigned)&allanims[i];
\r
568 // new chain of animating tiles
\r
570 if (i>=MAXANIMTYPES)
\r
571 Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");
\r
572 allanims[i].current = tilehigh;
\r
573 allanims[i].count = tinf[MSPEED+tile];
\r
575 *info = (unsigned)&allanims[i];
\r
580 change = (signed char)(tinf[MANIM+tile]);
\r
581 next = tile+change;
\r
582 while (change && next != tile)
\r
584 CA_MarkGrChunk(STARTTILE16M+next);
\r
585 change = (signed char)(tinf[MANIM+next]);
\r
589 strcpy (str,"RF_MarkTileGraphics: Unending foreground animation:");
\r
590 itoa (next,str2,10);
\r
600 } while (start<end);
\r
604 //===========================================================================
\r
608 =========================
\r
612 = Call to clear out the entire animating tile list and return all of them to
\r
615 =========================
\r
618 void RFL_InitAnimList (void)
\r
622 animfreeptr = &animarray[0];
\r
624 for (i=0;i<MAXANIMTILES-1;i++)
\r
625 animarray[i].nexttile = &animarray[i+1];
\r
627 animarray[i].nexttile = NULL;
\r
629 animhead = NULL; // nothing in list
\r
634 ====================
\r
636 = RFL_CheckForAnimTile
\r
638 ====================
\r
641 void RFL_CheckForAnimTile (unsigned x, unsigned y)
\r
643 unsigned tile,offset,speed,lasttime,thistime,timemissed;
\r
645 animtiletype *anim,*next;
\r
647 // the info plane of each animating tile has a near pointer into allanims[]
\r
648 // which gives the current state of all concurrently animating tiles
\r
650 offset = mapbwidthtable[y]/2+x;
\r
655 map = mapsegs[0]+offset;
\r
657 if (tinf[ANIM+tile] && tinf[SPEED+tile])
\r
660 Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
\r
661 anim = animfreeptr;
\r
662 animfreeptr = animfreeptr->nexttile;
\r
663 next = animhead; // stick it at the start of the list
\r
666 next->prevptr = &anim->nexttile;
\r
667 anim->nexttile = next;
\r
668 anim->prevptr = &animhead;
\r
673 anim->mapplane = map;
\r
674 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
680 map = mapsegs[1]+offset;
\r
682 if (tinf[MANIM+tile] && tinf[MSPEED+tile])
\r
685 Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
\r
686 anim = animfreeptr;
\r
687 animfreeptr = animfreeptr->nexttile;
\r
688 next = animhead; // stick it at the start of the list
\r
691 next->prevptr = &anim->nexttile;
\r
692 anim->nexttile = next;
\r
693 anim->prevptr = &animhead;
\r
698 anim->mapplane = map;
\r
699 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
706 ====================
\r
708 = RFL_RemoveAnimsOnX
\r
710 ====================
\r
713 void RFL_RemoveAnimsOnX (unsigned x)
\r
715 animtiletype *current,*next;
\r
717 current = animhead;
\r
720 if (current->x == x)
\r
722 *(void **)current->prevptr = current->nexttile;
\r
723 if (current->nexttile)
\r
724 current->nexttile->prevptr = current->prevptr;
\r
725 next = current->nexttile;
\r
726 current->nexttile = animfreeptr;
\r
727 animfreeptr = current;
\r
731 current = current->nexttile;
\r
737 ====================
\r
739 = RFL_RemoveAnimsOnY
\r
741 ====================
\r
744 void RFL_RemoveAnimsOnY (unsigned y)
\r
746 animtiletype *current,*next;
\r
748 current = animhead;
\r
751 if (current->y == y)
\r
753 *(void **)current->prevptr = current->nexttile;
\r
754 if (current->nexttile)
\r
755 current->nexttile->prevptr = current->prevptr;
\r
756 next = current->nexttile;
\r
757 current->nexttile = animfreeptr;
\r
758 animfreeptr = current;
\r
762 current = current->nexttile;
\r
768 ====================
\r
770 = RFL_RemoveAnimsInBlock
\r
772 ====================
\r
775 void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)
\r
777 animtiletype *current,*next;
\r
779 current = animhead;
\r
782 if (current->x - x < width && current->y - y < height)
\r
784 *(void **)current->prevptr = current->nexttile;
\r
785 if (current->nexttile)
\r
786 current->nexttile->prevptr = current->prevptr;
\r
787 next = current->nexttile;
\r
788 current->nexttile = animfreeptr;
\r
789 animfreeptr = current;
\r
793 current = current->nexttile;
\r
799 ====================
\r
803 ====================
\r
806 void RFL_AnimateTiles (void)
\r
808 animtiletype *current;
\r
809 unsigned updateofs,tile,x,y;
\r
813 // animate the lists of tiles
\r
815 anim = &allanims[0];
\r
816 while (anim->current)
\r
819 while ( anim->count < 1)
\r
821 if (anim->current & 0x8000)
\r
823 tile = anim->current & 0x7fff;
\r
824 tile += (signed char)tinf[MANIM+tile];
\r
825 anim->count += tinf[MSPEED+tile];
\r
830 tile = anim->current;
\r
831 tile += (signed char)tinf[ANIM+tile];
\r
832 anim->count += tinf[SPEED+tile];
\r
834 anim->current = tile;
\r
841 // traverse the list of animating tiles
\r
843 current = animhead;
\r
846 tile =current->chain->current;
\r
847 if ( tile != current->tile)
\r
849 // tile has animated
\r
851 // remove tile from master screen cache,
\r
852 // change a tile to its next state, set the structure up for
\r
853 // next animation, and post an update region to both update pages
\r
855 current->tile = tile;
\r
857 *(current->mapplane) = tile & 0x7fff; // change in map
\r
859 x = current->x-originxtile;
\r
860 y = current->y-originytile;
\r
862 if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)
\r
863 Quit ("RFL_AnimateTiles: Out of bounds!");
\r
865 updateofs = uwidthtable[y] + x;
\r
866 RFL_NewTile(updateofs); // puts "1"s in both pages
\r
868 current = current->nexttile;
\r
873 //===========================================================================
\r
876 =========================
\r
878 = RFL_InitSpriteList
\r
880 = Call to clear out the entire sprite list and return all of them to
\r
883 =========================
\r
886 void RFL_InitSpriteList (void)
\r
890 spritefreeptr = &spritearray[0];
\r
891 for (i=0;i<MAXSPRITES-1;i++)
\r
892 spritearray[i].nextsprite = &spritearray[i+1];
\r
894 spritearray[i].nextsprite = NULL;
\r
896 // NULL in all priority levels
\r
898 memset (prioritystart,0,sizeof(prioritystart));
\r
901 //===========================================================================
\r
906 = RFL_CalcOriginStuff
\r
908 = Calculate all the global variables for a new position
\r
909 = Long parms so position can be clipped to a maximum near 64k
\r
914 void RFL_CalcOriginStuff (long x, long y)
\r
918 originxtile = originxglobal>>G_T_SHIFT;
\r
919 originytile = originyglobal>>G_T_SHIFT;
\r
920 originxscreen = originxtile<<SX_T_SHIFT;
\r
921 originyscreen = originytile<<SY_T_SHIFT;
\r
922 originmap = mapbwidthtable[originytile] + originxtile*2;
\r
924 #if GRMODE == EGAGR
\r
925 panx = (originxglobal>>G_P_SHIFT) & 15;
\r
927 pany = pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
928 panadjust = panx/8 + ylookup[pany];
\r
931 #if GRMODE == CGAGR
\r
932 panx = (originxglobal>>G_P_SHIFT) & 15;
\r
934 pany = pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
935 panadjust = pansx/4 + ylookup[pansy];
\r
944 = RFL_ClearScrollBlocks
\r
949 void RFL_ClearScrollBlocks (void)
\r
951 hscrollblocks = vscrollblocks = 0;
\r
958 = RF_SetScrollBlock
\r
960 = Sets a horizontal or vertical scroll block
\r
961 = a horizontal block is ----, meaning it blocks up/down movement
\r
966 void RF_SetScrollBlock (int x, int y, boolean horizontal)
\r
970 hscrolledge[hscrollblocks] = y;
\r
971 if (hscrollblocks++ == MAXSCROLLEDGES)
\r
972 Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks");
\r
976 vscrolledge[vscrollblocks] = x;
\r
977 if (vscrollblocks++ == MAXSCROLLEDGES)
\r
978 Quit ("RF_SetScrollBlock: Too many vertical scroll blocks");
\r
988 = Bound a given x/y movement to scroll blocks
\r
993 void RFL_BoundScroll (int x, int y)
\r
995 int check,newxtile,newytile;
\r
997 originxglobal += x;
\r
998 originyglobal += y;
\r
1000 newxtile= originxglobal >> G_T_SHIFT;
\r
1001 newytile = originyglobal >> G_T_SHIFT;
\r
1005 newxtile+=SCREENTILESWIDE;
\r
1006 for (check=0;check<vscrollblocks;check++)
\r
1007 if (vscrolledge[check] == newxtile)
\r
1009 originxglobal = originxglobal&0xff00;
\r
1015 for (check=0;check<vscrollblocks;check++)
\r
1016 if (vscrolledge[check] == newxtile)
\r
1018 originxglobal = (originxglobal&0xff00)+0x100;
\r
1026 newytile+=SCREENTILESHIGH;
\r
1027 for (check=0;check<hscrollblocks;check++)
\r
1028 if (hscrolledge[check] == newytile)
\r
1030 originyglobal = originyglobal&0xff00;
\r
1036 for (check=0;check<hscrollblocks;check++)
\r
1037 if (hscrolledge[check] == newytile)
\r
1039 originyglobal = (originyglobal&0xff00)+0x100;
\r
1045 RFL_CalcOriginStuff (originxglobal, originyglobal);
\r
1049 //===========================================================================
\r
1052 =====================
\r
1054 = RF_SetRefreshHook
\r
1056 =====================
\r
1059 void RF_SetRefreshHook (void (*func) (void) )
\r
1061 refreshvector = func;
\r
1065 //===========================================================================
\r
1072 = Bring a new row of tiles onto the port, spawning animating tiles
\r
1077 void RFL_NewRow (int dir)
\r
1079 unsigned count,updatespot,updatestep;
\r
1080 int x,y,xstep,ystep;
\r
1084 case 0: // top row
\r
1091 count = PORTTILESWIDE;
\r
1094 case 1: // right row
\r
1095 updatespot = PORTTILESWIDE-1;
\r
1096 updatestep = UPDATEWIDE;
\r
1097 x = originxtile + PORTTILESWIDE-1;
\r
1101 count = PORTTILESHIGH;
\r
1104 case 2: // bottom row
\r
1105 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1108 y = originytile + PORTTILESHIGH-1;
\r
1111 count = PORTTILESWIDE;
\r
1114 case 3: // left row
\r
1116 updatestep = UPDATEWIDE;
\r
1121 count = PORTTILESHIGH;
\r
1124 Quit ("RFL_NewRow: Bad dir!");
\r
1129 RFL_NewTile(updatespot);
\r
1130 RFL_CheckForAnimTile (x,y);
\r
1131 updatespot+=updatestep;
\r
1137 //===========================================================================
\r
1140 =====================
\r
1144 =====================
\r
1147 void RF_ForceRefresh (void)
\r
1149 RF_NewPosition (originxglobal,originyglobal);
\r
1154 //===========================================================================
\r
1157 =====================
\r
1161 = Copies a block of tiles (all three planes) from one point
\r
1162 = in the map to another, accounting for animating tiles
\r
1164 =====================
\r
1167 void RF_MapToMap (unsigned srcx, unsigned srcy,
\r
1168 unsigned destx, unsigned desty,
\r
1169 unsigned width, unsigned height)
\r
1172 unsigned source,destofs,xspot,yspot;
\r
1173 unsigned linedelta,p0,p1,p2,updatespot;
\r
1174 unsigned far *source0, far *source1, far *source2;
\r
1175 unsigned far *dest0, far *dest1, far *dest2;
\r
1178 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1180 source = mapbwidthtable[srcy]/2 + srcx;
\r
1182 source0 = mapsegs[0]+source;
\r
1183 source1 = mapsegs[1]+source;
\r
1184 source2 = mapsegs[2]+source;
\r
1186 destofs = mapbwidthtable[desty]/2 + destx;
\r
1187 destofs -= source;
\r
1189 linedelta = mapwidth - width;
\r
1191 for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)
\r
1192 for (x=0;x<width;x++,source0++,source1++,source2++)
\r
1198 dest0 = source0 + destofs;
\r
1199 dest1 = source1 + destofs;
\r
1200 dest2 = source2 + destofs;
\r
1203 // only make a new tile if it is different
\r
1205 if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)
\r
1216 // if tile is on the view port
\r
1218 xspot = destx+x-originxtile;
\r
1219 yspot = desty+y-originytile;
\r
1220 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1224 updatespot = uwidthtable[yspot]+xspot;
\r
1225 RFL_NewTile(updatespot);
\r
1227 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1232 //===========================================================================
\r
1236 =====================
\r
1240 = Copies a string of tiles from main memory to the map,
\r
1241 = accounting for animating tiles
\r
1243 =====================
\r
1246 void RF_MemToMap (unsigned far *source, unsigned plane,
\r
1247 unsigned destx, unsigned desty,
\r
1248 unsigned width, unsigned height)
\r
1251 unsigned xspot,yspot;
\r
1252 unsigned linedelta,updatespot;
\r
1253 unsigned far *dest,old,new;
\r
1256 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1258 dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;
\r
1260 linedelta = mapwidth - width;
\r
1262 for (y=0;y<height;y++,dest+=linedelta)
\r
1263 for (x=0;x<width;x++)
\r
1276 xspot = destx+x-originxtile;
\r
1277 yspot = desty+y-originytile;
\r
1278 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1282 updatespot = uwidthtable[yspot]+xspot;
\r
1283 RFL_NewTile(updatespot);
\r
1285 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1290 //===========================================================================
\r
1294 =====================
\r
1296 = RFL_BoundNewOrigin
\r
1298 = Copies a string of tiles from main memory to the map,
\r
1299 = accounting for animating tiles
\r
1301 =====================
\r
1304 void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)
\r
1309 // calculate new origin related globals
\r
1311 if (orgx<originxmin)
\r
1313 else if (orgx>originxmax)
\r
1316 if (orgy<originymin)
\r
1318 else if (orgy>originymax)
\r
1321 originxtile = orgx>>G_T_SHIFT;
\r
1322 originytile = orgy>>G_T_SHIFT;
\r
1324 for (check=0;check<vscrollblocks;check++)
\r
1326 edge = vscrolledge[check];
\r
1327 if (edge>=originxtile && edge <=originxtile+10)
\r
1329 orgx = (edge+1)*TILEGLOBAL;
\r
1332 if (edge>=originxtile+11 && edge <=originxtile+20)
\r
1334 orgx = (edge-20)*TILEGLOBAL;
\r
1339 for (check=0;check<hscrollblocks;check++)
\r
1341 edge = hscrolledge[check];
\r
1342 if (edge>=originytile && edge <=originytile+6)
\r
1344 orgy = (edge+1)*TILEGLOBAL;
\r
1347 if (edge>=originytile+7 && edge <=originytile+13)
\r
1349 orgy = (edge-13)*TILEGLOBAL;
\r
1355 RFL_CalcOriginStuff (orgx,orgy);
\r
1359 //===========================================================================
\r
1362 =====================
\r
1366 = Posts erase blocks to clear a certain area of the screen to the master
\r
1367 = screen, to erase text or something draw directly to the screen
\r
1369 = Parameters in pixels, but erasure is byte bounded
\r
1371 =====================
\r
1374 void RF_ClearBlock (int x, int y, int width, int height)
\r
1376 eraseblocktype block;
\r
1378 #if GRMODE == EGAGR
\r
1379 block.screenx = x/8+originxscreen;
\r
1380 block.screeny = y+originyscreen;
\r
1381 block.width = (width+(x&7)+7)/8;
\r
1382 block.height = height;
\r
1383 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1384 memcpy (eraselistptr[1]++,&block,sizeof(block));
\r
1387 #if GRMODE == CGAGR
\r
1388 block.screenx = x/4+originxscreen;
\r
1389 block.screeny = y+originyscreen;
\r
1390 block.width = (width+(x&3)+3)/4;
\r
1391 block.height = height;
\r
1392 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1397 //===========================================================================
\r
1400 =====================
\r
1404 = Causes a number of tiles to be redrawn to the master screen and updated
\r
1406 = Parameters in pixels, but erasure is tile bounded
\r
1408 =====================
\r
1411 void RF_RedrawBlock (int x, int y, int width, int height)
\r
1413 int xx,yy,xl,xh,yl,yh;
\r
1416 xh=(x+panx+width+15)/16;
\r
1418 yh=(y+pany+height+15)/16;
\r
1419 for (yy=yl;yy<=yh;yy++)
\r
1420 for (xx=xl;xx<=xh;xx++)
\r
1421 RFL_NewTile (yy*UPDATEWIDE+xx);
\r
1425 //===========================================================================
\r
1428 =====================
\r
1432 =====================
\r
1435 void RF_CalcTics (void)
\r
1437 long newtime,oldtimecount;
\r
1440 // calculate tics since last refresh for adaptive timing
\r
1442 if (lasttimecount > TimeCount)
\r
1443 TimeCount = lasttimecount; // if the game was paused a LONG time
\r
1445 if (DemoMode) // demo recording and playback needs
\r
1446 { // to be constant
\r
1448 // take DEMOTICS or more tics, and modify Timecount to reflect time taken
\r
1450 oldtimecount = lasttimecount;
\r
1451 while (TimeCount<oldtimecount+DEMOTICS*2)
\r
1453 lasttimecount = oldtimecount + DEMOTICS;
\r
1454 TimeCount = lasttimecount + DEMOTICS;
\r
1460 // non demo, so report actual time
\r
1464 newtime = TimeCount;
\r
1465 tics = newtime-lasttimecount;
\r
1466 } while (tics<MINTICS);
\r
1467 lasttimecount = newtime;
\r
1470 strcpy (scratch,"\tTics:");
\r
1471 itoa (tics,str,10);
\r
1472 strcat (scratch,str);
\r
1473 strcat (scratch,"\n");
\r
1474 write (profilehandle,scratch,strlen(scratch));
\r
1479 TimeCount -= (tics-MAXTICS);
\r
1486 =============================================================================
\r
1488 EGA specific routines
\r
1490 =============================================================================
\r
1493 #if GRMODE == EGAGR
\r
1496 =====================
\r
1498 = RF_FindFreeBuffer
\r
1500 = Finds the start of unused, non visable buffer space
\r
1502 =====================
\r
1505 unsigned RF_FindFreeBuffer (void)
\r
1507 unsigned spot,i,j;
\r
1512 spot = screenstart[i]+SCREENSPACE;
\r
1515 if (spot == screenstart[j])
\r
1524 return 0; // never get here...
\r
1527 //===========================================================================
\r
1530 =====================
\r
1532 = RF_NewPosition EGA
\r
1534 =====================
\r
1537 void RF_NewPosition (unsigned x, unsigned y)
\r
1540 byte *page0ptr,*page1ptr;
\r
1541 unsigned updatenum;
\r
1543 RFL_BoundNewOrigin (x,y);
\r
1545 // clear out all animating tiles
\r
1547 RFL_InitAnimList ();
\r
1550 // set up the new update arrays at base position
\r
1552 updatestart[0] = baseupdatestart[0];
\r
1553 updatestart[1] = baseupdatestart[1];
\r
1554 updateptr = updatestart[otherpage];
\r
1556 page0ptr = updatestart[0]+PORTTILESWIDE; // used to stick "0"s after rows
\r
1557 page1ptr = updatestart[1]+PORTTILESWIDE;
\r
1559 updatenum = 0; // start at first visable tile
\r
1561 for (my=0;my<PORTTILESHIGH;my++)
\r
1563 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
1565 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
1566 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
1570 *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles
\r
1571 page0ptr+=(PORTTILESWIDE+1);
\r
1572 page1ptr+=(PORTTILESWIDE+1);
\r
1574 *(word *)(page0ptr-PORTTILESWIDE)
\r
1575 = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
1578 //===========================================================================
\r
1582 =====================
\r
1586 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
1587 = scroll if needed. If the scroll distance is greater than one tile, the
\r
1588 = entire screen will be redrawn (this could be generalized, but scrolling
\r
1589 = more than one tile per refresh is a bad idea!).
\r
1591 =====================
\r
1594 void RF_Scroll (int x, int y)
\r
1596 long neworgx,neworgy;
\r
1597 int i,deltax,deltay,absdx,absdy;
\r
1598 int oldxt,oldyt,move,yy;
\r
1599 unsigned updatespot;
\r
1600 byte *update0,*update1;
\r
1601 unsigned oldpanx,oldpanadjust,oldscreen,newscreen,screencopy;
\r
1604 oldxt = originxtile;
\r
1605 oldyt = originytile;
\r
1606 oldpanadjust = panadjust;
\r
1609 RFL_BoundScroll (x,y);
\r
1611 deltax = originxtile - oldxt;
\r
1612 absdx = abs(deltax);
\r
1613 deltay = originytile - oldyt;
\r
1614 absdy = abs(deltay);
\r
1616 if (absdx>1 || absdy>1)
\r
1619 // scrolled more than one tile, so start from scratch
\r
1621 RF_NewPosition(originxglobal,originyglobal);
\r
1625 if (!absdx && !absdy)
\r
1626 return; // the screen has not scrolled an entire tile
\r
1630 // adjust screens and handle SVGA crippled compatability mode
\r
1632 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
1635 screenstart[i]+= screenmove;
\r
1636 if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )
\r
1639 // move the screen to the opposite end of the buffer
\r
1641 screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;
\r
1642 oldscreen = screenstart[i] - screenmove;
\r
1643 newscreen = oldscreen + screencopy;
\r
1644 screenstart[i] = newscreen + screenmove;
\r
1645 VW_ScreenToScreen (oldscreen,newscreen,
\r
1646 PORTTILESWIDE*2,PORTTILESHIGH*16);
\r
1648 if (i==screenpage)
\r
1649 VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);
\r
1652 bufferofs = screenstart[otherpage];
\r
1653 displayofs = screenstart[screenpage];
\r
1654 masterofs = screenstart[2];
\r
1658 // float the update regions
\r
1662 move += UPDATEWIDE;
\r
1663 else if (deltay==-1)
\r
1664 move -= UPDATEWIDE;
\r
1666 updatestart[0]+=move;
\r
1667 updatestart[1]+=move;
\r
1670 // draw the new tiles just scrolled on to the master screen, and
\r
1671 // mark them as needing to be copied to each screen next refreshes
\r
1672 // Make sure a zero is at the end of each row in update
\r
1679 RFL_NewRow (1); // new right row
\r
1680 RFL_RemoveAnimsOnX (originxtile-1);
\r
1684 RFL_NewRow (3); // new left row
\r
1685 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
1688 update0 = updatestart[0]+PORTTILESWIDE;
\r
1689 update1 = updatestart[1]+PORTTILESWIDE;
\r
1690 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
1692 *update0 = *update1 = 0; // drop a 0 at end of each row
\r
1693 update0+=UPDATEWIDE;
\r
1694 update1+=UPDATEWIDE;
\r
1698 //----------------
\r
1704 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1705 RFL_NewRow (2); // new bottom row
\r
1706 RFL_RemoveAnimsOnY (originytile-1);
\r
1711 RFL_NewRow (0); // new top row
\r
1712 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
1715 *(updatestart[0]+updatespot+PORTTILESWIDE) =
\r
1716 *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;
\r
1719 //----------------
\r
1722 // place a new terminator
\r
1724 update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1725 update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1726 *update0++ = *update1++ = 0;
\r
1727 *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;
\r
1730 //===========================================================================
\r
1733 =====================
\r
1735 = RF_PlaceSprite EGA
\r
1737 =====================
\r
1740 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
1741 unsigned spritenumber, drawtype draw, int priority)
\r
1743 spritelisttype register *sprite,*next;
\r
1744 spritetabletype far *spr;
\r
1745 spritetype _seg *block;
\r
1746 unsigned shift,pixx;
\r
1747 char str[80],str2[10];
\r
1749 if (!spritenumber || spritenumber == (unsigned)-1)
\r
1751 RF_RemoveSprite (user);
\r
1755 sprite = (spritelisttype *)*user;
\r
1759 // sprite allready exists in the list, so we can use it's block
\r
1762 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1763 // both pages may not need to be erased if the sprite just changed last frame
\r
1765 if (sprite->updatecount<2)
\r
1767 if (!sprite->updatecount)
\r
1768 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1769 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1772 if (priority != sprite->priority)
\r
1774 // sprite mvoed to another priority, so unlink the old one and
\r
1775 // relink it in the new priority
\r
1777 next = sprite->nextsprite; // cut old links
\r
1779 next->prevptr = sprite->prevptr;
\r
1780 *sprite->prevptr = next;
\r
1786 // this is a brand new sprite, so allocate a block from the array
\r
1788 if (!spritefreeptr)
\r
1789 Quit ("RF_PlaceSprite: No free spots in spritearray!");
\r
1791 sprite = spritefreeptr;
\r
1792 spritefreeptr = spritefreeptr->nextsprite;
\r
1795 next = prioritystart[priority]; // stick it in new spot
\r
1797 next->prevptr = &sprite->nextsprite;
\r
1798 sprite->nextsprite = next;
\r
1799 prioritystart[priority] = sprite;
\r
1800 sprite->prevptr = &prioritystart[priority];
\r
1804 // write the new info to the sprite
\r
1806 spr = &spritetable[spritenumber-STARTSPRITES];
\r
1807 block = (spritetype _seg *)grsegs[spritenumber];
\r
1811 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");
\r
1812 itoa (spritenumber,str2,10);
\r
1813 strcat (str,str2);
\r
1817 globaly+=spr->orgy;
\r
1818 globalx+=spr->orgx;
\r
1820 pixx = globalx >> G_SY_SHIFT;
\r
1821 shift = (pixx&7)/2;
\r
1823 sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);
\r
1824 sprite->screeny = globaly >> G_SY_SHIFT;
\r
1825 sprite->width = block->width[shift];
\r
1826 sprite->height = spr->height;
\r
1827 sprite->grseg = spritenumber;
\r
1828 sprite->sourceofs = block->sourceoffset[shift];
\r
1829 sprite->planesize = block->planesize[shift];
\r
1830 sprite->draw = draw;
\r
1831 sprite->priority = priority;
\r
1832 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
1833 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
1834 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
1835 - sprite->tilex + 1;
\r
1836 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
1837 - sprite->tiley + 1;
\r
1839 sprite->updatecount = 2; // draw on next two refreshes
\r
1841 // save the sprite pointer off in the user's pointer so it can be moved
\r
1847 //===========================================================================
\r
1850 =====================
\r
1852 = RF_RemoveSprite EGA
\r
1854 =====================
\r
1857 void RF_RemoveSprite (void **user)
\r
1859 spritelisttype *sprite,*next;
\r
1861 sprite = (spritelisttype *)*user;
\r
1866 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1867 // both pages may not need to be erased if the sprite just changed last frame
\r
1869 if (sprite->updatecount<2)
\r
1871 if (!sprite->updatecount)
\r
1872 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1873 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1877 // unlink the sprite node
\r
1879 next = sprite->nextsprite;
\r
1880 if (next) // if (!next), sprite is last in chain
\r
1881 next->prevptr = sprite->prevptr;
\r
1882 *sprite->prevptr = next;
\r
1885 // add it back to the free list
\r
1887 sprite->nextsprite = spritefreeptr;
\r
1888 spritefreeptr = sprite;
\r
1891 // null the users pointer, so next time that actor gets placed, it will
\r
1892 // allocate a new block
\r
1899 //===========================================================================
\r
1903 ====================
\r
1905 = RFL_EraseBlocks EGA
\r
1907 = Write mode 1 should be set
\r
1909 ====================
\r
1912 void RFL_EraseBlocks (void)
\r
1914 eraseblocktype *block,*done;
\r
1915 int screenxh,screenyh;
\r
1916 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
1918 unsigned updatedelta;
\r
1919 unsigned erasecount;
\r
1925 block = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
1927 done = eraselistptr[otherpage];
\r
1929 while (block != done)
\r
1933 // clip the block to the current screen view
\r
1935 block->screenx -= originxscreen;
\r
1936 block->screeny -= originyscreen;
\r
1938 if (block->screenx < 0)
\r
1940 block->width += block->screenx;
\r
1941 if (block->width<1)
\r
1943 block->screenx = 0;
\r
1946 if (block->screeny < 0)
\r
1948 block->height += block->screeny;
\r
1949 if (block->height<1)
\r
1951 block->screeny = 0;
\r
1954 screenxh = block->screenx + block->width;
\r
1955 screenyh = block->screeny + block->height;
\r
1957 if (screenxh > EGAPORTSCREENWIDE)
\r
1959 block->width = EGAPORTSCREENWIDE-block->screenx;
\r
1960 screenxh = block->screenx + block->width;
\r
1963 if (screenyh > PORTSCREENHIGH)
\r
1965 block->height = PORTSCREENHIGH-block->screeny;
\r
1966 screenyh = block->screeny + block->height;
\r
1969 if (block->width<1 || block->height<1)
\r
1973 // erase the block by copying from the master screen
\r
1975 pos = ylookup[block->screeny]+block->screenx;
\r
1976 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
1977 block->width,block->height);
\r
1980 // put 2s in update where the block was, to force sprites to update
\r
1982 xtl = block->screenx >> SX_T_SHIFT;
\r
1983 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
1984 ytl = block->screeny >> SY_T_SHIFT;
\r
1985 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
1987 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
1988 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
1990 for (y=ytl;y<=yth;y++)
\r
1992 for (x=xtl;x<=xth;x++)
\r
1993 *updatespot++ = 2;
\r
1994 updatespot += updatedelta; // down to next line
\r
2003 eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
2005 strcpy (scratch,"\tErase:");
\r
2006 itoa (erasecount,str,10);
\r
2007 strcat (scratch,str);
\r
2008 write (profilehandle,scratch,strlen(scratch));
\r
2015 ====================
\r
2017 = RFL_UpdateSprites EGA
\r
2019 = NOTE: Implement vertical clipping!
\r
2021 ====================
\r
2024 void RFL_UpdateSprites (void)
\r
2026 spritelisttype *sprite;
\r
2027 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2030 byte *updatespot,*baseupdatespot;
\r
2031 unsigned updatedelta;
\r
2032 unsigned updatecount;
\r
2033 unsigned height,sourceofs;
\r
2039 for (priority=0;priority<PRIORITIES;priority++)
\r
2041 if (priority==MASKEDTILEPRIORITY)
\r
2042 RFL_MaskForegroundTiles ();
\r
2044 for (sprite = prioritystart[priority]; sprite ;
\r
2045 sprite = (spritelisttype *)sprite->nextsprite)
\r
2048 // see if the sprite has any visable area in the port
\r
2051 portx = sprite->screenx - originxscreen;
\r
2052 porty = sprite->screeny - originyscreen;
\r
2053 xtl = portx >> SX_T_SHIFT;
\r
2054 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2055 ytl = porty >> SY_T_SHIFT;
\r
2056 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2060 if (xth>=PORTTILESWIDE)
\r
2061 xth = PORTTILESWIDE-1;
\r
2064 if (yth>=PORTTILESHIGH)
\r
2065 yth = PORTTILESHIGH-1;
\r
2067 if (xtl>xth || ytl>yth)
\r
2071 // see if it's visable area covers any non 0 update tiles
\r
2073 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2074 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2076 if (sprite->updatecount)
\r
2078 sprite->updatecount--; // the sprite was just placed,
\r
2079 goto redraw; // so draw it for sure
\r
2082 for (y=ytl;y<=yth;y++)
\r
2084 for (x=xtl;x<=xth;x++)
\r
2085 if (*updatespot++)
\r
2087 updatespot += updatedelta; // down to next line
\r
2089 continue; // no need to update
\r
2093 // set the tiles it covers to 3, because those tiles are being
\r
2096 updatespot = baseupdatespot;
\r
2097 for (y=ytl;y<=yth;y++)
\r
2099 for (x=xtl;x<=xth;x++)
\r
2100 *updatespot++ = 3;
\r
2101 updatespot += updatedelta; // down to next line
\r
2106 height = sprite->height;
\r
2107 sourceofs = sprite->sourceofs;
\r
2110 height += porty; // clip top off
\r
2111 sourceofs -= porty*sprite->width;
\r
2114 else if (porty+height>PORTSCREENHIGH)
\r
2116 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2119 dest = bufferofs + ylookup[porty] + portx;
\r
2121 switch (sprite->draw)
\r
2124 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2125 dest,sprite->width,height,sprite->planesize);
\r
2140 strcpy (scratch,"\tSprites:");
\r
2141 itoa (updatecount,str,10);
\r
2142 strcat (scratch,str);
\r
2143 write (profilehandle,scratch,strlen(scratch));
\r
2150 =====================
\r
2154 = All routines will draw at the port at bufferofs, possibly copying from
\r
2155 = the port at masterofs. The EGA version then page flips, while the
\r
2156 = CGA version updates the screen from the buffer port.
\r
2158 = Screenpage is the currently displayed page, not the one being drawn
\r
2159 = Otherpage is the page to be worked with now
\r
2161 =====================
\r
2164 void RF_Refresh (void)
\r
2168 updateptr = updatestart[otherpage];
\r
2170 RFL_AnimateTiles (); // DEBUG
\r
2173 // update newly scrolled on tiles and animated tiles from the master screen
\r
2177 RFL_UpdateTiles ();
\r
2178 RFL_EraseBlocks ();
\r
2181 // Update is all 0 except where sprites have changed or new area has
\r
2182 // been scrolled on. Go through all sprites and update the ones that cover
\r
2183 // a non 0 update tile
\r
2186 RFL_UpdateSprites ();
\r
2189 // if the main program has a refresh hook set, call their function before
\r
2190 // displaying the new page
\r
2192 if (refreshvector)
\r
2196 // display the changed screen
\r
2198 VW_SetScreen(bufferofs+panadjust,panx & xpanmask);
\r
2201 // prepare for next refresh
\r
2203 // Set the update array to the middle position and clear it out to all "0"s
\r
2204 // with an UPDATETERMINATE at the end
\r
2206 updatestart[otherpage] = newupdate = baseupdatestart[otherpage];
\r
2210 asm mov cx,(UPDATESCREENSIZE-2)/2
\r
2211 asm mov di,[newupdate]
\r
2213 asm mov [WORD PTR es:di],UPDATETERMINATE
\r
2217 bufferofs = screenstart[otherpage];
\r
2218 displayofs = screenstart[screenpage];
\r
2221 // calculate tics since last refresh for adaptive timing
\r
2226 #endif // GRMODE == EGAGR
\r
2229 =============================================================================
\r
2231 CGA specific routines
\r
2233 =============================================================================
\r
2236 #if GRMODE == CGAGR
\r
2240 =====================
\r
2242 = RF_NewPosition CGA
\r
2244 =====================
\r
2247 void RF_NewPosition (unsigned x, unsigned y)
\r
2251 unsigned updatenum;
\r
2253 RFL_BoundNewOrigin (x,y);
\r
2256 // clear out all animating tiles
\r
2258 RFL_InitAnimList ();
\r
2261 // set up the new update arrays at base position
\r
2263 updateptr = baseupdateptr;
\r
2265 spotptr = updateptr + PORTTILESWIDE; // used to stick "0"s after rows
\r
2267 updatenum = 0; // start at first visable tile
\r
2269 for (my=0;my<PORTTILESHIGH;my++)
\r
2271 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
2273 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
2274 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
2278 *spotptr = 0; // set a 0 at end of a line of tiles
\r
2279 spotptr +=(PORTTILESWIDE+1);
\r
2281 *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
2286 =====================
\r
2290 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
2291 = scroll if needed. If the scroll distance is greater than one tile, the
\r
2292 = entire screen will be redrawn (this could be generalized, but scrolling
\r
2293 = more than one tile per refresh is a bad idea!).
\r
2295 =====================
\r
2298 void RF_Scroll (int x, int y)
\r
2300 long neworgx,neworgy;
\r
2301 int i,deltax,deltay,absdx,absdy;
\r
2302 int oldxt,oldyt,move,yy;
\r
2303 unsigned updatespot;
\r
2305 unsigned oldoriginmap,oldscreen,newscreen,screencopy;
\r
2308 oldxt = originxtile;
\r
2309 oldyt = originytile;
\r
2311 RFL_BoundScroll (x,y);
\r
2313 deltax = originxtile - oldxt;
\r
2314 absdx = abs(deltax);
\r
2315 deltay = originytile - oldyt;
\r
2316 absdy = abs(deltay);
\r
2318 if (absdx>1 || absdy>1)
\r
2321 // scrolled more than one tile, so start from scratch
\r
2323 RF_NewPosition(originxglobal,originyglobal);
\r
2327 if (!absdx && !absdy)
\r
2328 return; // the screen has not scrolled an entire tile
\r
2334 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
2335 bufferofs += screenmove;
\r
2336 masterofs += screenmove;
\r
2340 // float the update regions
\r
2344 move += UPDATEWIDE;
\r
2345 else if (deltay==-1)
\r
2346 move -= UPDATEWIDE;
\r
2351 // draw the new tiles just scrolled on to the master screen, and
\r
2352 // mark them as needing to be copied to each screen next refreshes
\r
2353 // Make sure a zero is at the end of each row in update
\r
2360 RFL_NewRow (1); // new right row
\r
2361 RFL_RemoveAnimsOnX (originxtile-1);
\r
2365 RFL_NewRow (3); // new left row
\r
2366 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
2369 spotptr = updateptr+PORTTILESWIDE;
\r
2370 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
2372 *spotptr = 0; // drop a 0 at end of each row
\r
2373 spotptr+=UPDATEWIDE;
\r
2377 //----------------
\r
2383 RFL_NewRow (2); // new bottom row
\r
2384 *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;
\r
2385 RFL_RemoveAnimsOnY (originytile-1);
\r
2389 RFL_NewRow (0); // new top row
\r
2390 *(updateptr+PORTTILESWIDE) = 0;
\r
2391 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
2395 //----------------
\r
2398 // place a new terminator
\r
2400 spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;
\r
2402 *(unsigned *)spotptr = UPDATETERMINATE;
\r
2406 =====================
\r
2408 = RF_PlaceSprite CGA
\r
2410 =====================
\r
2413 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
2414 unsigned spritenumber, drawtype draw, int priority)
\r
2416 spritelisttype register *sprite,*next;
\r
2417 spritetabletype far *spr;
\r
2418 spritetype _seg *block;
\r
2419 unsigned shift,pixx;
\r
2420 char str[80],str2[10];
\r
2422 if (!spritenumber || spritenumber == (unsigned)-1)
\r
2424 RF_RemoveSprite (user);
\r
2428 sprite = (spritelisttype *)*user;
\r
2432 // sprite allready exists in the list, so we can use it's block
\r
2435 // post an erase block to erase the old position by copying
\r
2436 // screenx,screeny,width,height
\r
2438 if (!sprite->updatecount) // may not have been drawn at all yet
\r
2439 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2441 if (priority != sprite->priority)
\r
2443 // sprite moved to another priority, so unlink the old one and
\r
2444 // relink it in the new priority
\r
2446 next = sprite->nextsprite; // cut old links
\r
2448 next->prevptr = sprite->prevptr;
\r
2449 *sprite->prevptr = next;
\r
2455 // this is a brand new sprite, so allocate a block from the array
\r
2457 if (!spritefreeptr)
\r
2458 Quit ("RF_PlaceSprite: No free spots in spritearray!");
\r
2460 sprite = spritefreeptr;
\r
2461 spritefreeptr = spritefreeptr->nextsprite;
\r
2464 next = prioritystart[priority]; // stick it in new spot
\r
2466 next->prevptr = &sprite->nextsprite;
\r
2467 sprite->nextsprite = next;
\r
2468 prioritystart[priority] = sprite;
\r
2469 sprite->prevptr = &prioritystart[priority];
\r
2473 // write the new info to the sprite
\r
2475 spr = &spritetable[spritenumber-STARTSPRITES];
\r
2476 block = (spritetype _seg *)grsegs[spritenumber];
\r
2480 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");
\r
2481 itoa (spritenumber,str2,10);
\r
2482 strcat (str,str2);
\r
2487 globaly+=spr->orgy;
\r
2488 globalx+=spr->orgx;
\r
2490 sprite->screenx = globalx >> G_CGASX_SHIFT;
\r
2491 sprite->screeny = globaly >> G_SY_SHIFT;
\r
2492 sprite->width = block->width[0];
\r
2493 sprite->height = spr->height;
\r
2494 sprite->grseg = spritenumber;
\r
2495 sprite->sourceofs = block->sourceoffset[0];
\r
2496 sprite->planesize = block->planesize[0];
\r
2497 sprite->draw = draw;
\r
2498 sprite->priority = priority;
\r
2499 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
2500 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
2501 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
2502 - sprite->tilex + 1;
\r
2503 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
2504 - sprite->tiley + 1;
\r
2506 sprite->updatecount = 1; // draw on next refresh
\r
2508 // save the sprite pointer off in the user's pointer so it can be moved
\r
2514 //===========================================================================
\r
2517 =====================
\r
2519 = RF_RemoveSprite CGA
\r
2521 =====================
\r
2524 void RF_RemoveSprite (void **user)
\r
2526 spritelisttype *sprite,*next;
\r
2528 sprite = (spritelisttype *)*user;
\r
2533 // post an erase block to erase the old position by copying
\r
2534 // screenx,screeny,width,height
\r
2536 if (!sprite->updatecount)
\r
2538 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2542 // unlink the sprite node
\r
2544 next = sprite->nextsprite;
\r
2545 if (next) // if (!next), sprite is last in chain
\r
2546 next->prevptr = sprite->prevptr;
\r
2547 *sprite->prevptr = next;
\r
2550 // add it back to the free list
\r
2552 sprite->nextsprite = spritefreeptr;
\r
2553 spritefreeptr = sprite;
\r
2556 // null the users pointer, so next time that actor gets placed, it will
\r
2557 // allocate a new block
\r
2565 ====================
\r
2567 = RFL_EraseBlocks CGA
\r
2569 = Write mode 1 should be set
\r
2571 ====================
\r
2574 void RFL_EraseBlocks (void)
\r
2576 eraseblocktype *block,*done;
\r
2577 int screenxh,screenyh;
\r
2578 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
2580 unsigned updatedelta;
\r
2582 block = &eraselist[0][0];
\r
2584 done = eraselistptr[0];
\r
2586 while (block != done)
\r
2590 // clip the block to the current screen view
\r
2592 block->screenx -= originxscreen;
\r
2593 block->screeny -= originyscreen;
\r
2595 if (block->screenx < 0)
\r
2597 block->width += block->screenx;
\r
2598 if (block->width<1)
\r
2600 block->screenx = 0;
\r
2603 if (block->screeny < 0)
\r
2605 block->height += block->screeny;
\r
2606 if (block->height<1)
\r
2608 block->screeny = 0;
\r
2611 screenxh = block->screenx + block->width;
\r
2612 screenyh = block->screeny + block->height;
\r
2614 if (screenxh > CGAPORTSCREENWIDE)
\r
2616 block->width = CGAPORTSCREENWIDE-block->screenx;
\r
2617 screenxh = block->screenx + block->width;
\r
2620 if (screenyh > PORTSCREENHIGH)
\r
2622 block->height = PORTSCREENHIGH-block->screeny;
\r
2623 screenyh = block->screeny + block->height;
\r
2626 if (block->width<1 || block->height<1)
\r
2630 // erase the block by copying from the master screen
\r
2632 pos = ylookup[block->screeny]+block->screenx;
\r
2633 block->width = (block->width + (pos&1) + 1)& ~1;
\r
2634 pos &= ~1; // make sure a word copy gets used
\r
2635 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2636 block->width,block->height);
\r
2639 // put 2s in update where the block was, to force sprites to update
\r
2641 xtl = block->screenx >> SX_T_SHIFT;
\r
2642 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2643 ytl = block->screeny >> SY_T_SHIFT;
\r
2644 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2646 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2647 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2649 for (y=ytl;y<=yth;y++)
\r
2651 for (x=xtl;x<=xth;x++)
\r
2652 *updatespot++ = 2;
\r
2653 updatespot += updatedelta; // down to next line
\r
2659 eraselistptr[0] = &eraselist[0][0];
\r
2664 ====================
\r
2666 = RFL_UpdateSprites CGA
\r
2668 = NOTE: Implement vertical clipping!
\r
2670 ====================
\r
2673 void RFL_UpdateSprites (void)
\r
2675 spritelisttype *sprite;
\r
2676 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2679 byte *updatespot,*baseupdatespot;
\r
2680 unsigned updatedelta;
\r
2682 unsigned updatecount;
\r
2683 unsigned height,sourceofs;
\r
2690 for (priority=0;priority<PRIORITIES;priority++)
\r
2692 if (priority==MASKEDTILEPRIORITY)
\r
2693 RFL_MaskForegroundTiles ();
\r
2695 for (sprite = prioritystart[priority]; sprite ;
\r
2696 sprite = (spritelisttype *)sprite->nextsprite)
\r
2699 // see if the sprite has any visable area in the port
\r
2702 portx = sprite->screenx - originxscreen;
\r
2703 porty = sprite->screeny - originyscreen;
\r
2704 xtl = portx >> SX_T_SHIFT;
\r
2705 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2706 ytl = porty >> SY_T_SHIFT;
\r
2707 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2711 if (xth>=PORTTILESWIDE)
\r
2712 xth = PORTTILESWIDE-1;
\r
2715 if (yth>=PORTTILESHIGH)
\r
2716 yth = PORTTILESHIGH-1;
\r
2718 if (xtl>xth || ytl>yth)
\r
2722 // see if it's visable area covers any non 0 update tiles
\r
2724 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2725 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2727 if (sprite->updatecount)
\r
2729 sprite->updatecount--; // the sprite was just placed,
\r
2730 goto redraw; // so draw it for sure
\r
2733 for (y=ytl;y<=yth;y++)
\r
2735 for (x=xtl;x<=xth;x++)
\r
2736 if (*updatespot++)
\r
2738 updatespot += updatedelta; // down to next line
\r
2740 continue; // no need to update
\r
2744 // set the tiles it covers to 3, because those tiles are being
\r
2747 updatespot = baseupdatespot;
\r
2748 for (y=ytl;y<=yth;y++)
\r
2750 for (x=xtl;x<=xth;x++)
\r
2751 *updatespot++ = 3;
\r
2752 updatespot += updatedelta; // down to next line
\r
2757 height = sprite->height;
\r
2758 sourceofs = sprite->sourceofs;
\r
2761 height += porty; // clip top off
\r
2762 sourceofs -= porty*sprite->width;
\r
2765 else if (porty+height>PORTSCREENHIGH)
\r
2767 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2770 dest = bufferofs + ylookup[porty] + portx;
\r
2772 switch (sprite->draw)
\r
2775 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2776 dest,sprite->width,height,sprite->planesize);
\r
2794 =====================
\r
2798 = All routines will draw at the port at bufferofs, possibly copying from
\r
2799 = the port at masterofs. The EGA version then page flips, while the
\r
2800 = CGA version updates the screen from the buffer port.
\r
2802 = Screenpage is the currently displayed page, not the one being drawn
\r
2803 = Otherpage is the page to be worked with now
\r
2805 =====================
\r
2808 void RF_Refresh (void)
\r
2810 long newtime,oldtimecount;
\r
2812 RFL_AnimateTiles ();
\r
2815 // update newly scrolled on tiles and animated tiles from the master screen
\r
2817 RFL_UpdateTiles ();
\r
2818 RFL_EraseBlocks ();
\r
2821 // Update is all 0 except where sprites have changed or new area has
\r
2822 // been scrolled on. Go through all sprites and update the ones that cover
\r
2823 // a non 0 update tile
\r
2825 RFL_UpdateSprites ();
\r
2828 // if the main program has a refresh hook set, call their function before
\r
2829 // displaying the new page
\r
2831 if (refreshvector)
\r
2835 // update everything to the screen
\r
2837 VW_CGAFullUpdate ();
\r
2840 // calculate tics since last refresh for adaptive timing
\r
2845 #endif // GRMODE == CGAGR
\r