1 /* Reconstructed Commander Keen 4-6 Source Code
\r
2 * Copyright (C) 2021 K1n9_Duk3
\r
4 * This file is primarily based on:
\r
5 * Catacomb 3-D Source Code
\r
6 * Copyright (C) 1993-2014 Flat Rock Software
\r
8 * This program is free software; you can redistribute it and/or modify
\r
9 * it under the terms of the GNU General Public License as published by
\r
10 * the Free Software Foundation; either version 2 of the License, or
\r
11 * (at your option) any later version.
\r
13 * This program is distributed in the hope that it will be useful,
\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 * GNU General Public License for more details.
\r
18 * You should have received a copy of the GNU General Public License along
\r
19 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
26 =============================================================================
\r
31 scrolling more than one tile / refresh forces a total redraw
\r
33 two overlapping sprites of equal priority can change drawing order when
\r
36 =============================================================================
\r
39 #include "ID_HEADS.H"
\r
43 =============================================================================
\r
47 =============================================================================
\r
50 #define SCREENTILESWIDE 20
\r
51 #define SCREENTILESHIGH 13
\r
53 #define SCREENSPACE (SCREENWIDTH*240)
\r
54 #define FREEEGAMEM (0x10000l-3l*SCREENSPACE)
\r
57 // the update array must have enough space for two screens that can float
\r
58 // up two two tiles each way
\r
60 // (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared
\r
61 // by word width instructions
\r
63 #define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)
\r
64 #define UPDATESPARESIZE (UPDATEWIDE*2+4)
\r
65 #define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)
\r
67 #define G_EGASX_SHIFT 7 // global >> ?? = screen x
\r
68 #define G_CGASX_SHIFT 6 // global >> ?? = screen x
\r
69 #define G_SY_SHIFT 4 // global >> ?? = screen y
\r
71 unsigned SX_T_SHIFT; // screen x >> ?? = tile EGA = 1, CGA = 2;
\r
72 #define SY_T_SHIFT 4 // screen y >> ?? = tile
\r
75 #define EGAPORTSCREENWIDE 42
\r
76 #define CGAPORTSCREENWIDE 84
\r
77 #define PORTSCREENHIGH 224
\r
79 #define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)
\r
80 #define UPDATESPARESIZE (UPDATEWIDE*2+4)
\r
81 #define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)
\r
83 #define MAXSCROLLEDGES 6
\r
86 =============================================================================
\r
90 =============================================================================
\r
93 typedef struct spriteliststruct
\r
95 int screenx,screeny;
\r
98 unsigned grseg,sourceofs,planesize;
\r
100 unsigned tilex,tiley,tilewide,tilehigh;
\r
101 int priority,updatecount;
\r
102 struct spriteliststruct **prevptr,*nextsprite;
\r
108 int screenx,screeny;
\r
115 unsigned current; // foreground tiles have high bit set
\r
118 unsigned soundtile;
\r
125 typedef struct animtilestruct
\r
129 unsigned far *mapplane;
\r
130 struct animtilestruct **prevptr,*nexttile;
\r
134 =============================================================================
\r
138 =============================================================================
\r
142 long lasttimecount;
\r
144 boolean compatability; // crippled refresh for wierdo SVGAs
\r
146 unsigned mapwidth,mapheight,mapbyteswide,mapwordswide
\r
147 ,mapbytesextra,mapwordsextra;
\r
148 unsigned mapbwidthtable[MAXMAPHEIGHT];
\r
151 // Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow
\r
152 // for fractional movement and acceleration.
\r
154 // Tiles : Tile offsets from the upper left corner of the current map.
\r
156 // Screen : Graphics level offsets from map origin, x in bytes, y in pixels.
\r
157 // originxscreen is the same spot as originxtile, just with extra precision
\r
158 // so graphics don't need to be done in tile boundaries.
\r
161 unsigned originxglobal,originyglobal;
\r
162 unsigned originxtile,originytile;
\r
163 unsigned originxscreen,originyscreen;
\r
164 unsigned originmap;
\r
165 unsigned originxmin,originxmax,originymin,originymax;
\r
167 unsigned masterofs;
\r
170 // Table of the offsets from bufferofs of each tile spot in the
\r
171 // view port. The extra wide tile should never be drawn, but the space
\r
172 // is needed to account for the extra 0 in the update arrays. Built by
\r
176 unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];
\r
177 unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH];
\r
179 unsigned uwidthtable[PORTTILESHIGH]; // lookup instead of multiply
\r
181 byte update[2][UPDATESIZE];
\r
182 byte *updateptr,*baseupdateptr, // current start of update window
\r
184 *baseupdatestart[2];
\r
187 =============================================================================
\r
191 =============================================================================
\r
194 static char scratch[20],str[80];
\r
196 tiletype allanims[MAXANIMTYPES];
\r
197 unsigned numanimchains;
\r
199 void (*refreshvector) (void);
\r
201 unsigned screenstart[3] =
\r
202 {0,SCREENSPACE,SCREENSPACE*2};
\r
204 unsigned xpanmask; // prevent panning to odd pixels
\r
206 unsigned screenpage; // screen currently being displayed
\r
207 unsigned otherpage;
\r
210 spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],
\r
213 animtiletype animarray[MAXANIMTILES],*animhead,*animfreeptr;
\r
217 eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];
\r
219 int hscrollblocks,vscrollblocks;
\r
220 int hscrolledge[MAXSCROLLEDGES],vscrolledge[MAXSCROLLEDGES];
\r
223 =============================================================================
\r
227 =============================================================================
\r
230 void RFL_NewTile (unsigned updateoffset);
\r
231 void RFL_MaskForegroundTiles (void);
\r
232 void RFL_UpdateTiles (void);
\r
234 void RFL_BoundScroll (int x, int y);
\r
235 void RFL_CalcOriginStuff (long x, long y);
\r
236 void RFL_ClearScrollBlocks (void);
\r
237 void RFL_InitSpriteList (void);
\r
238 void RFL_InitAnimList (void);
\r
239 void RFL_CheckForAnimTile (unsigned x, unsigned y);
\r
240 void RFL_AnimateTiles (void);
\r
241 void RFL_RemoveAnimsOnX (unsigned x);
\r
242 void RFL_RemoveAnimsOnY (unsigned y);
\r
243 void RFL_EraseBlocks (void);
\r
244 void RFL_UpdateSprites (void);
\r
248 =============================================================================
\r
250 GRMODE INDEPENDANT ROUTINES
\r
252 =============================================================================
\r
257 =====================
\r
261 =====================
\r
264 static char *ParmStrings[] = {"comp",""};
\r
266 void RF_Startup (void)
\r
269 unsigned *blockstart;
\r
273 // Keen 4-6 store the compatability setting in the game's config file.
\r
274 // The setting is loaded from that file AFTER RF_Startup is executed,
\r
275 // making this check useless (unless the config file doesn't exist).
\r
276 // Instead, US_Startup now checks for that parameter after the config
\r
277 // file has been read.
\r
279 if (grmode == EGAGR)
\r
280 for (i = 1;i < _argc;i++)
\r
281 if (US_CheckParm(_argv[i],ParmStrings) == 0)
\r
283 compatability = true;
\r
288 for (i=0;i<PORTTILESHIGH;i++)
\r
289 uwidthtable[i] = UPDATEWIDE*i;
\r
291 originxmin = originymin = MAPBORDER*TILEGLOBAL;
\r
293 eraselistptr[0] = &eraselist[0][0];
\r
294 eraselistptr[1] = &eraselist[1][0];
\r
298 if (grmode == EGAGR)
\r
302 baseupdatestart[0] = &update[0][UPDATESPARESIZE];
\r
303 baseupdatestart[1] = &update[1][UPDATESPARESIZE];
\r
307 displayofs = screenstart[screenpage];
\r
308 bufferofs = screenstart[otherpage];
\r
309 masterofs = screenstart[2];
\r
311 updateptr = baseupdatestart[otherpage];
\r
313 blockstart = &blockstarts[0];
\r
314 for (y=0;y<UPDATEHIGH;y++)
\r
315 for (x=0;x<UPDATEWIDE;x++)
\r
316 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
318 xpanmask = 6; // dont pan to odd pixels
\r
321 else if (grmode == CGAGR)
\r
325 updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];
\r
328 masterofs = 0x8000;
\r
330 blockstart = &blockstarts[0];
\r
331 for (y=0;y<UPDATEHIGH;y++)
\r
332 for (x=0;x<UPDATEWIDE;x++)
\r
333 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
341 =====================
\r
345 =====================
\r
348 void RF_Shutdown (void)
\r
353 //===========================================================================
\r
357 =====================
\r
361 = Sets bufferofs,displayofs, and masterofs to regular values, for the
\r
362 = occasions when you have moved them around manually
\r
364 =====================
\r
367 void RF_FixOfs (void)
\r
369 screenstart[0] = 0;
\r
370 screenstart[1] = SCREENSPACE;
\r
371 screenstart[2] = SCREENSPACE*2;
\r
373 if (grmode == EGAGR)
\r
377 panx = pany = pansx = pansy = panadjust = 0;
\r
378 displayofs = screenstart[screenpage];
\r
379 bufferofs = screenstart[otherpage];
\r
380 masterofs = screenstart[2];
\r
381 VW_SetScreen (displayofs,0);
\r
385 panx = pany = pansx = pansy = panadjust = 0;
\r
387 masterofs = 0x8000;
\r
392 //===========================================================================
\r
395 =====================
\r
399 = Makes some convienient calculations based on maphead->
\r
401 =====================
\r
404 void RF_NewMap (void)
\r
407 unsigned spot,*table;
\r
409 mapwidth = mapheaderseg[mapon]->width;
\r
410 mapbyteswide = 2*mapwidth;
\r
411 mapheight = mapheaderseg[mapon]->height;
\r
412 mapwordsextra = mapwidth-PORTTILESWIDE;
\r
413 mapbytesextra = 2*mapwordsextra;
\r
416 // make a lookup table for the maps left edge
\r
418 if (mapheight > MAXMAPHEIGHT)
\r
419 Quit ("RF_NewMap: Map too tall!");
\r
421 for (i=0;i<mapheight;i++)
\r
423 mapbwidthtable[i] = spot;
\r
424 spot += mapbyteswide;
\r
428 // fill in updatemapofs with the new width info
\r
430 table = &updatemapofs[0];
\r
431 for (y=0;y<PORTTILESHIGH;y++)
\r
432 for (x=0;x<UPDATEWIDE;x++)
\r
433 *table++ = mapbwidthtable[y]+x*2;
\r
436 // the y max value clips off the bottom half of a tile so a map that is
\r
437 // 13 + MAPBORDER*2 tile high will not scroll at all vertically
\r
439 originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;
\r
440 originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;
\r
441 if (originxmax<originxmin) // for very small maps
\r
442 originxmax=originxmin;
\r
443 if (originymax<originymin)
\r
444 originymax=originymin;
\r
447 // clear out the lists
\r
449 RFL_InitSpriteList ();
\r
450 RFL_InitAnimList ();
\r
451 RFL_ClearScrollBlocks ();
\r
452 RF_SetScrollBlock (0,MAPBORDER-1,true);
\r
453 RF_SetScrollBlock (0,mapheight-MAPBORDER,true);
\r
454 RF_SetScrollBlock (MAPBORDER-1,0,false);
\r
455 RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);
\r
458 lasttimecount = TimeCount; // setup for adaptive timing
\r
462 //===========================================================================
\r
466 ==========================
\r
468 = RFL_CheckTileSound
\r
470 = Checks if the tile plays a sound and if so adds that info to the animation
\r
472 ==========================
\r
475 #define NUMSOUNDTILES 2
\r
477 unsigned tilenums[NUMSOUNDTILES];
\r
478 int sounds[NUMSOUNDTILES];
\r
481 tilesoundtype far soundtiles = {
\r
482 {2152|0x8000, 2208|0x8000},
\r
483 {SND_STOMP, SND_FLAME}
\r
486 void RFL_CheckTileSound(tiletype *anim, unsigned tile)
\r
490 for (i=0; i<NUMSOUNDTILES; i++)
\r
492 if (soundtiles.tilenums[i] == tile)
\r
494 anim->soundtile = tile;
\r
495 anim->sound = soundtiles.sounds[i];
\r
503 //===========================================================================
\r
506 ==========================
\r
508 = RF_MarkTileGraphics
\r
510 = Goes through mapplane[0/1] and marks all background/foreground tiles
\r
511 = needed, then follows all animation sequences to make sure animated
\r
512 = tiles get all the stages. Every unique animating tile is given an
\r
513 = entry in allanims[], so every instance of that tile will animate at the
\r
514 = same rate. The info plane for each animating tile will hold a pointer
\r
515 = into allanims[], therefore you can't have both an animating foreground
\r
516 = and background tile in the same spot!
\r
518 ==========================
\r
521 void RF_MarkTileGraphics (void)
\r
524 int tile,next,anims,change;
\r
525 unsigned far *start,far *end,far *info;
\r
526 unsigned i,tilehigh;
\r
527 char str[80],str2[10];
\r
529 memset (allanims,0,sizeof(allanims));
\r
532 size = mapwidth*mapheight;
\r
535 // background plane
\r
537 start = mapsegs[0];
\r
543 if (tile>=0) // <0 is a tile that is never drawn
\r
545 CA_MarkGrChunk(STARTTILE16+tile);
\r
546 if (tinf[ANIM+tile])
\r
548 // this tile will animated
\r
550 if (tinf[SPEED+tile])
\r
552 if (!tinf[ANIM+tile])
\r
554 strcpy (str,"RF_MarkTileGraphics: Background anim of 0:");
\r
555 itoa (tile,str2,10);
\r
559 for (i=0;i<numanimchains;i++)
\r
560 if (allanims[i].current == tile)
\r
562 *info = (unsigned)&allanims[i];
\r
566 // new chain of animating tiles
\r
568 if (i>=MAXANIMTYPES)
\r
569 Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");
\r
570 allanims[i].current = tile;
\r
571 allanims[i].count = tinf[SPEED+tile];
\r
573 allanims[i].visible = 0;
\r
574 allanims[i].sound = -1;
\r
576 *info = (unsigned)&allanims[i];
\r
580 RFL_CheckTileSound(&allanims[i], tile);
\r
584 change = (signed char)(tinf[ANIM+tile]);
\r
585 next = tile+change;
\r
586 while (change && next != tile)
\r
589 RFL_CheckTileSound(&allanims[i], next);
\r
591 CA_MarkGrChunk(STARTTILE16+next);
\r
592 change = (signed char)(tinf[ANIM+next]);
\r
596 strcpy (str,"RF_MarkTileGraphics: Unending background animation:");
\r
597 itoa (next,str2,10);
\r
607 } while (start<end);
\r
610 // foreground plane
\r
612 start = mapsegs[1];
\r
618 if (tile>=0) // <0 is a tile that is never drawn
\r
620 CA_MarkGrChunk(STARTTILE16M+tile);
\r
621 if (tinf[MANIM+tile])
\r
623 // this tile will animated
\r
625 if (tinf[MSPEED+tile])
\r
627 if (!tinf[MANIM+tile])
\r
629 strcpy (str,"RF_MarkTileGraphics: Foreground anim of 0:");
\r
630 itoa (tile,str2,10);
\r
634 tilehigh = tile | 0x8000; // foreground tiles have high bit
\r
635 for (i=0;i<numanimchains;i++)
\r
636 if (allanims[i].current == tilehigh)
\r
638 *info = (unsigned)&allanims[i];
\r
642 // new chain of animating tiles
\r
644 if (i>=MAXANIMTYPES)
\r
645 Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");
\r
646 allanims[i].current = tilehigh;
\r
647 allanims[i].count = tinf[MSPEED+tile];
\r
649 allanims[i].visible = 0;
\r
650 allanims[i].sound = -1;
\r
652 *info = (unsigned)&allanims[i];
\r
657 RFL_CheckTileSound(&allanims[i], tilehigh);
\r
660 change = (signed char)(tinf[MANIM+tile]);
\r
661 next = tile+change;
\r
662 while (change && next != tile)
\r
665 RFL_CheckTileSound(&allanims[i], next | 0x8000); // foreground tiles have high bit
\r
667 CA_MarkGrChunk(STARTTILE16M+next);
\r
668 change = (signed char)(tinf[MANIM+next]);
\r
672 strcpy (str,"RF_MarkTileGraphics: Unending foreground animation:");
\r
673 itoa (next,str2,10);
\r
683 } while (start<end);
\r
687 //===========================================================================
\r
691 =========================
\r
695 = Call to clear out the entire animating tile list and return all of them to
\r
698 =========================
\r
701 void RFL_InitAnimList (void)
\r
705 animfreeptr = &animarray[0];
\r
707 for (i=0;i<MAXANIMTILES-1;i++)
\r
708 animarray[i].nexttile = &animarray[i+1];
\r
710 animarray[i].nexttile = NULL;
\r
712 animhead = NULL; // nothing in list
\r
718 for (anim = allanims; anim->current != 0; anim++)
\r
726 ====================
\r
728 = RFL_CheckForAnimTile
\r
730 ====================
\r
733 void RFL_CheckForAnimTile (unsigned x, unsigned y)
\r
735 unsigned tile,offset,speed,lasttime,thistime,timemissed;
\r
737 animtiletype *anim,*next;
\r
739 // the info plane of each animating tile has a near pointer into allanims[]
\r
740 // which gives the current state of all concurrently animating tiles
\r
742 offset = mapbwidthtable[y]/2+x;
\r
747 map = mapsegs[0]+offset;
\r
749 if (tinf[ANIM+tile] && tinf[SPEED+tile])
\r
752 Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
\r
753 anim = animfreeptr;
\r
754 animfreeptr = animfreeptr->nexttile;
\r
755 next = animhead; // stick it at the start of the list
\r
758 next->prevptr = &anim->nexttile;
\r
759 anim->nexttile = next;
\r
760 anim->prevptr = &animhead;
\r
765 anim->mapplane = map;
\r
766 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
768 anim->chain->visible++;
\r
775 map = mapsegs[1]+offset;
\r
777 if (tinf[MANIM+tile] && tinf[MSPEED+tile])
\r
780 Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
\r
781 anim = animfreeptr;
\r
782 animfreeptr = animfreeptr->nexttile;
\r
783 next = animhead; // stick it at the start of the list
\r
786 next->prevptr = &anim->nexttile;
\r
787 anim->nexttile = next;
\r
788 anim->prevptr = &animhead;
\r
793 anim->mapplane = map;
\r
794 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
796 anim->chain->visible++;
\r
804 ====================
\r
806 = RFL_RemoveAnimsOnX
\r
808 ====================
\r
811 void RFL_RemoveAnimsOnX (unsigned x)
\r
813 animtiletype *current,*next;
\r
815 current = animhead;
\r
818 if (current->x == x)
\r
821 current->chain->visible--;
\r
823 *(void **)current->prevptr = current->nexttile;
\r
824 if (current->nexttile)
\r
825 current->nexttile->prevptr = current->prevptr;
\r
826 next = current->nexttile;
\r
827 current->nexttile = animfreeptr;
\r
828 animfreeptr = current;
\r
832 current = current->nexttile;
\r
838 ====================
\r
840 = RFL_RemoveAnimsOnY
\r
842 ====================
\r
845 void RFL_RemoveAnimsOnY (unsigned y)
\r
847 animtiletype *current,*next;
\r
849 current = animhead;
\r
852 if (current->y == y)
\r
855 current->chain->visible--;
\r
857 *(void **)current->prevptr = current->nexttile;
\r
858 if (current->nexttile)
\r
859 current->nexttile->prevptr = current->prevptr;
\r
860 next = current->nexttile;
\r
861 current->nexttile = animfreeptr;
\r
862 animfreeptr = current;
\r
866 current = current->nexttile;
\r
872 ====================
\r
874 = RFL_RemoveAnimsInBlock
\r
876 ====================
\r
879 void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)
\r
881 animtiletype *current,*next;
\r
883 current = animhead;
\r
886 if (current->x - x < width && current->y - y < height)
\r
889 current->chain->visible--;
\r
891 *(void **)current->prevptr = current->nexttile;
\r
892 if (current->nexttile)
\r
893 current->nexttile->prevptr = current->prevptr;
\r
894 next = current->nexttile;
\r
895 current->nexttile = animfreeptr;
\r
896 animfreeptr = current;
\r
900 current = current->nexttile;
\r
906 ====================
\r
910 ====================
\r
913 void RFL_AnimateTiles (void)
\r
915 animtiletype *current;
\r
916 unsigned updateofs,tile,x,y;
\r
920 // animate the lists of tiles
\r
922 anim = &allanims[0];
\r
923 while (anim->current)
\r
926 while ( anim->count < 1)
\r
928 if (anim->current & 0x8000)
\r
930 tile = anim->current & 0x7fff;
\r
931 tile += (signed char)tinf[MANIM+tile];
\r
932 anim->count += tinf[MSPEED+tile];
\r
937 tile = anim->current;
\r
938 tile += (signed char)tinf[ANIM+tile];
\r
939 anim->count += tinf[SPEED+tile];
\r
941 anim->current = tile;
\r
943 if (anim->visible && anim->current == anim->soundtile && anim->sound != -1)
\r
945 SD_PlaySound(anim->sound);
\r
954 // traverse the list of animating tiles
\r
956 current = animhead;
\r
959 tile =current->chain->current;
\r
960 if ( tile != current->tile)
\r
962 // tile has animated
\r
964 // remove tile from master screen cache,
\r
965 // change a tile to its next state, set the structure up for
\r
966 // next animation, and post an update region to both update pages
\r
968 current->tile = tile;
\r
970 *(current->mapplane) = tile & 0x7fff; // change in map
\r
972 x = current->x-originxtile;
\r
973 y = current->y-originytile;
\r
975 if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)
\r
976 Quit ("RFL_AnimateTiles: Out of bounds!");
\r
978 updateofs = uwidthtable[y] + x;
\r
979 RFL_NewTile(updateofs); // puts "1"s in both pages
\r
981 current = current->nexttile;
\r
986 //===========================================================================
\r
989 =========================
\r
991 = RFL_InitSpriteList
\r
993 = Call to clear out the entire sprite list and return all of them to
\r
996 =========================
\r
999 void RFL_InitSpriteList (void)
\r
1003 spritefreeptr = &spritearray[0];
\r
1004 for (i=0;i<MAXSPRITES-1;i++)
\r
1005 spritearray[i].nextsprite = &spritearray[i+1];
\r
1007 spritearray[i].nextsprite = NULL;
\r
1009 // NULL in all priority levels
\r
1011 memset (prioritystart,0,sizeof(prioritystart));
\r
1014 //===========================================================================
\r
1019 = RFL_CalcOriginStuff
\r
1021 = Calculate all the global variables for a new position
\r
1022 = Long parms so position can be clipped to a maximum near 64k
\r
1027 void RFL_CalcOriginStuff (long x, long y)
\r
1029 originxglobal = x;
\r
1030 originyglobal = y;
\r
1031 originxtile = originxglobal>>G_T_SHIFT;
\r
1032 originytile = originyglobal>>G_T_SHIFT;
\r
1033 originxscreen = originxtile<<SX_T_SHIFT;
\r
1034 originyscreen = originytile<<SY_T_SHIFT;
\r
1035 originmap = mapbwidthtable[originytile] + originxtile*2;
\r
1037 #if GRMODE == EGAGR
\r
1038 panx = (originxglobal>>G_P_SHIFT) & 15;
\r
1040 pany = pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
1041 panadjust = panx/8 + ylookup[pany];
\r
1044 #if GRMODE == CGAGR
\r
1045 panx = (originxglobal>>G_P_SHIFT) & 15;
\r
1046 pansx = panx & 12;
\r
1047 pany = pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
1048 panadjust = pansx/4 + ylookup[pansy];
\r
1057 = RFL_ClearScrollBlocks
\r
1062 void RFL_ClearScrollBlocks (void)
\r
1064 hscrollblocks = vscrollblocks = 0;
\r
1071 = RF_SetScrollBlock
\r
1073 = Sets a horizontal or vertical scroll block
\r
1074 = a horizontal block is ----, meaning it blocks up/down movement
\r
1079 void RF_SetScrollBlock (int x, int y, boolean horizontal)
\r
1083 hscrolledge[hscrollblocks] = y;
\r
1084 if (hscrollblocks++ == MAXSCROLLEDGES)
\r
1085 Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks");
\r
1089 vscrolledge[vscrollblocks] = x;
\r
1090 if (vscrollblocks++ == MAXSCROLLEDGES)
\r
1091 Quit ("RF_SetScrollBlock: Too many vertical scroll blocks");
\r
1101 = Bound a given x/y movement to scroll blocks
\r
1106 void RFL_BoundScroll (int x, int y)
\r
1108 int check,newxtile,newytile;
\r
1110 originxglobal += x;
\r
1111 originyglobal += y;
\r
1113 newxtile= originxglobal >> G_T_SHIFT;
\r
1114 newytile = originyglobal >> G_T_SHIFT;
\r
1118 newxtile+=SCREENTILESWIDE;
\r
1119 for (check=0;check<vscrollblocks;check++)
\r
1120 if (vscrolledge[check] == newxtile)
\r
1122 originxglobal = originxglobal&0xff00;
\r
1128 for (check=0;check<vscrollblocks;check++)
\r
1129 if (vscrolledge[check] == newxtile)
\r
1131 originxglobal = (originxglobal&0xff00)+0x100;
\r
1139 newytile+=SCREENTILESHIGH;
\r
1140 for (check=0;check<hscrollblocks;check++)
\r
1141 if (hscrolledge[check] == newytile)
\r
1143 originyglobal = originyglobal&0xff00;
\r
1149 for (check=0;check<hscrollblocks;check++)
\r
1150 if (hscrolledge[check] == newytile)
\r
1152 originyglobal = (originyglobal&0xff00)+0x100;
\r
1158 RFL_CalcOriginStuff (originxglobal, originyglobal);
\r
1162 //===========================================================================
\r
1165 =====================
\r
1167 = RF_SetRefreshHook
\r
1169 =====================
\r
1172 void RF_SetRefreshHook (void (*func) (void) )
\r
1174 refreshvector = func;
\r
1178 //===========================================================================
\r
1185 = Bring a new row of tiles onto the port, spawning animating tiles
\r
1190 void RFL_NewRow (int dir)
\r
1192 unsigned count,updatespot,updatestep;
\r
1193 int x,y,xstep,ystep;
\r
1197 case 0: // top row
\r
1204 count = PORTTILESWIDE;
\r
1207 case 1: // right row
\r
1208 updatespot = PORTTILESWIDE-1;
\r
1209 updatestep = UPDATEWIDE;
\r
1210 x = originxtile + PORTTILESWIDE-1;
\r
1214 count = PORTTILESHIGH;
\r
1217 case 2: // bottom row
\r
1218 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1221 y = originytile + PORTTILESHIGH-1;
\r
1224 count = PORTTILESWIDE;
\r
1227 case 3: // left row
\r
1229 updatestep = UPDATEWIDE;
\r
1234 count = PORTTILESHIGH;
\r
1237 Quit ("RFL_NewRow: Bad dir!");
\r
1242 RFL_NewTile(updatespot);
\r
1243 RFL_CheckForAnimTile (x,y);
\r
1244 updatespot+=updatestep;
\r
1250 //===========================================================================
\r
1253 =====================
\r
1257 =====================
\r
1260 void RF_ForceRefresh (void)
\r
1262 RF_NewPosition (originxglobal,originyglobal);
\r
1267 //===========================================================================
\r
1270 =====================
\r
1274 = Copies a block of tiles (all three planes) from one point
\r
1275 = in the map to another, accounting for animating tiles
\r
1277 =====================
\r
1280 void RF_MapToMap (unsigned srcx, unsigned srcy,
\r
1281 unsigned destx, unsigned desty,
\r
1282 unsigned width, unsigned height)
\r
1285 unsigned source,destofs,xspot,yspot;
\r
1286 unsigned linedelta,p0,p1,p2,updatespot;
\r
1287 unsigned far *source0, far *source1, far *source2;
\r
1288 unsigned far *dest0, far *dest1, far *dest2;
\r
1291 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1293 source = mapbwidthtable[srcy]/2 + srcx;
\r
1295 source0 = mapsegs[0]+source;
\r
1296 source1 = mapsegs[1]+source;
\r
1297 source2 = mapsegs[2]+source;
\r
1299 destofs = mapbwidthtable[desty]/2 + destx;
\r
1300 destofs -= source;
\r
1302 linedelta = mapwidth - width;
\r
1304 for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)
\r
1305 for (x=0;x<width;x++,source0++,source1++,source2++)
\r
1311 dest0 = source0 + destofs;
\r
1312 dest1 = source1 + destofs;
\r
1313 dest2 = source2 + destofs;
\r
1316 // only make a new tile if it is different
\r
1318 if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)
\r
1329 // if tile is on the view port
\r
1331 xspot = destx+x-originxtile;
\r
1332 yspot = desty+y-originytile;
\r
1333 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1337 updatespot = uwidthtable[yspot]+xspot;
\r
1338 RFL_NewTile(updatespot);
\r
1340 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1345 //===========================================================================
\r
1349 =====================
\r
1353 = Copies a string of tiles from main memory to the map,
\r
1354 = accounting for animating tiles
\r
1356 =====================
\r
1359 void RF_MemToMap (unsigned far *source, unsigned plane,
\r
1360 unsigned destx, unsigned desty,
\r
1361 unsigned width, unsigned height)
\r
1364 unsigned xspot,yspot;
\r
1365 unsigned linedelta,updatespot;
\r
1366 unsigned far *dest,old,new;
\r
1369 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1371 dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;
\r
1373 linedelta = mapwidth - width;
\r
1375 for (y=0;y<height;y++,dest+=linedelta)
\r
1376 for (x=0;x<width;x++)
\r
1389 xspot = destx+x-originxtile;
\r
1390 yspot = desty+y-originytile;
\r
1391 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1395 updatespot = uwidthtable[yspot]+xspot;
\r
1396 RFL_NewTile(updatespot);
\r
1398 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1403 //===========================================================================
\r
1407 =====================
\r
1409 = RFL_BoundNewOrigin
\r
1411 = Copies a string of tiles from main memory to the map,
\r
1412 = accounting for animating tiles
\r
1414 =====================
\r
1417 void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)
\r
1422 // calculate new origin related globals
\r
1424 if (orgx<originxmin)
\r
1426 else if (orgx>originxmax)
\r
1429 if (orgy<originymin)
\r
1431 else if (orgy>originymax)
\r
1434 originxtile = orgx>>G_T_SHIFT;
\r
1435 originytile = orgy>>G_T_SHIFT;
\r
1437 for (check=0;check<vscrollblocks;check++)
\r
1439 edge = vscrolledge[check];
\r
1440 if (edge>=originxtile && edge <=originxtile+10)
\r
1442 orgx = (edge+1)*TILEGLOBAL;
\r
1445 if (edge>=originxtile+11 && edge <=originxtile+20)
\r
1447 orgx = (edge-20)*TILEGLOBAL;
\r
1452 for (check=0;check<hscrollblocks;check++)
\r
1454 edge = hscrolledge[check];
\r
1455 if (edge>=originytile && edge <=originytile+6)
\r
1457 orgy = (edge+1)*TILEGLOBAL;
\r
1460 if (edge>=originytile+7 && edge <=originytile+13)
\r
1462 orgy = (edge-13)*TILEGLOBAL;
\r
1468 RFL_CalcOriginStuff (orgx,orgy);
\r
1472 //===========================================================================
\r
1475 =====================
\r
1479 = Posts erase blocks to clear a certain area of the screen to the master
\r
1480 = screen, to erase text or something draw directly to the screen
\r
1482 = Parameters in pixels, but erasure is byte bounded
\r
1484 =====================
\r
1487 void RF_ClearBlock (int x, int y, int width, int height)
\r
1489 eraseblocktype block;
\r
1491 #if GRMODE == EGAGR
\r
1492 block.screenx = x/8+originxscreen;
\r
1493 block.screeny = y+originyscreen;
\r
1494 block.width = (width+(x&7)+7)/8;
\r
1495 block.height = height;
\r
1496 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1497 memcpy (eraselistptr[1]++,&block,sizeof(block));
\r
1500 #if GRMODE == CGAGR
\r
1501 block.screenx = x/4+originxscreen;
\r
1502 block.screeny = y+originyscreen;
\r
1503 block.width = (width+(x&3)+3)/4;
\r
1504 block.height = height;
\r
1505 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1510 //===========================================================================
\r
1513 =====================
\r
1517 = Causes a number of tiles to be redrawn to the master screen and updated
\r
1519 = Parameters in pixels, but erasure is tile bounded
\r
1521 =====================
\r
1524 void RF_RedrawBlock (int x, int y, int width, int height)
\r
1526 int xx,yy,xl,xh,yl,yh;
\r
1529 xh=(x+panx+width+15)/16;
\r
1531 yh=(y+pany+height+15)/16;
\r
1532 for (yy=yl;yy<=yh;yy++)
\r
1533 for (xx=xl;xx<=xh;xx++)
\r
1534 RFL_NewTile (yy*UPDATEWIDE+xx);
\r
1538 //===========================================================================
\r
1541 =====================
\r
1545 =====================
\r
1548 void RF_CalcTics (void)
\r
1550 long newtime,oldtimecount;
\r
1553 // calculate tics since last refresh for adaptive timing
\r
1555 if (lasttimecount > TimeCount)
\r
1556 TimeCount = lasttimecount; // if the game was paused a LONG time
\r
1558 if (DemoMode) // demo recording and playback needs
\r
1559 { // to be constant
\r
1561 // take DEMOTICS or more tics, and modify Timecount to reflect time taken
\r
1563 oldtimecount = lasttimecount;
\r
1564 while (TimeCount<oldtimecount+DEMOTICS*2)
\r
1566 lasttimecount = oldtimecount + DEMOTICS;
\r
1567 TimeCount = lasttimecount + DEMOTICS;
\r
1573 // non demo, so report actual time
\r
1577 newtime = TimeCount;
\r
1578 tics = newtime-lasttimecount;
\r
1579 } while (tics<MINTICS);
\r
1580 lasttimecount = newtime;
\r
1583 strcpy (scratch,"\tTics:");
\r
1584 itoa (tics,str,10);
\r
1585 strcat (scratch,str);
\r
1586 strcat (scratch,"\n");
\r
1587 write (profilehandle,scratch,strlen(scratch));
\r
1592 TimeCount -= (tics-MAXTICS);
\r
1598 //===========================================================================
\r
1601 =====================
\r
1603 = RF_FindFreeBuffer
\r
1605 = Finds the start of unused, non visable buffer space
\r
1607 =====================
\r
1610 unsigned RF_FindFreeBuffer (void)
\r
1612 unsigned spot,i,j;
\r
1617 spot = screenstart[i]+SCREENSPACE;
\r
1620 if (spot == screenstart[j])
\r
1629 return 0; // never get here...
\r
1633 =============================================================================
\r
1635 EGA specific routines
\r
1637 =============================================================================
\r
1640 #if GRMODE == EGAGR
\r
1643 =====================
\r
1645 = RF_NewPosition EGA
\r
1647 =====================
\r
1650 void RF_NewPosition (unsigned x, unsigned y)
\r
1653 byte *page0ptr,*page1ptr;
\r
1654 unsigned updatenum;
\r
1656 RFL_BoundNewOrigin (x,y);
\r
1658 // clear out all animating tiles
\r
1660 RFL_InitAnimList ();
\r
1663 // set up the new update arrays at base position
\r
1665 updatestart[0] = baseupdatestart[0];
\r
1666 updatestart[1] = baseupdatestart[1];
\r
1667 updateptr = updatestart[otherpage];
\r
1669 page0ptr = updatestart[0]+PORTTILESWIDE; // used to stick "0"s after rows
\r
1670 page1ptr = updatestart[1]+PORTTILESWIDE;
\r
1672 updatenum = 0; // start at first visable tile
\r
1674 for (my=0;my<PORTTILESHIGH;my++)
\r
1676 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
1678 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
1679 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
1683 *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles
\r
1684 page0ptr+=(PORTTILESWIDE+1);
\r
1685 page1ptr+=(PORTTILESWIDE+1);
\r
1687 *(word *)(page0ptr-PORTTILESWIDE)
\r
1688 = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
1691 //===========================================================================
\r
1695 =====================
\r
1699 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
1700 = scroll if needed. If the scroll distance is greater than one tile, the
\r
1701 = entire screen will be redrawn (this could be generalized, but scrolling
\r
1702 = more than one tile per refresh is a bad idea!).
\r
1704 =====================
\r
1707 void RF_Scroll (int x, int y)
\r
1709 long neworgx,neworgy;
\r
1710 int i,deltax,deltay,absdx,absdy;
\r
1711 int oldxt,oldyt,move,yy;
\r
1712 unsigned updatespot;
\r
1713 byte *update0,*update1;
\r
1714 unsigned oldpanx,oldpanadjust,oldscreen,newscreen,screencopy;
\r
1717 oldxt = originxtile;
\r
1718 oldyt = originytile;
\r
1719 oldpanadjust = panadjust;
\r
1722 RFL_BoundScroll (x,y);
\r
1724 deltax = originxtile - oldxt;
\r
1725 absdx = abs(deltax);
\r
1726 deltay = originytile - oldyt;
\r
1727 absdy = abs(deltay);
\r
1729 if (absdx>1 || absdy>1)
\r
1732 // scrolled more than one tile, so start from scratch
\r
1734 RF_NewPosition(originxglobal,originyglobal);
\r
1738 if (!absdx && !absdy)
\r
1739 return; // the screen has not scrolled an entire tile
\r
1743 // adjust screens and handle SVGA crippled compatability mode
\r
1745 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
1748 screenstart[i]+= screenmove;
\r
1749 if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )
\r
1752 // move the screen to the opposite end of the buffer
\r
1754 screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;
\r
1755 oldscreen = screenstart[i] - screenmove;
\r
1756 newscreen = oldscreen + screencopy;
\r
1757 screenstart[i] = newscreen + screenmove;
\r
1758 VW_ScreenToScreen (oldscreen,newscreen,
\r
1759 PORTTILESWIDE*2,PORTTILESHIGH*16);
\r
1761 if (i==screenpage)
\r
1762 VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);
\r
1765 bufferofs = screenstart[otherpage];
\r
1766 displayofs = screenstart[screenpage];
\r
1767 masterofs = screenstart[2];
\r
1771 // float the update regions
\r
1775 move += UPDATEWIDE;
\r
1776 else if (deltay==-1)
\r
1777 move -= UPDATEWIDE;
\r
1779 updatestart[0]+=move;
\r
1780 updatestart[1]+=move;
\r
1783 // draw the new tiles just scrolled on to the master screen, and
\r
1784 // mark them as needing to be copied to each screen next refreshes
\r
1785 // Make sure a zero is at the end of each row in update
\r
1792 RFL_NewRow (1); // new right row
\r
1793 RFL_RemoveAnimsOnX (originxtile-1);
\r
1797 RFL_NewRow (3); // new left row
\r
1798 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
1801 update0 = updatestart[0]+PORTTILESWIDE;
\r
1802 update1 = updatestart[1]+PORTTILESWIDE;
\r
1803 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
1805 *update0 = *update1 = 0; // drop a 0 at end of each row
\r
1806 update0+=UPDATEWIDE;
\r
1807 update1+=UPDATEWIDE;
\r
1811 //----------------
\r
1817 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1818 RFL_NewRow (2); // new bottom row
\r
1819 RFL_RemoveAnimsOnY (originytile-1);
\r
1824 RFL_NewRow (0); // new top row
\r
1825 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
1828 *(updatestart[0]+updatespot+PORTTILESWIDE) =
\r
1829 *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;
\r
1832 //----------------
\r
1835 // place a new terminator
\r
1837 update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1838 update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1839 *update0++ = *update1++ = 0;
\r
1840 *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;
\r
1843 //===========================================================================
\r
1846 =====================
\r
1848 = RF_PlaceSprite EGA
\r
1850 =====================
\r
1853 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
1854 unsigned spritenumber, drawtype draw, int priority)
\r
1856 spritelisttype register *sprite,*next;
\r
1857 spritetabletype far *spr;
\r
1858 spritetype _seg *block;
\r
1859 unsigned shift,pixx;
\r
1860 char str[80],str2[10];
\r
1862 if (!spritenumber || spritenumber == (unsigned)-1)
\r
1864 RF_RemoveSprite (user);
\r
1868 sprite = (spritelisttype *)*user;
\r
1872 // sprite allready exists in the list, so we can use it's block
\r
1875 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1876 // both pages may not need to be erased if the sprite just changed last frame
\r
1878 if (sprite->updatecount<2)
\r
1880 if (!sprite->updatecount)
\r
1881 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1882 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1885 if (priority != sprite->priority)
\r
1887 // sprite mvoed to another priority, so unlink the old one and
\r
1888 // relink it in the new priority
\r
1890 next = sprite->nextsprite; // cut old links
\r
1892 next->prevptr = sprite->prevptr;
\r
1893 *sprite->prevptr = next;
\r
1899 // this is a brand new sprite, so allocate a block from the array
\r
1901 if (!spritefreeptr)
\r
1902 Quit ("RF_PlaceSprite: No free spots in spritearray!");
\r
1904 sprite = spritefreeptr;
\r
1905 spritefreeptr = spritefreeptr->nextsprite;
\r
1908 next = prioritystart[priority]; // stick it in new spot
\r
1910 next->prevptr = &sprite->nextsprite;
\r
1911 sprite->nextsprite = next;
\r
1912 prioritystart[priority] = sprite;
\r
1913 sprite->prevptr = &prioritystart[priority];
\r
1917 // write the new info to the sprite
\r
1919 spr = &spritetable[spritenumber-STARTSPRITES];
\r
1920 block = (spritetype _seg *)grsegs[spritenumber];
\r
1924 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");
\r
1925 itoa (spritenumber,str2,10);
\r
1926 strcat (str,str2);
\r
1930 globaly+=spr->orgy;
\r
1931 globalx+=spr->orgx;
\r
1933 pixx = globalx >> G_SY_SHIFT;
\r
1937 shift = (pixx&7)/2;
\r
1939 sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);
\r
1940 sprite->screeny = globaly >> G_SY_SHIFT;
\r
1941 sprite->width = block->width[shift];
\r
1942 sprite->height = spr->height;
\r
1943 sprite->grseg = spritenumber;
\r
1944 sprite->sourceofs = block->sourceoffset[shift];
\r
1945 sprite->planesize = block->planesize[shift];
\r
1946 sprite->draw = draw;
\r
1947 sprite->priority = priority;
\r
1948 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
1949 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
1950 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
1951 - sprite->tilex + 1;
\r
1952 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
1953 - sprite->tiley + 1;
\r
1955 sprite->updatecount = 2; // draw on next two refreshes
\r
1957 // save the sprite pointer off in the user's pointer so it can be moved
\r
1963 //===========================================================================
\r
1966 =====================
\r
1968 = RF_RemoveSprite EGA
\r
1970 =====================
\r
1973 void RF_RemoveSprite (void **user)
\r
1975 spritelisttype *sprite,*next;
\r
1977 sprite = (spritelisttype *)*user;
\r
1982 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1983 // both pages may not need to be erased if the sprite just changed last frame
\r
1985 if (sprite->updatecount<2)
\r
1987 if (!sprite->updatecount)
\r
1988 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1989 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1993 // unlink the sprite node
\r
1995 next = sprite->nextsprite;
\r
1996 if (next) // if (!next), sprite is last in chain
\r
1997 next->prevptr = sprite->prevptr;
\r
1998 *sprite->prevptr = next;
\r
2001 // add it back to the free list
\r
2003 sprite->nextsprite = spritefreeptr;
\r
2004 spritefreeptr = sprite;
\r
2007 // null the users pointer, so next time that actor gets placed, it will
\r
2008 // allocate a new block
\r
2015 //===========================================================================
\r
2019 ====================
\r
2021 = RFL_EraseBlocks EGA
\r
2023 = Write mode 1 should be set
\r
2025 ====================
\r
2028 void RFL_EraseBlocks (void)
\r
2030 eraseblocktype *block,*done;
\r
2031 int screenxh,screenyh;
\r
2032 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
2034 unsigned updatedelta;
\r
2035 unsigned erasecount;
\r
2041 block = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
2043 done = eraselistptr[otherpage];
\r
2045 while (block != done)
\r
2049 // clip the block to the current screen view
\r
2051 block->screenx -= originxscreen;
\r
2052 block->screeny -= originyscreen;
\r
2054 if (block->screenx < 0)
\r
2056 block->width += block->screenx;
\r
2057 if (block->width<1)
\r
2059 block->screenx = 0;
\r
2062 if (block->screeny < 0)
\r
2064 block->height += block->screeny;
\r
2065 if (block->height<1)
\r
2067 block->screeny = 0;
\r
2070 screenxh = block->screenx + block->width;
\r
2071 screenyh = block->screeny + block->height;
\r
2073 if (screenxh > EGAPORTSCREENWIDE)
\r
2075 block->width = EGAPORTSCREENWIDE-block->screenx;
\r
2076 screenxh = block->screenx + block->width;
\r
2079 if (screenyh > PORTSCREENHIGH)
\r
2081 block->height = PORTSCREENHIGH-block->screeny;
\r
2082 screenyh = block->screeny + block->height;
\r
2085 if (block->width<1 || block->height<1)
\r
2089 // erase the block by copying from the master screen
\r
2091 pos = ylookup[block->screeny]+block->screenx;
\r
2092 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2093 block->width,block->height);
\r
2096 // put 2s in update where the block was, to force sprites to update
\r
2098 xtl = block->screenx >> SX_T_SHIFT;
\r
2099 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2100 ytl = block->screeny >> SY_T_SHIFT;
\r
2101 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2103 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2104 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2106 for (y=ytl;y<=yth;y++)
\r
2108 for (x=xtl;x<=xth;x++)
\r
2109 *updatespot++ = 2;
\r
2110 updatespot += updatedelta; // down to next line
\r
2119 eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
2121 strcpy (scratch,"\tErase:");
\r
2122 itoa (erasecount,str,10);
\r
2123 strcat (scratch,str);
\r
2124 write (profilehandle,scratch,strlen(scratch));
\r
2131 ====================
\r
2133 = RFL_UpdateSprites EGA
\r
2135 = NOTE: Implement vertical clipping!
\r
2137 ====================
\r
2140 void RFL_UpdateSprites (void)
\r
2142 spritelisttype *sprite;
\r
2143 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2146 byte *updatespot,*baseupdatespot;
\r
2147 unsigned updatedelta;
\r
2148 unsigned updatecount;
\r
2149 unsigned height,sourceofs;
\r
2155 for (priority=0;priority<PRIORITIES;priority++)
\r
2157 if (priority==MASKEDTILEPRIORITY)
\r
2158 RFL_MaskForegroundTiles ();
\r
2160 for (sprite = prioritystart[priority]; sprite ;
\r
2161 sprite = (spritelisttype *)sprite->nextsprite)
\r
2164 // see if the sprite has any visable area in the port
\r
2167 portx = sprite->screenx - originxscreen;
\r
2168 porty = sprite->screeny - originyscreen;
\r
2169 xtl = portx >> SX_T_SHIFT;
\r
2170 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2171 ytl = porty >> SY_T_SHIFT;
\r
2172 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2176 if (xth>=PORTTILESWIDE)
\r
2177 xth = PORTTILESWIDE-1;
\r
2180 if (yth>=PORTTILESHIGH)
\r
2181 yth = PORTTILESHIGH-1;
\r
2183 if (xtl>xth || ytl>yth)
\r
2187 // see if it's visable area covers any non 0 update tiles
\r
2189 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2190 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2192 if (sprite->updatecount)
\r
2194 sprite->updatecount--; // the sprite was just placed,
\r
2195 goto redraw; // so draw it for sure
\r
2198 for (y=ytl;y<=yth;y++)
\r
2200 for (x=xtl;x<=xth;x++)
\r
2201 if (*updatespot++)
\r
2203 updatespot += updatedelta; // down to next line
\r
2205 continue; // no need to update
\r
2209 // set the tiles it covers to 3, because those tiles are being
\r
2212 updatespot = baseupdatespot;
\r
2213 for (y=ytl;y<=yth;y++)
\r
2215 for (x=xtl;x<=xth;x++)
\r
2216 *updatespot++ = 3;
\r
2217 updatespot += updatedelta; // down to next line
\r
2222 height = sprite->height;
\r
2223 sourceofs = sprite->sourceofs;
\r
2226 height += porty; // clip top off
\r
2227 sourceofs -= porty*sprite->width;
\r
2230 else if (porty+height>PORTSCREENHIGH)
\r
2232 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2235 dest = bufferofs + ylookup[porty] + portx;
\r
2237 switch (sprite->draw)
\r
2240 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2241 dest,sprite->width,height,sprite->planesize);
\r
2245 VW_InverseMask(grsegs[sprite->grseg], sourceofs,
\r
2246 dest,sprite->width,height);
\r
2258 strcpy (scratch,"\tSprites:");
\r
2259 itoa (updatecount,str,10);
\r
2260 strcat (scratch,str);
\r
2261 write (profilehandle,scratch,strlen(scratch));
\r
2268 =====================
\r
2272 = All routines will draw at the port at bufferofs, possibly copying from
\r
2273 = the port at masterofs. The EGA version then page flips, while the
\r
2274 = CGA version updates the screen from the buffer port.
\r
2276 = Screenpage is the currently displayed page, not the one being drawn
\r
2277 = Otherpage is the page to be worked with now
\r
2279 =====================
\r
2282 void RF_Refresh (void)
\r
2286 updateptr = updatestart[otherpage];
\r
2288 RFL_AnimateTiles (); // DEBUG
\r
2291 // update newly scrolled on tiles and animated tiles from the master screen
\r
2295 RFL_UpdateTiles ();
\r
2296 RFL_EraseBlocks ();
\r
2299 // Update is all 0 except where sprites have changed or new area has
\r
2300 // been scrolled on. Go through all sprites and update the ones that cover
\r
2301 // a non 0 update tile
\r
2304 RFL_UpdateSprites ();
\r
2307 // if the main program has a refresh hook set, call their function before
\r
2308 // displaying the new page
\r
2310 if (refreshvector)
\r
2314 // display the changed screen
\r
2316 VW_SetScreen(bufferofs+panadjust,panx & xpanmask);
\r
2319 // prepare for next refresh
\r
2321 // Set the update array to the middle position and clear it out to all "0"s
\r
2322 // with an UPDATETERMINATE at the end
\r
2324 updatestart[otherpage] = newupdate = baseupdatestart[otherpage];
\r
2328 asm mov cx,(UPDATESCREENSIZE-2)/2
\r
2329 asm mov di,[newupdate]
\r
2331 asm mov [WORD PTR es:di],UPDATETERMINATE
\r
2335 bufferofs = screenstart[otherpage];
\r
2336 displayofs = screenstart[screenpage];
\r
2339 // calculate tics since last refresh for adaptive timing
\r
2344 #endif // GRMODE == EGAGR
\r
2347 =============================================================================
\r
2349 CGA specific routines
\r
2351 =============================================================================
\r
2354 #if GRMODE == CGAGR
\r
2358 =====================
\r
2360 = RF_NewPosition CGA
\r
2362 =====================
\r
2365 void RF_NewPosition (unsigned x, unsigned y)
\r
2369 unsigned updatenum;
\r
2371 RFL_BoundNewOrigin (x,y);
\r
2374 // clear out all animating tiles
\r
2376 RFL_InitAnimList ();
\r
2379 // set up the new update arrays at base position
\r
2381 updateptr = baseupdateptr;
\r
2383 spotptr = updateptr + PORTTILESWIDE; // used to stick "0"s after rows
\r
2385 updatenum = 0; // start at first visable tile
\r
2387 for (my=0;my<PORTTILESHIGH;my++)
\r
2389 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
2391 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
2392 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
2396 *spotptr = 0; // set a 0 at end of a line of tiles
\r
2397 spotptr +=(PORTTILESWIDE+1);
\r
2399 *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
2404 =====================
\r
2408 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
2409 = scroll if needed. If the scroll distance is greater than one tile, the
\r
2410 = entire screen will be redrawn (this could be generalized, but scrolling
\r
2411 = more than one tile per refresh is a bad idea!).
\r
2413 =====================
\r
2416 void RF_Scroll (int x, int y)
\r
2418 long neworgx,neworgy;
\r
2419 int i,deltax,deltay,absdx,absdy;
\r
2420 int oldxt,oldyt,move,yy;
\r
2421 unsigned updatespot;
\r
2423 unsigned oldoriginmap,oldscreen,newscreen,screencopy;
\r
2426 oldxt = originxtile;
\r
2427 oldyt = originytile;
\r
2429 RFL_BoundScroll (x,y);
\r
2431 deltax = originxtile - oldxt;
\r
2432 absdx = abs(deltax);
\r
2433 deltay = originytile - oldyt;
\r
2434 absdy = abs(deltay);
\r
2436 if (absdx>1 || absdy>1)
\r
2439 // scrolled more than one tile, so start from scratch
\r
2441 RF_NewPosition(originxglobal,originyglobal);
\r
2445 if (!absdx && !absdy)
\r
2446 return; // the screen has not scrolled an entire tile
\r
2452 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
2453 bufferofs += screenmove;
\r
2454 masterofs += screenmove;
\r
2458 // float the update regions
\r
2462 move += UPDATEWIDE;
\r
2463 else if (deltay==-1)
\r
2464 move -= UPDATEWIDE;
\r
2469 // draw the new tiles just scrolled on to the master screen, and
\r
2470 // mark them as needing to be copied to each screen next refreshes
\r
2471 // Make sure a zero is at the end of each row in update
\r
2478 RFL_NewRow (1); // new right row
\r
2479 RFL_RemoveAnimsOnX (originxtile-1);
\r
2483 RFL_NewRow (3); // new left row
\r
2484 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
2487 spotptr = updateptr+PORTTILESWIDE;
\r
2488 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
2490 *spotptr = 0; // drop a 0 at end of each row
\r
2491 spotptr+=UPDATEWIDE;
\r
2495 //----------------
\r
2501 RFL_NewRow (2); // new bottom row
\r
2502 *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;
\r
2503 RFL_RemoveAnimsOnY (originytile-1);
\r
2507 RFL_NewRow (0); // new top row
\r
2508 *(updateptr+PORTTILESWIDE) = 0;
\r
2509 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
2513 //----------------
\r
2516 // place a new terminator
\r
2518 spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;
\r
2520 *(unsigned *)spotptr = UPDATETERMINATE;
\r
2524 =====================
\r
2526 = RF_PlaceSprite CGA
\r
2528 =====================
\r
2531 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
2532 unsigned spritenumber, drawtype draw, int priority)
\r
2534 spritelisttype register *sprite,*next;
\r
2535 spritetabletype far *spr;
\r
2536 spritetype _seg *block;
\r
2537 unsigned shift,pixx;
\r
2538 char str[80],str2[10];
\r
2540 if (!spritenumber || spritenumber == (unsigned)-1)
\r
2542 RF_RemoveSprite (user);
\r
2546 sprite = (spritelisttype *)*user;
\r
2550 // sprite allready exists in the list, so we can use it's block
\r
2553 // post an erase block to erase the old position by copying
\r
2554 // screenx,screeny,width,height
\r
2556 if (!sprite->updatecount) // may not have been drawn at all yet
\r
2557 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2559 if (priority != sprite->priority)
\r
2561 // sprite moved to another priority, so unlink the old one and
\r
2562 // relink it in the new priority
\r
2564 next = sprite->nextsprite; // cut old links
\r
2566 next->prevptr = sprite->prevptr;
\r
2567 *sprite->prevptr = next;
\r
2573 // this is a brand new sprite, so allocate a block from the array
\r
2575 if (!spritefreeptr)
\r
2576 Quit ("RF_PlaceSprite: No free spots in spritearray!");
\r
2578 sprite = spritefreeptr;
\r
2579 spritefreeptr = spritefreeptr->nextsprite;
\r
2582 next = prioritystart[priority]; // stick it in new spot
\r
2584 next->prevptr = &sprite->nextsprite;
\r
2585 sprite->nextsprite = next;
\r
2586 prioritystart[priority] = sprite;
\r
2587 sprite->prevptr = &prioritystart[priority];
\r
2591 // write the new info to the sprite
\r
2593 spr = &spritetable[spritenumber-STARTSPRITES];
\r
2594 block = (spritetype _seg *)grsegs[spritenumber];
\r
2598 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");
\r
2599 itoa (spritenumber,str2,10);
\r
2600 strcat (str,str2);
\r
2605 globaly+=spr->orgy;
\r
2606 globalx+=spr->orgx;
\r
2608 sprite->screenx = globalx >> G_CGASX_SHIFT;
\r
2609 sprite->screeny = globaly >> G_SY_SHIFT;
\r
2610 sprite->width = block->width[0];
\r
2611 sprite->height = spr->height;
\r
2612 sprite->grseg = spritenumber;
\r
2613 sprite->sourceofs = block->sourceoffset[0];
\r
2614 sprite->planesize = block->planesize[0];
\r
2615 sprite->draw = draw;
\r
2616 sprite->priority = priority;
\r
2617 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
2618 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
2619 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
2620 - sprite->tilex + 1;
\r
2621 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
2622 - sprite->tiley + 1;
\r
2624 sprite->updatecount = 1; // draw on next refresh
\r
2626 // save the sprite pointer off in the user's pointer so it can be moved
\r
2632 //===========================================================================
\r
2635 =====================
\r
2637 = RF_RemoveSprite CGA
\r
2639 =====================
\r
2642 void RF_RemoveSprite (void **user)
\r
2644 spritelisttype *sprite,*next;
\r
2646 sprite = (spritelisttype *)*user;
\r
2651 // post an erase block to erase the old position by copying
\r
2652 // screenx,screeny,width,height
\r
2654 if (!sprite->updatecount)
\r
2656 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2660 // unlink the sprite node
\r
2662 next = sprite->nextsprite;
\r
2663 if (next) // if (!next), sprite is last in chain
\r
2664 next->prevptr = sprite->prevptr;
\r
2665 *sprite->prevptr = next;
\r
2668 // add it back to the free list
\r
2670 sprite->nextsprite = spritefreeptr;
\r
2671 spritefreeptr = sprite;
\r
2674 // null the users pointer, so next time that actor gets placed, it will
\r
2675 // allocate a new block
\r
2683 ====================
\r
2685 = RFL_EraseBlocks CGA
\r
2687 = Write mode 1 should be set
\r
2689 ====================
\r
2692 void RFL_EraseBlocks (void)
\r
2694 eraseblocktype *block,*done;
\r
2695 int screenxh,screenyh;
\r
2696 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
2698 unsigned updatedelta;
\r
2700 block = &eraselist[0][0];
\r
2702 done = eraselistptr[0];
\r
2704 while (block != done)
\r
2708 // clip the block to the current screen view
\r
2710 block->screenx -= originxscreen;
\r
2711 block->screeny -= originyscreen;
\r
2713 if (block->screenx < 0)
\r
2715 block->width += block->screenx;
\r
2716 if (block->width<1)
\r
2718 block->screenx = 0;
\r
2721 if (block->screeny < 0)
\r
2723 block->height += block->screeny;
\r
2724 if (block->height<1)
\r
2726 block->screeny = 0;
\r
2729 screenxh = block->screenx + block->width;
\r
2730 screenyh = block->screeny + block->height;
\r
2732 if (screenxh > CGAPORTSCREENWIDE)
\r
2734 block->width = CGAPORTSCREENWIDE-block->screenx;
\r
2735 screenxh = block->screenx + block->width;
\r
2738 if (screenyh > PORTSCREENHIGH)
\r
2740 block->height = PORTSCREENHIGH-block->screeny;
\r
2741 screenyh = block->screeny + block->height;
\r
2744 if (block->width<1 || block->height<1)
\r
2748 // erase the block by copying from the master screen
\r
2750 pos = ylookup[block->screeny]+block->screenx;
\r
2751 block->width = (block->width + (pos&1) + 1)& ~1;
\r
2752 pos &= ~1; // make sure a word copy gets used
\r
2753 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2754 block->width,block->height);
\r
2757 // put 2s in update where the block was, to force sprites to update
\r
2759 xtl = block->screenx >> SX_T_SHIFT;
\r
2760 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2761 ytl = block->screeny >> SY_T_SHIFT;
\r
2762 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2764 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2765 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2767 for (y=ytl;y<=yth;y++)
\r
2769 for (x=xtl;x<=xth;x++)
\r
2770 *updatespot++ = 2;
\r
2771 updatespot += updatedelta; // down to next line
\r
2777 eraselistptr[0] = &eraselist[0][0];
\r
2782 ====================
\r
2784 = RFL_UpdateSprites CGA
\r
2786 = NOTE: Implement vertical clipping!
\r
2788 ====================
\r
2791 void RFL_UpdateSprites (void)
\r
2793 spritelisttype *sprite;
\r
2794 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2797 byte *updatespot,*baseupdatespot;
\r
2798 unsigned updatedelta;
\r
2800 unsigned updatecount;
\r
2801 unsigned height,sourceofs;
\r
2808 for (priority=0;priority<PRIORITIES;priority++)
\r
2810 if (priority==MASKEDTILEPRIORITY)
\r
2811 RFL_MaskForegroundTiles ();
\r
2813 for (sprite = prioritystart[priority]; sprite ;
\r
2814 sprite = (spritelisttype *)sprite->nextsprite)
\r
2817 // see if the sprite has any visable area in the port
\r
2820 portx = sprite->screenx - originxscreen;
\r
2821 porty = sprite->screeny - originyscreen;
\r
2822 xtl = portx >> SX_T_SHIFT;
\r
2823 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2824 ytl = porty >> SY_T_SHIFT;
\r
2825 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2829 if (xth>=PORTTILESWIDE)
\r
2830 xth = PORTTILESWIDE-1;
\r
2833 if (yth>=PORTTILESHIGH)
\r
2834 yth = PORTTILESHIGH-1;
\r
2836 if (xtl>xth || ytl>yth)
\r
2840 // see if it's visable area covers any non 0 update tiles
\r
2842 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2843 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2845 if (sprite->updatecount)
\r
2847 sprite->updatecount--; // the sprite was just placed,
\r
2848 goto redraw; // so draw it for sure
\r
2851 for (y=ytl;y<=yth;y++)
\r
2853 for (x=xtl;x<=xth;x++)
\r
2854 if (*updatespot++)
\r
2856 updatespot += updatedelta; // down to next line
\r
2858 continue; // no need to update
\r
2862 // set the tiles it covers to 3, because those tiles are being
\r
2865 updatespot = baseupdatespot;
\r
2866 for (y=ytl;y<=yth;y++)
\r
2868 for (x=xtl;x<=xth;x++)
\r
2869 *updatespot++ = 3;
\r
2870 updatespot += updatedelta; // down to next line
\r
2875 height = sprite->height;
\r
2876 sourceofs = sprite->sourceofs;
\r
2879 height += porty; // clip top off
\r
2880 sourceofs -= porty*sprite->width;
\r
2883 else if (porty+height>PORTSCREENHIGH)
\r
2885 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2888 dest = bufferofs + ylookup[porty] + portx;
\r
2890 switch (sprite->draw)
\r
2893 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2894 dest,sprite->width,height,sprite->planesize);
\r
2898 VW_InverseMask(grsegs[sprite->grseg], sourceofs,
\r
2899 dest,sprite->width,height);
\r
2914 =====================
\r
2918 = All routines will draw at the port at bufferofs, possibly copying from
\r
2919 = the port at masterofs. The EGA version then page flips, while the
\r
2920 = CGA version updates the screen from the buffer port.
\r
2922 = Screenpage is the currently displayed page, not the one being drawn
\r
2923 = Otherpage is the page to be worked with now
\r
2925 =====================
\r
2928 void RF_Refresh (void)
\r
2930 long newtime,oldtimecount;
\r
2932 RFL_AnimateTiles ();
\r
2935 // update newly scrolled on tiles and animated tiles from the master screen
\r
2937 RFL_UpdateTiles ();
\r
2938 RFL_EraseBlocks ();
\r
2941 // Update is all 0 except where sprites have changed or new area has
\r
2942 // been scrolled on. Go through all sprites and update the ones that cover
\r
2943 // a non 0 update tile
\r
2945 RFL_UpdateSprites ();
\r
2948 // if the main program has a refresh hook set, call their function before
\r
2949 // displaying the new page
\r
2951 if (refreshvector)
\r
2955 // update everything to the screen
\r
2957 VW_CGAFullUpdate ();
\r
2960 // calculate tics since last refresh for adaptive timing
\r
2965 #endif // GRMODE == CGAGR
\r