]> 4ch.mooo.com Git - 16.git/blobdiff - 16/keen456/KEEN4-6/ID_RF.C
extrcted keen code remake
[16.git] / 16 / keen456 / KEEN4-6 / ID_RF.C
diff --git a/16/keen456/KEEN4-6/ID_RF.C b/16/keen456/KEEN4-6/ID_RF.C
new file mode 100755 (executable)
index 0000000..a040c6b
--- /dev/null
@@ -0,0 +1,2965 @@
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is primarily based on:\r
+ * Catacomb 3-D Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_RF.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+notes\r
+-----\r
+\r
+scrolling more than one tile / refresh forces a total redraw\r
+\r
+two overlapping sprites of equal priority can change drawing order when\r
+updated\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define        SCREENTILESWIDE 20\r
+#define        SCREENTILESHIGH 13\r
+\r
+#define        SCREENSPACE             (SCREENWIDTH*240)\r
+#define FREEEGAMEM             (0x10000l-3l*SCREENSPACE)\r
+\r
+//\r
+// the update array must have enough space for two screens that can float\r
+// up two two tiles each way\r
+//\r
+// (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared\r
+// by word width instructions\r
+\r
+#define        UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define        UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
+#define UPDATESIZE                     (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+\r
+#define G_EGASX_SHIFT  7       // global >> ?? = screen x\r
+#define G_CGASX_SHIFT  6       // global >> ?? = screen x\r
+#define G_SY_SHIFT             4       // global >> ?? = screen y\r
+\r
+unsigned       SX_T_SHIFT;             // screen x >> ?? = tile EGA = 1, CGA = 2;\r
+#define        SY_T_SHIFT              4       // screen y >> ?? = tile\r
+\r
+\r
+#define        EGAPORTSCREENWIDE       42\r
+#define        CGAPORTSCREENWIDE       84\r
+#define        PORTSCREENHIGH          224\r
+\r
+#define        UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define        UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
+#define UPDATESIZE                     (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+\r
+#define MAXSCROLLEDGES 6\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                  LOCAL TYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+typedef        struct spriteliststruct\r
+{\r
+       int                     screenx,screeny;\r
+       int                     width,height;\r
+\r
+       unsigned        grseg,sourceofs,planesize;\r
+       drawtype        draw;\r
+       unsigned        tilex,tiley,tilewide,tilehigh;\r
+       int                     priority,updatecount;\r
+       struct spriteliststruct **prevptr,*nextsprite;\r
+} spritelisttype;\r
+\r
+\r
+typedef struct\r
+{\r
+       int                     screenx,screeny;\r
+       int                     width,height;\r
+} eraseblocktype;\r
+\r
+\r
+typedef struct\r
+{\r
+       unsigned        current;                // foreground tiles have high bit set\r
+       int                     count;\r
+#ifdef KEEN6\r
+       unsigned        soundtile;\r
+       unsigned        visible;\r
+       int             sound;\r
+#endif\r
+} tiletype;\r
+\r
+\r
+typedef struct animtilestruct\r
+{\r
+       unsigned        x,y,tile;\r
+       tiletype        *chain;\r
+       unsigned        far *mapplane;\r
+       struct animtilestruct **prevptr,*nexttile;\r
+} animtiletype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+unsigned       tics;\r
+long           lasttimecount;\r
+\r
+boolean                compatability;                  // crippled refresh for wierdo SVGAs\r
+\r
+unsigned       mapwidth,mapheight,mapbyteswide,mapwordswide\r
+                       ,mapbytesextra,mapwordsextra;\r
+unsigned       mapbwidthtable[MAXMAPHEIGHT];\r
+\r
+//\r
+// Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow\r
+// for fractional movement and acceleration.\r
+//\r
+// Tiles  : Tile offsets from the upper left corner of the current map.\r
+//\r
+// Screen : Graphics level offsets from map origin, x in bytes, y in pixels.\r
+// originxscreen is the same spot as originxtile, just with extra precision\r
+// so graphics don't need to be done in tile boundaries.\r
+//\r
+\r
+unsigned       originxglobal,originyglobal;\r
+unsigned       originxtile,originytile;\r
+unsigned       originxscreen,originyscreen;\r
+unsigned       originmap;\r
+unsigned       originxmin,originxmax,originymin,originymax;\r
+\r
+unsigned       masterofs;\r
+\r
+//\r
+// Table of the offsets from bufferofs of each tile spot in the\r
+// view port.  The extra wide tile should never be drawn, but the space\r
+// is needed to account for the extra 0 in the update arrays.  Built by\r
+// RF_Startup\r
+//\r
+\r
+unsigned       blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+unsigned       updatemapofs[UPDATEWIDE*UPDATEHIGH];\r
+\r
+unsigned       uwidthtable[PORTTILESHIGH];             // lookup instead of multiply\r
+\r
+byte           update[2][UPDATESIZE];\r
+byte           *updateptr,*baseupdateptr,                                              // current start of update window\r
+                       *updatestart[2],\r
+                       *baseupdatestart[2];\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+static         char    scratch[20],str[80];\r
+\r
+tiletype       allanims[MAXANIMTYPES];\r
+unsigned       numanimchains;\r
+\r
+void           (*refreshvector) (void);\r
+\r
+unsigned       screenstart[3] =\r
+       {0,SCREENSPACE,SCREENSPACE*2};\r
+\r
+unsigned       xpanmask;                       // prevent panning to odd pixels\r
+\r
+unsigned       screenpage;                     // screen currently being displayed\r
+unsigned       otherpage;\r
+\r
+\r
+spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],\r
+                               *spritefreeptr;\r
+\r
+animtiletype   animarray[MAXANIMTILES],*animhead,*animfreeptr;\r
+\r
+int                            animfreespot;\r
+\r
+eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];\r
+\r
+int            hscrollblocks,vscrollblocks;\r
+int            hscrolledge[MAXSCROLLEDGES],vscrolledge[MAXSCROLLEDGES];\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL PROTOTYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void RFL_NewTile (unsigned updateoffset);\r
+void RFL_MaskForegroundTiles (void);\r
+void RFL_UpdateTiles (void);\r
+\r
+void RFL_BoundScroll (int x, int y);\r
+void RFL_CalcOriginStuff (long x, long y);\r
+void RFL_ClearScrollBlocks (void);\r
+void RFL_InitSpriteList (void);\r
+void RFL_InitAnimList (void);\r
+void RFL_CheckForAnimTile (unsigned x, unsigned y);\r
+void RFL_AnimateTiles (void);\r
+void RFL_RemoveAnimsOnX (unsigned x);\r
+void RFL_RemoveAnimsOnY (unsigned y);\r
+void RFL_EraseBlocks (void);\r
+void RFL_UpdateSprites (void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                        GRMODE INDEPENDANT ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Startup\r
+=\r
+=====================\r
+*/\r
+\r
+static char *ParmStrings[] = {"comp",""};\r
+\r
+void RF_Startup (void)\r
+{\r
+       int i,x,y;\r
+       unsigned        *blockstart;\r
+\r
+#ifndef KEEN\r
+       //\r
+       // Keen 4-6 store the compatability setting in the game's config file.\r
+       // The setting is loaded from that file AFTER RF_Startup is executed,\r
+       // making this check useless (unless the config file doesn't exist).\r
+       // Instead, US_Startup now checks for that parameter after the config\r
+       // file has been read.\r
+       //\r
+       if (grmode == EGAGR)\r
+               for (i = 1;i < _argc;i++)\r
+                       if (US_CheckParm(_argv[i],ParmStrings) == 0)\r
+                       {\r
+                               compatability = true;\r
+                               break;\r
+                       }\r
+#endif\r
+\r
+       for (i=0;i<PORTTILESHIGH;i++)\r
+               uwidthtable[i] = UPDATEWIDE*i;\r
+\r
+       originxmin = originymin = MAPBORDER*TILEGLOBAL;\r
+\r
+       eraselistptr[0] = &eraselist[0][0];\r
+       eraselistptr[1] = &eraselist[1][0];\r
+\r
+\r
+\r
+       if (grmode == EGAGR)\r
+       {\r
+               SX_T_SHIFT = 1;\r
+\r
+               baseupdatestart[0] = &update[0][UPDATESPARESIZE];\r
+               baseupdatestart[1] = &update[1][UPDATESPARESIZE];\r
+\r
+               screenpage = 0;\r
+               otherpage = 1;\r
+               displayofs = screenstart[screenpage];\r
+               bufferofs = screenstart[otherpage];\r
+               masterofs = screenstart[2];\r
+\r
+               updateptr = baseupdatestart[otherpage];\r
+\r
+               blockstart = &blockstarts[0];\r
+               for (y=0;y<UPDATEHIGH;y++)\r
+                       for (x=0;x<UPDATEWIDE;x++)\r
+                               *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+\r
+               xpanmask = 6;   // dont pan to odd pixels\r
+       }\r
+\r
+       else if (grmode == CGAGR)\r
+       {\r
+               SX_T_SHIFT = 2;\r
+\r
+               updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];\r
+\r
+               bufferofs = 0;\r
+               masterofs = 0x8000;\r
+\r
+               blockstart = &blockstarts[0];\r
+               for (y=0;y<UPDATEHIGH;y++)\r
+                       for (x=0;x<UPDATEWIDE;x++)\r
+                               *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Shutdown\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Shutdown (void)\r
+{\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_FixOfs\r
+=\r
+= Sets bufferofs,displayofs, and masterofs to regular values, for the\r
+= occasions when you have moved them around manually\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_FixOfs (void)\r
+{\r
+       screenstart[0] = 0;\r
+       screenstart[1] = SCREENSPACE;\r
+       screenstart[2] = SCREENSPACE*2;\r
+\r
+       if (grmode == EGAGR)\r
+       {\r
+               screenpage = 0;\r
+               otherpage = 1;\r
+               panx = pany = pansx = pansy = panadjust = 0;\r
+               displayofs = screenstart[screenpage];\r
+               bufferofs = screenstart[otherpage];\r
+               masterofs = screenstart[2];\r
+               VW_SetScreen (displayofs,0);\r
+       }\r
+       else\r
+       {\r
+               panx = pany = pansx = pansy = panadjust = 0;\r
+               bufferofs = 0;\r
+               masterofs = 0x8000;\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewMap\r
+=\r
+= Makes some convienient calculations based on maphead->\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewMap (void)\r
+{\r
+       int i,x,y;\r
+       unsigned spot,*table;\r
+\r
+       mapwidth = mapheaderseg[mapon]->width;\r
+       mapbyteswide = 2*mapwidth;\r
+       mapheight = mapheaderseg[mapon]->height;\r
+       mapwordsextra = mapwidth-PORTTILESWIDE;\r
+       mapbytesextra = 2*mapwordsextra;\r
+\r
+//\r
+// make a lookup table for the maps left edge\r
+//\r
+       if (mapheight > MAXMAPHEIGHT)\r
+               Quit ("RF_NewMap: Map too tall!");\r
+       spot = 0;\r
+       for (i=0;i<mapheight;i++)\r
+       {\r
+         mapbwidthtable[i] = spot;\r
+         spot += mapbyteswide;\r
+       }\r
+\r
+//\r
+// fill in updatemapofs with the new width info\r
+//\r
+       table = &updatemapofs[0];\r
+       for (y=0;y<PORTTILESHIGH;y++)\r
+               for (x=0;x<UPDATEWIDE;x++)\r
+                       *table++ = mapbwidthtable[y]+x*2;\r
+\r
+//\r
+// the y max value clips off the bottom half of a tile so a map that is\r
+// 13 + MAPBORDER*2 tile high will not scroll at all vertically\r
+//\r
+       originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;\r
+       originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;\r
+       if (originxmax<originxmin)              // for very small maps\r
+               originxmax=originxmin;\r
+       if (originymax<originymin)\r
+               originymax=originymin;\r
+\r
+//\r
+// clear out the lists\r
+//\r
+       RFL_InitSpriteList ();\r
+       RFL_InitAnimList ();\r
+       RFL_ClearScrollBlocks ();\r
+       RF_SetScrollBlock (0,MAPBORDER-1,true);\r
+       RF_SetScrollBlock (0,mapheight-MAPBORDER,true);\r
+       RF_SetScrollBlock (MAPBORDER-1,0,false);\r
+       RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);\r
+\r
+\r
+       lasttimecount = TimeCount;              // setup for adaptive timing\r
+       tics = 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+#ifdef KEEN6\r
+/*\r
+==========================\r
+=\r
+= RFL_CheckTileSound\r
+=\r
+= Checks if the tile plays a sound and if so adds that info to the animation\r
+=\r
+==========================\r
+*/\r
+\r
+#define NUMSOUNDTILES 2\r
+typedef struct {\r
+       unsigned tilenums[NUMSOUNDTILES];\r
+       int sounds[NUMSOUNDTILES];\r
+} tilesoundtype;\r
+\r
+tilesoundtype far soundtiles = {\r
+       {2152|0x8000, 2208|0x8000},\r
+       {SND_STOMP,   SND_FLAME}\r
+};\r
+\r
+void RFL_CheckTileSound(tiletype *anim, unsigned tile)\r
+{\r
+       int i;\r
+\r
+       for (i=0; i<NUMSOUNDTILES; i++)\r
+       {\r
+               if (soundtiles.tilenums[i] == tile)\r
+               {\r
+                       anim->soundtile = tile;\r
+                       anim->sound = soundtiles.sounds[i];\r
+                       break;\r
+               }\r
+       }\r
+}\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= RF_MarkTileGraphics\r
+=\r
+= Goes through mapplane[0/1] and marks all background/foreground tiles\r
+= needed, then follows all animation sequences to make sure animated\r
+= tiles get all the stages.  Every unique animating tile is given an\r
+= entry in allanims[], so every instance of that tile will animate at the\r
+= same rate.  The info plane for each animating tile will hold a pointer\r
+= into allanims[], therefore you can't have both an animating foreground\r
+= and background tile in the same spot!\r
+=\r
+==========================\r
+*/\r
+\r
+void RF_MarkTileGraphics (void)\r
+{\r
+       unsigned        size;\r
+       int                     tile,next,anims,change;\r
+       unsigned        far     *start,far *end,far *info;\r
+       unsigned        i,tilehigh;\r
+       char            str[80],str2[10];\r
+\r
+       memset (allanims,0,sizeof(allanims));\r
+       numanimchains = 0;\r
+\r
+       size = mapwidth*mapheight;\r
+\r
+//\r
+// background plane\r
+//\r
+       start = mapsegs[0];\r
+       info = mapsegs[2];\r
+       end = start+size;\r
+       do\r
+       {\r
+               tile = *start++;\r
+               if (tile>=0)                    // <0 is a tile that is never drawn\r
+               {\r
+                       CA_MarkGrChunk(STARTTILE16+tile);\r
+                       if (tinf[ANIM+tile])\r
+                       {\r
+                               // this tile will animated\r
+\r
+                               if (tinf[SPEED+tile])\r
+                               {\r
+                                       if (!tinf[ANIM+tile])\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Background anim of 0:");\r
+                                               itoa (tile,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                                       for (i=0;i<numanimchains;i++)\r
+                                               if (allanims[i].current == tile)\r
+                                               {\r
+                                                       *info = (unsigned)&allanims[i];\r
+                                                       goto nextback;\r
+                                               }\r
+\r
+                                       // new chain of animating tiles\r
+\r
+                                       if (i>=MAXANIMTYPES)\r
+                                               Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
+                                       allanims[i].current = tile;\r
+                                       allanims[i].count = tinf[SPEED+tile];\r
+#ifdef KEEN6\r
+                                       allanims[i].visible = 0;\r
+                                       allanims[i].sound = -1;\r
+#endif\r
+                                       *info = (unsigned)&allanims[i];\r
+                                       numanimchains++;\r
+                               }\r
+#ifdef KEEN6\r
+                               RFL_CheckTileSound(&allanims[i], tile);\r
+#endif\r
+\r
+                               anims = 0;\r
+                               change = (signed char)(tinf[ANIM+tile]);\r
+                               next = tile+change;\r
+                               while (change && next != tile)\r
+                               {\r
+#ifdef KEEN6\r
+                                       RFL_CheckTileSound(&allanims[i], next);\r
+#endif\r
+                                       CA_MarkGrChunk(STARTTILE16+next);\r
+                                       change = (signed char)(tinf[ANIM+next]);\r
+                                       next += change;\r
+                                       if (++anims > 20)\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Unending background animation:");\r
+                                               itoa (next,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                               }\r
+\r
+                       }\r
+               }\r
+nextback:\r
+               info++;\r
+       } while (start<end);\r
+\r
+//\r
+// foreground plane\r
+//\r
+       start = mapsegs[1];\r
+       info = mapsegs[2];\r
+       end = start+size;\r
+       do\r
+       {\r
+               tile = *start++;\r
+               if (tile>=0)                    // <0 is a tile that is never drawn\r
+               {\r
+                       CA_MarkGrChunk(STARTTILE16M+tile);\r
+                       if (tinf[MANIM+tile])\r
+                       {\r
+                               // this tile will animated\r
+\r
+                               if (tinf[MSPEED+tile])\r
+                               {\r
+                                       if (!tinf[MANIM+tile])\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Foreground anim of 0:");\r
+                                               itoa (tile,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                                       tilehigh = tile | 0x8000;       // foreground tiles have high bit\r
+                                       for (i=0;i<numanimchains;i++)\r
+                                               if (allanims[i].current == tilehigh)\r
+                                               {\r
+                                                       *info = (unsigned)&allanims[i];\r
+                                                       goto nextfront;\r
+                                               }\r
+\r
+                                       // new chain of animating tiles\r
+\r
+                                       if (i>=MAXANIMTYPES)\r
+                                               Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
+                                       allanims[i].current = tilehigh;\r
+                                       allanims[i].count = tinf[MSPEED+tile];\r
+#ifdef KEEN6\r
+                                       allanims[i].visible = 0;\r
+                                       allanims[i].sound = -1;\r
+#endif\r
+                                       *info = (unsigned)&allanims[i];\r
+                                       numanimchains++;\r
+                               }\r
+\r
+#ifdef KEEN6\r
+                               RFL_CheckTileSound(&allanims[i], tilehigh);\r
+#endif\r
+                               anims = 0;\r
+                               change = (signed char)(tinf[MANIM+tile]);\r
+                               next = tile+change;\r
+                               while (change && next != tile)\r
+                               {\r
+#ifdef KEEN6\r
+                                       RFL_CheckTileSound(&allanims[i], next | 0x8000);        // foreground tiles have high bit\r
+#endif\r
+                                       CA_MarkGrChunk(STARTTILE16M+next);\r
+                                       change = (signed char)(tinf[MANIM+next]);\r
+                                       next += change;\r
+                                       if (++anims > 20)\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Unending foreground animation:");\r
+                                               itoa (next,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                               }\r
+\r
+                       }\r
+               }\r
+nextfront:\r
+               info++;\r
+       } while (start<end);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=========================\r
+=\r
+= RFL_InitAnimList\r
+=\r
+= Call to clear out the entire animating tile list and return all of them to\r
+= the free list.\r
+=\r
+=========================\r
+*/\r
+\r
+void RFL_InitAnimList (void)\r
+{\r
+       int     i;\r
+\r
+       animfreeptr = &animarray[0];\r
+\r
+       for (i=0;i<MAXANIMTILES-1;i++)\r
+               animarray[i].nexttile = &animarray[i+1];\r
+\r
+       animarray[i].nexttile = NULL;\r
+\r
+       animhead = NULL;                        // nothing in list\r
+\r
+#ifdef KEEN6\r
+       {\r
+               tiletype *anim;\r
+\r
+               for (anim = allanims; anim->current != 0; anim++)\r
+                       anim->visible = 0;\r
+       }\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_CheckForAnimTile\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_CheckForAnimTile (unsigned x, unsigned y)\r
+{\r
+       unsigned        tile,offset,speed,lasttime,thistime,timemissed;\r
+       unsigned        far *map;\r
+       animtiletype    *anim,*next;\r
+\r
+// the info plane of each animating tile has a near pointer into allanims[]\r
+// which gives the current state of all concurrently animating tiles\r
+\r
+       offset = mapbwidthtable[y]/2+x;\r
+\r
+//\r
+// background\r
+//\r
+       map = mapsegs[0]+offset;\r
+       tile = *map;\r
+       if (tinf[ANIM+tile] && tinf[SPEED+tile])\r
+       {\r
+               if (!animfreeptr)\r
+                       Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
+               anim = animfreeptr;\r
+               animfreeptr = animfreeptr->nexttile;\r
+               next = animhead;                                // stick it at the start of the list\r
+               animhead = anim;\r
+               if (next)\r
+                       next->prevptr = &anim->nexttile;\r
+               anim->nexttile = next;\r
+               anim->prevptr = &animhead;\r
+\r
+               anim->x = x;\r
+               anim->y = y;\r
+               anim->tile = tile;\r
+               anim->mapplane = map;\r
+               anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
+#ifdef KEEN6\r
+               anim->chain->visible++;\r
+#endif\r
+       }\r
+\r
+//\r
+// foreground\r
+//\r
+       map = mapsegs[1]+offset;\r
+       tile = *map;\r
+       if (tinf[MANIM+tile] && tinf[MSPEED+tile])\r
+       {\r
+               if (!animfreeptr)\r
+                       Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
+               anim = animfreeptr;\r
+               animfreeptr = animfreeptr->nexttile;\r
+               next = animhead;                                // stick it at the start of the list\r
+               animhead = anim;\r
+               if (next)\r
+                       next->prevptr = &anim->nexttile;\r
+               anim->nexttile = next;\r
+               anim->prevptr = &animhead;\r
+\r
+               anim->x = x;\r
+               anim->y = y;\r
+               anim->tile = tile;\r
+               anim->mapplane = map;\r
+               anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
+#ifdef KEEN6\r
+               anim->chain->visible++;\r
+#endif\r
+       }\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsOnX\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsOnX (unsigned x)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->x == x)\r
+               {\r
+#ifdef KEEN6\r
+                       current->chain->visible--;\r
+#endif\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsOnY\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsOnY (unsigned y)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->y == y)\r
+               {\r
+#ifdef KEEN6\r
+                       current->chain->visible--;\r
+#endif\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsInBlock\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->x - x < width && current->y - y < height)\r
+               {\r
+#ifdef KEEN6\r
+                       current->chain->visible--;\r
+#endif\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_AnimateTiles\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_AnimateTiles (void)\r
+{\r
+       animtiletype *current;\r
+       unsigned        updateofs,tile,x,y;\r
+       tiletype        *anim;\r
+\r
+//\r
+// animate the lists of tiles\r
+//\r
+       anim = &allanims[0];\r
+       while (anim->current)\r
+       {\r
+               anim->count-=tics;\r
+               while ( anim->count < 1)\r
+               {\r
+                       if (anim->current & 0x8000)\r
+                       {\r
+                               tile = anim->current & 0x7fff;\r
+                               tile += (signed char)tinf[MANIM+tile];\r
+                               anim->count += tinf[MSPEED+tile];\r
+                               tile |= 0x8000;\r
+                       }\r
+                       else\r
+                       {\r
+                               tile = anim->current;\r
+                               tile += (signed char)tinf[ANIM+tile];\r
+                               anim->count += tinf[SPEED+tile];\r
+                       }\r
+                       anim->current = tile;\r
+#ifdef KEEN6\r
+                       if (anim->visible && anim->current == anim->soundtile && anim->sound != -1)\r
+                       {\r
+                               SD_PlaySound(anim->sound);\r
+                       }\r
+#endif\r
+               }\r
+               anim++;\r
+       }\r
+\r
+\r
+//\r
+// traverse the list of animating tiles\r
+//\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               tile =current->chain->current;\r
+               if ( tile != current->tile)\r
+               {\r
+               // tile has animated\r
+               //\r
+               // remove tile from master screen cache,\r
+               // change a tile to its next state, set the structure up for\r
+               // next animation, and post an update region to both update pages\r
+               //\r
+                       current->tile = tile;\r
+\r
+                       *(current->mapplane) = tile & 0x7fff;           // change in map\r
+\r
+                       x = current->x-originxtile;\r
+                       y = current->y-originytile;\r
+\r
+                       if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)\r
+                               Quit ("RFL_AnimateTiles: Out of bounds!");\r
+\r
+                       updateofs = uwidthtable[y] + x;\r
+                       RFL_NewTile(updateofs);                         // puts "1"s in both pages\r
+               }\r
+               current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= RFL_InitSpriteList\r
+=\r
+= Call to clear out the entire sprite list and return all of them to\r
+= the free list.\r
+=\r
+=========================\r
+*/\r
+\r
+void RFL_InitSpriteList (void)\r
+{\r
+       int     i;\r
+\r
+       spritefreeptr = &spritearray[0];\r
+       for (i=0;i<MAXSPRITES-1;i++)\r
+               spritearray[i].nextsprite = &spritearray[i+1];\r
+\r
+       spritearray[i].nextsprite = NULL;\r
+\r
+// NULL in all priority levels\r
+\r
+       memset (prioritystart,0,sizeof(prioritystart));\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_CalcOriginStuff\r
+=\r
+= Calculate all the global variables for a new position\r
+= Long parms so position can be clipped to a maximum near 64k\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_CalcOriginStuff (long x, long y)\r
+{\r
+       originxglobal = x;\r
+       originyglobal = y;\r
+       originxtile = originxglobal>>G_T_SHIFT;\r
+       originytile = originyglobal>>G_T_SHIFT;\r
+       originxscreen = originxtile<<SX_T_SHIFT;\r
+       originyscreen = originytile<<SY_T_SHIFT;\r
+       originmap = mapbwidthtable[originytile] + originxtile*2;\r
+\r
+#if GRMODE == EGAGR\r
+       panx = (originxglobal>>G_P_SHIFT) & 15;\r
+       pansx = panx & 8;\r
+       pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
+       panadjust = panx/8 + ylookup[pany];\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       panx = (originxglobal>>G_P_SHIFT) & 15;\r
+       pansx = panx & 12;\r
+       pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
+       panadjust = pansx/4 + ylookup[pansy];\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_ClearScrollBlocks\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_ClearScrollBlocks (void)\r
+{\r
+       hscrollblocks = vscrollblocks = 0;\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RF_SetScrollBlock\r
+=\r
+= Sets a horizontal or vertical scroll block\r
+= a horizontal block is ----, meaning it blocks up/down movement\r
+=\r
+=================\r
+*/\r
+\r
+void RF_SetScrollBlock (int x, int y, boolean horizontal)\r
+{\r
+       if (horizontal)\r
+       {\r
+               hscrolledge[hscrollblocks] = y;\r
+               if (hscrollblocks++ == MAXSCROLLEDGES)\r
+                       Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks");\r
+       }\r
+       else\r
+       {\r
+               vscrolledge[vscrollblocks] = x;\r
+               if (vscrollblocks++ == MAXSCROLLEDGES)\r
+                       Quit ("RF_SetScrollBlock: Too many vertical scroll blocks");\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_BoundScroll\r
+=\r
+= Bound a given x/y movement to scroll blocks\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_BoundScroll (int x, int y)\r
+{\r
+       int     check,newxtile,newytile;\r
+\r
+       originxglobal += x;\r
+       originyglobal += y;\r
+\r
+       newxtile= originxglobal >> G_T_SHIFT;\r
+       newytile = originyglobal >> G_T_SHIFT;\r
+\r
+       if (x>0)\r
+       {\r
+               newxtile+=SCREENTILESWIDE;\r
+               for (check=0;check<vscrollblocks;check++)\r
+                       if (vscrolledge[check] == newxtile)\r
+                       {\r
+                               originxglobal = originxglobal&0xff00;\r
+                               break;\r
+                       }\r
+       }\r
+       else if (x<0)\r
+       {\r
+               for (check=0;check<vscrollblocks;check++)\r
+                       if (vscrolledge[check] == newxtile)\r
+                       {\r
+                               originxglobal = (originxglobal&0xff00)+0x100;\r
+                               break;\r
+                       }\r
+       }\r
+\r
+\r
+       if (y>0)\r
+       {\r
+               newytile+=SCREENTILESHIGH;\r
+               for (check=0;check<hscrollblocks;check++)\r
+                       if (hscrolledge[check] == newytile)\r
+                       {\r
+                               originyglobal = originyglobal&0xff00;\r
+                               break;\r
+                       }\r
+       }\r
+       else if (y<0)\r
+       {\r
+               for (check=0;check<hscrollblocks;check++)\r
+                       if (hscrolledge[check] == newytile)\r
+                       {\r
+                               originyglobal = (originyglobal&0xff00)+0x100;\r
+                               break;\r
+                       }\r
+       }\r
+\r
+\r
+       RFL_CalcOriginStuff (originxglobal, originyglobal);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_SetRefreshHook\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_SetRefreshHook (void (*func) (void) )\r
+{\r
+       refreshvector = func;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_NewRow\r
+=\r
+= Bring a new row of tiles onto the port, spawning animating tiles\r
+=\r
+=================\r
+*/\r
+\r
+void   RFL_NewRow (int dir)\r
+{\r
+       unsigned count,updatespot,updatestep;\r
+       int             x,y,xstep,ystep;\r
+\r
+       switch (dir)\r
+       {\r
+       case 0:         // top row\r
+               updatespot = 0;\r
+               updatestep = 1;\r
+               x = originxtile;\r
+               y = originytile;\r
+               xstep = 1;\r
+               ystep = 0;\r
+               count = PORTTILESWIDE;\r
+               break;\r
+\r
+       case 1:         // right row\r
+               updatespot = PORTTILESWIDE-1;\r
+               updatestep = UPDATEWIDE;\r
+               x = originxtile + PORTTILESWIDE-1;\r
+               y = originytile;\r
+               xstep = 0;\r
+               ystep = 1;\r
+               count = PORTTILESHIGH;\r
+               break;\r
+\r
+       case 2:         // bottom row\r
+               updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
+               updatestep = 1;\r
+               x = originxtile;\r
+               y = originytile + PORTTILESHIGH-1;\r
+               xstep = 1;\r
+               ystep = 0;\r
+               count = PORTTILESWIDE;\r
+               break;\r
+\r
+       case 3:         // left row\r
+               updatespot = 0;\r
+               updatestep = UPDATEWIDE;\r
+               x = originxtile;\r
+               y = originytile;\r
+               xstep = 0;\r
+               ystep = 1;\r
+               count = PORTTILESHIGH;\r
+               break;\r
+       default:\r
+               Quit ("RFL_NewRow: Bad dir!");\r
+       }\r
+\r
+       while (count--)\r
+       {\r
+               RFL_NewTile(updatespot);\r
+               RFL_CheckForAnimTile (x,y);\r
+               updatespot+=updatestep;\r
+               x+=xstep;\r
+               y+=ystep;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_ForceRefresh\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_ForceRefresh (void)\r
+{\r
+       RF_NewPosition (originxglobal,originyglobal);\r
+       RF_Refresh ();\r
+       RF_Refresh ();\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_MapToMap\r
+=\r
+= Copies a block of tiles (all three planes) from one point\r
+= in the map to another, accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_MapToMap (unsigned srcx, unsigned srcy,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height)\r
+{\r
+       int                     x,y;\r
+       unsigned        source,destofs,xspot,yspot;\r
+       unsigned        linedelta,p0,p1,p2,updatespot;\r
+       unsigned        far *source0, far *source1, far *source2;\r
+       unsigned        far *dest0, far *dest1, far *dest2;\r
+       boolean         changed;\r
+\r
+       RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
+\r
+       source = mapbwidthtable[srcy]/2 + srcx;\r
+\r
+       source0 = mapsegs[0]+source;\r
+       source1 = mapsegs[1]+source;\r
+       source2 = mapsegs[2]+source;\r
+\r
+       destofs = mapbwidthtable[desty]/2 + destx;\r
+       destofs -= source;\r
+\r
+       linedelta = mapwidth - width;\r
+\r
+       for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)\r
+               for (x=0;x<width;x++,source0++,source1++,source2++)\r
+               {\r
+                       p0 = *source0;\r
+                       p1 = *source1;\r
+                       p2 = *source2;\r
+\r
+                       dest0 = source0 + destofs;\r
+                       dest1 = source1 + destofs;\r
+                       dest2 = source2 + destofs;\r
+\r
+//\r
+// only make a new tile if it is different\r
+//\r
+                       if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)\r
+                       {\r
+                               *dest0 = p0;\r
+                               *dest1 = p1;\r
+                               *dest2 = p2;\r
+                               changed = true;\r
+                       }\r
+                       else\r
+                               changed = false;\r
+\r
+//\r
+// if tile is on the view port\r
+//\r
+                       xspot = destx+x-originxtile;\r
+                       yspot = desty+y-originytile;\r
+                       if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
+                       {\r
+                               if (changed)\r
+                               {\r
+                                       updatespot = uwidthtable[yspot]+xspot;\r
+                                       RFL_NewTile(updatespot);\r
+                               }\r
+                               RFL_CheckForAnimTile (destx+x,desty+y);\r
+                       }\r
+               }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_MemToMap\r
+=\r
+= Copies a string of tiles from main memory to the map,\r
+= accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_MemToMap (unsigned far *source, unsigned plane,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height)\r
+{\r
+       int                     x,y;\r
+       unsigned        xspot,yspot;\r
+       unsigned        linedelta,updatespot;\r
+       unsigned        far *dest,old,new;\r
+       boolean         changed;\r
+\r
+       RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
+\r
+       dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;\r
+\r
+       linedelta = mapwidth - width;\r
+\r
+       for (y=0;y<height;y++,dest+=linedelta)\r
+               for (x=0;x<width;x++)\r
+               {\r
+                       old = *dest;\r
+                       new = *source++;\r
+                       if (old != new)\r
+                       {\r
+                               *dest = new;\r
+                               changed = true;\r
+                       }\r
+                       else\r
+                               changed = false;\r
+\r
+                       dest++;\r
+                       xspot = destx+x-originxtile;\r
+                       yspot = desty+y-originytile;\r
+                       if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
+                       {\r
+                               if (changed)\r
+                               {\r
+                                       updatespot = uwidthtable[yspot]+xspot;\r
+                                       RFL_NewTile(updatespot);\r
+                               }\r
+                               RFL_CheckForAnimTile (destx+x,desty+y);\r
+                       }\r
+               }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RFL_BoundNewOrigin\r
+=\r
+= Copies a string of tiles from main memory to the map,\r
+= accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)\r
+{\r
+       int     check,edge;\r
+\r
+//\r
+// calculate new origin related globals\r
+//\r
+       if (orgx<originxmin)\r
+         orgx=originxmin;\r
+       else if (orgx>originxmax)\r
+         orgx=originxmax;\r
+\r
+       if (orgy<originymin)\r
+         orgy=originymin;\r
+       else if (orgy>originymax)\r
+         orgy=originymax;\r
+\r
+       originxtile = orgx>>G_T_SHIFT;\r
+       originytile = orgy>>G_T_SHIFT;\r
+\r
+       for (check=0;check<vscrollblocks;check++)\r
+       {\r
+               edge = vscrolledge[check];\r
+               if (edge>=originxtile && edge <=originxtile+10)\r
+               {\r
+                       orgx = (edge+1)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+               if (edge>=originxtile+11 && edge <=originxtile+20)\r
+               {\r
+                       orgx = (edge-20)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       for (check=0;check<hscrollblocks;check++)\r
+       {\r
+               edge = hscrolledge[check];\r
+               if (edge>=originytile && edge <=originytile+6)\r
+               {\r
+                       orgy = (edge+1)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+               if (edge>=originytile+7 && edge <=originytile+13)\r
+               {\r
+                       orgy = (edge-13)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+       }\r
+\r
+\r
+       RFL_CalcOriginStuff (orgx,orgy);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_ClearBlock\r
+=\r
+= Posts erase blocks to clear a certain area of the screen to the master\r
+= screen, to erase text or something draw directly to the screen\r
+=\r
+= Parameters in pixels, but erasure is byte bounded\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_ClearBlock (int        x, int y, int width, int height)\r
+{\r
+       eraseblocktype block;\r
+\r
+#if GRMODE == EGAGR\r
+       block.screenx = x/8+originxscreen;\r
+       block.screeny = y+originyscreen;\r
+       block.width = (width+(x&7)+7)/8;\r
+       block.height = height;\r
+       memcpy (eraselistptr[0]++,&block,sizeof(block));\r
+       memcpy (eraselistptr[1]++,&block,sizeof(block));\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       block.screenx = x/4+originxscreen;\r
+       block.screeny = y+originyscreen;\r
+       block.width = (width+(x&3)+3)/4;\r
+       block.height = height;\r
+       memcpy (eraselistptr[0]++,&block,sizeof(block));\r
+#endif\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RedrawBlock\r
+=\r
+= Causes a number of tiles to be redrawn to the master screen and updated\r
+=\r
+= Parameters in pixels, but erasure is tile bounded\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RedrawBlock (int x, int y, int width, int height)\r
+{\r
+       int     xx,yy,xl,xh,yl,yh;\r
+\r
+       xl=(x+panx)/16;\r
+       xh=(x+panx+width+15)/16;\r
+       yl=(y+pany)/16;\r
+       yh=(y+pany+height+15)/16;\r
+       for (yy=yl;yy<=yh;yy++)\r
+               for (xx=xl;xx<=xh;xx++)\r
+                       RFL_NewTile (yy*UPDATEWIDE+xx);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_CalcTics\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_CalcTics (void)\r
+{\r
+       long    newtime,oldtimecount;\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       if (lasttimecount > TimeCount)\r
+               TimeCount = lasttimecount;              // if the game was paused a LONG time\r
+\r
+       if (DemoMode)                                   // demo recording and playback needs\r
+       {                                                               // to be constant\r
+//\r
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken\r
+//\r
+               oldtimecount = lasttimecount;\r
+               while (TimeCount<oldtimecount+DEMOTICS*2)\r
+               ;\r
+               lasttimecount = oldtimecount + DEMOTICS;\r
+               TimeCount = lasttimecount + DEMOTICS;\r
+               tics = DEMOTICS;\r
+       }\r
+       else\r
+       {\r
+//\r
+// non demo, so report actual time\r
+//\r
+               do\r
+               {\r
+                       newtime = TimeCount;\r
+                       tics = newtime-lasttimecount;\r
+               } while (tics<MINTICS);\r
+               lasttimecount = newtime;\r
+\r
+#ifdef PROFILE\r
+                       strcpy (scratch,"\tTics:");\r
+                       itoa (tics,str,10);\r
+                       strcat (scratch,str);\r
+                       strcat (scratch,"\n");\r
+                       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+               if (tics>MAXTICS)\r
+               {\r
+                       TimeCount -= (tics-MAXTICS);\r
+                       tics = MAXTICS;\r
+               }\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_FindFreeBuffer\r
+=\r
+= Finds the start of unused, non visable buffer space\r
+=\r
+=====================\r
+*/\r
+\r
+unsigned RF_FindFreeBuffer (void)\r
+{\r
+       unsigned        spot,i,j;\r
+       boolean         ok;\r
+\r
+       for (i=0;i<3;i++)\r
+       {\r
+               spot = screenstart[i]+SCREENSPACE;\r
+               ok = true;\r
+               for (j=0;j<3;j++)\r
+                       if (spot == screenstart[j])\r
+                       {\r
+                               ok = false;\r
+                               break;\r
+                       }\r
+               if (ok)\r
+                       return spot;\r
+       }\r
+\r
+       return 0;       // never get here...\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       EGA specific routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == EGAGR\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewPosition EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewPosition (unsigned x, unsigned y)\r
+{\r
+       int mx,my;\r
+       byte    *page0ptr,*page1ptr;\r
+       unsigned        updatenum;\r
+\r
+       RFL_BoundNewOrigin (x,y);\r
+//\r
+// clear out all animating tiles\r
+//\r
+       RFL_InitAnimList ();\r
+\r
+//\r
+// set up the new update arrays at base position\r
+//\r
+       updatestart[0] = baseupdatestart[0];\r
+       updatestart[1] = baseupdatestart[1];\r
+       updateptr = updatestart[otherpage];\r
+\r
+       page0ptr = updatestart[0]+PORTTILESWIDE;        // used to stick "0"s after rows\r
+       page1ptr = updatestart[1]+PORTTILESWIDE;\r
+\r
+       updatenum = 0;                          // start at first visable tile\r
+\r
+       for (my=0;my<PORTTILESHIGH;my++)\r
+       {\r
+               for (mx=0;mx<PORTTILESWIDE;mx++)\r
+               {\r
+                       RFL_NewTile(updatenum);                 // puts "1"s in both pages\r
+                       RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
+                       updatenum++;\r
+               }\r
+               updatenum++;\r
+               *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles\r
+               page0ptr+=(PORTTILESWIDE+1);\r
+               page1ptr+=(PORTTILESWIDE+1);\r
+       }\r
+       *(word *)(page0ptr-PORTTILESWIDE)\r
+               = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Scroll  EGA\r
+=\r
+= Move the origin x/y global coordinates, readjust the screen panning, and\r
+= scroll if needed.  If the scroll distance is greater than one tile, the\r
+= entire screen will be redrawn (this could be generalized, but scrolling\r
+= more than one tile per refresh is a bad idea!).\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Scroll (int x, int y)\r
+{\r
+       long            neworgx,neworgy;\r
+       int                     i,deltax,deltay,absdx,absdy;\r
+       int                     oldxt,oldyt,move,yy;\r
+       unsigned        updatespot;\r
+       byte            *update0,*update1;\r
+       unsigned        oldpanx,oldpanadjust,oldscreen,newscreen,screencopy;\r
+       int                     screenmove;\r
+\r
+       oldxt = originxtile;\r
+       oldyt = originytile;\r
+       oldpanadjust = panadjust;\r
+       oldpanx = panx;\r
+\r
+       RFL_BoundScroll (x,y);\r
+\r
+       deltax = originxtile - oldxt;\r
+       absdx = abs(deltax);\r
+       deltay = originytile - oldyt;\r
+       absdy = abs(deltay);\r
+\r
+       if (absdx>1 || absdy>1)\r
+       {\r
+       //\r
+       // scrolled more than one tile, so start from scratch\r
+       //\r
+               RF_NewPosition(originxglobal,originyglobal);\r
+               return;\r
+       }\r
+\r
+       if (!absdx && !absdy)\r
+               return;                                 // the screen has not scrolled an entire tile\r
+\r
+\r
+//\r
+// adjust screens and handle SVGA crippled compatability mode\r
+//\r
+       screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
+       for (i=0;i<3;i++)\r
+       {\r
+               screenstart[i]+= screenmove;\r
+               if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )\r
+               {\r
+                       //\r
+                       // move the screen to the opposite end of the buffer\r
+                       //\r
+                       screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;\r
+                       oldscreen = screenstart[i] - screenmove;\r
+                       newscreen = oldscreen + screencopy;\r
+                       screenstart[i] = newscreen + screenmove;\r
+                       VW_ScreenToScreen (oldscreen,newscreen,\r
+                               PORTTILESWIDE*2,PORTTILESHIGH*16);\r
+\r
+                       if (i==screenpage)\r
+                               VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);\r
+               }\r
+       }\r
+       bufferofs = screenstart[otherpage];\r
+       displayofs = screenstart[screenpage];\r
+       masterofs = screenstart[2];\r
+\r
+\r
+//\r
+// float the update regions\r
+//\r
+       move = deltax;\r
+       if (deltay==1)\r
+         move += UPDATEWIDE;\r
+       else if (deltay==-1)\r
+         move -= UPDATEWIDE;\r
+\r
+       updatestart[0]+=move;\r
+       updatestart[1]+=move;\r
+\r
+//\r
+// draw the new tiles just scrolled on to the master screen, and\r
+// mark them as needing to be copied to each screen next refreshes\r
+// Make sure a zero is at the end of each row in update\r
+//\r
+\r
+       if (deltax)\r
+       {\r
+               if (deltax==1)\r
+               {\r
+                       RFL_NewRow (1);                 // new right row\r
+                       RFL_RemoveAnimsOnX (originxtile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (3);                 // new left row\r
+                       RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
+               }\r
+\r
+               update0 = updatestart[0]+PORTTILESWIDE;\r
+               update1 = updatestart[1]+PORTTILESWIDE;\r
+               for     (yy=0;yy<PORTTILESHIGH;yy++)\r
+               {\r
+                       *update0 = *update1 = 0;        // drop a 0 at end of each row\r
+                       update0+=UPDATEWIDE;\r
+                       update1+=UPDATEWIDE;\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       if (deltay)\r
+       {\r
+               if (deltay==1)\r
+               {\r
+                       updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
+                       RFL_NewRow (2);                 // new bottom row\r
+                       RFL_RemoveAnimsOnY (originytile-1);\r
+               }\r
+               else\r
+               {\r
+                       updatespot = 0;\r
+                       RFL_NewRow (0);                 // new top row\r
+                       RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
+               }\r
+\r
+               *(updatestart[0]+updatespot+PORTTILESWIDE) =\r
+                       *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;\r
+       }\r
+\r
+//----------------\r
+\r
+       //\r
+       // place a new terminator\r
+       //\r
+       update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;\r
+       update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;\r
+       *update0++ = *update1++ = 0;\r
+       *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_PlaceSprite   EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+       unsigned spritenumber, drawtype draw, int priority)\r
+{\r
+       spritelisttype  register *sprite,*next;\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        shift,pixx;\r
+       char            str[80],str2[10];\r
+\r
+       if (!spritenumber || spritenumber == (unsigned)-1)\r
+       {\r
+               RF_RemoveSprite (user);\r
+               return;\r
+       }\r
+\r
+       sprite = (spritelisttype *)*user;\r
+\r
+       if      (sprite)\r
+       {\r
+       // sprite allready exists in the list, so we can use it's block\r
+\r
+       //\r
+       // post an erase block to both pages by copying screenx,screeny,width,height\r
+       // both pages may not need to be erased if the sprite just changed last frame\r
+       //\r
+               if (sprite->updatecount<2)\r
+               {\r
+                       if (!sprite->updatecount)\r
+                               memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
+                       memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
+               }\r
+\r
+               if (priority != sprite->priority)\r
+               {\r
+               // sprite mvoed to another priority, so unlink the old one and\r
+               // relink it in the new priority\r
+\r
+                       next = sprite->nextsprite;                      // cut old links\r
+                       if (next)\r
+                               next->prevptr = sprite->prevptr;\r
+                       *sprite->prevptr = next;\r
+                       goto linknewspot;\r
+               }\r
+       }\r
+       else\r
+       {\r
+       // this is a brand new sprite, so allocate a block from the array\r
+\r
+               if (!spritefreeptr)\r
+                       Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
+\r
+               sprite = spritefreeptr;\r
+               spritefreeptr = spritefreeptr->nextsprite;\r
+\r
+linknewspot:\r
+               next = prioritystart[priority];         // stick it in new spot\r
+               if (next)\r
+                       next->prevptr = &sprite->nextsprite;\r
+               sprite->nextsprite = next;\r
+               prioritystart[priority] = sprite;\r
+               sprite->prevptr = &prioritystart[priority];\r
+       }\r
+\r
+//\r
+// write the new info to the sprite\r
+//\r
+       spr = &spritetable[spritenumber-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[spritenumber];\r
+\r
+       if (!block)\r
+       {\r
+               strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");\r
+               itoa (spritenumber,str2,10);\r
+               strcat (str,str2);\r
+               Quit (str);\r
+       }\r
+\r
+       globaly+=spr->orgy;\r
+       globalx+=spr->orgx;\r
+\r
+       pixx = globalx >> G_SY_SHIFT;\r
+       if (nopan)\r
+               shift = 0;\r
+       else\r
+               shift = (pixx&7)/2;\r
+\r
+       sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);\r
+       sprite->screeny = globaly >> G_SY_SHIFT;\r
+       sprite->width = block->width[shift];\r
+       sprite->height = spr->height;\r
+       sprite->grseg = spritenumber;\r
+       sprite->sourceofs = block->sourceoffset[shift];\r
+       sprite->planesize = block->planesize[shift];\r
+       sprite->draw = draw;\r
+       sprite->priority = priority;\r
+       sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
+       sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
+       sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
+               - sprite->tilex + 1;\r
+       sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
+               - sprite->tiley + 1;\r
+\r
+       sprite->updatecount = 2;                // draw on next two refreshes\r
+\r
+// save the sprite pointer off in the user's pointer so it can be moved\r
+// again later\r
+\r
+       *user = sprite;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RemoveSprite  EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RemoveSprite (void **user)\r
+{\r
+       spritelisttype  *sprite,*next;\r
+\r
+       sprite = (spritelisttype *)*user;\r
+       if (!sprite)\r
+               return;\r
+\r
+//\r
+// post an erase block to both pages by copying screenx,screeny,width,height\r
+// both pages may not need to be erased if the sprite just changed last frame\r
+//\r
+       if (sprite->updatecount<2)\r
+       {\r
+               if (!sprite->updatecount)\r
+                       memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
+               memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
+       }\r
+\r
+//\r
+// unlink the sprite node\r
+//\r
+       next = sprite->nextsprite;\r
+       if (next)                                               // if (!next), sprite is last in chain\r
+               next->prevptr = sprite->prevptr;\r
+       *sprite->prevptr = next;\r
+\r
+//\r
+// add it back to the free list\r
+//\r
+       sprite->nextsprite = spritefreeptr;\r
+       spritefreeptr = sprite;\r
+\r
+//\r
+// null the users pointer, so next time that actor gets placed, it will\r
+// allocate a new block\r
+//\r
+\r
+       *user = 0;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_EraseBlocks  EGA\r
+=\r
+= Write mode 1 should be set\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_EraseBlocks (void)\r
+{\r
+       eraseblocktype  *block,*done;\r
+       int                     screenxh,screenyh;\r
+       unsigned        pos,xtl,ytl,xth,yth,x,y;\r
+       byte            *updatespot;\r
+       unsigned        updatedelta;\r
+       unsigned        erasecount;\r
+\r
+#ifdef PROFILE\r
+       erasecount = 0;\r
+#endif\r
+\r
+       block = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
+\r
+       done = eraselistptr[otherpage];\r
+\r
+       while (block != done)\r
+       {\r
+\r
+       //\r
+       // clip the block to the current screen view\r
+       //\r
+               block->screenx -= originxscreen;\r
+               block->screeny -= originyscreen;\r
+\r
+               if (block->screenx < 0)\r
+               {\r
+                       block->width += block->screenx;\r
+                       if (block->width<1)\r
+                               goto next;\r
+                       block->screenx = 0;\r
+               }\r
+\r
+               if (block->screeny < 0)\r
+               {\r
+                       block->height += block->screeny;\r
+                       if (block->height<1)\r
+                               goto next;\r
+                       block->screeny = 0;\r
+               }\r
+\r
+               screenxh = block->screenx + block->width;\r
+               screenyh = block->screeny + block->height;\r
+\r
+               if (screenxh > EGAPORTSCREENWIDE)\r
+               {\r
+                       block->width = EGAPORTSCREENWIDE-block->screenx;\r
+                       screenxh = block->screenx + block->width;\r
+               }\r
+\r
+               if (screenyh > PORTSCREENHIGH)\r
+               {\r
+                       block->height = PORTSCREENHIGH-block->screeny;\r
+                       screenyh = block->screeny + block->height;\r
+               }\r
+\r
+               if (block->width<1 || block->height<1)\r
+                       goto next;\r
+\r
+       //\r
+       // erase the block by copying from the master screen\r
+       //\r
+               pos = ylookup[block->screeny]+block->screenx;\r
+               VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
+                       block->width,block->height);\r
+\r
+       //\r
+       // put 2s in update where the block was, to force sprites to update\r
+       //\r
+               xtl = block->screenx >> SX_T_SHIFT;\r
+               xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
+               ytl = block->screeny >> SY_T_SHIFT;\r
+               yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
+\r
+               updatespot = updateptr + uwidthtable[ytl] + xtl;\r
+               updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+               for (y=ytl;y<=yth;y++)\r
+               {\r
+                       for (x=xtl;x<=xth;x++)\r
+                               *updatespot++ = 2;\r
+                       updatespot += updatedelta;              // down to next line\r
+               }\r
+#ifdef PROFILE\r
+               erasecount++;\r
+#endif\r
+\r
+next:\r
+               block++;\r
+       }\r
+       eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
+#ifdef PROFILE\r
+       strcpy (scratch,"\tErase:");\r
+       itoa (erasecount,str,10);\r
+       strcat (scratch,str);\r
+       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_UpdateSprites EGA\r
+=\r
+= NOTE: Implement vertical clipping!\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_UpdateSprites (void)\r
+{\r
+       spritelisttype  *sprite;\r
+       int     portx,porty,x,y,xtl,xth,ytl,yth;\r
+       int     priority;\r
+       unsigned dest;\r
+       byte            *updatespot,*baseupdatespot;\r
+       unsigned        updatedelta;\r
+       unsigned        updatecount;\r
+       unsigned        height,sourceofs;\r
+\r
+#ifdef PROFILE\r
+       updatecount = 0;\r
+#endif\r
+\r
+       for (priority=0;priority<PRIORITIES;priority++)\r
+       {\r
+               if (priority==MASKEDTILEPRIORITY)\r
+                       RFL_MaskForegroundTiles ();\r
+\r
+               for (sprite = prioritystart[priority]; sprite ;\r
+                       sprite = (spritelisttype *)sprite->nextsprite)\r
+               {\r
+               //\r
+               // see if the sprite has any visable area in the port\r
+               //\r
+\r
+                       portx = sprite->screenx - originxscreen;\r
+                       porty = sprite->screeny - originyscreen;\r
+                       xtl = portx >> SX_T_SHIFT;\r
+                       xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
+                       ytl = porty >> SY_T_SHIFT;\r
+                       yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
+\r
+                       if (xtl<0)\r
+                         xtl = 0;\r
+                       if (xth>=PORTTILESWIDE)\r
+                         xth = PORTTILESWIDE-1;\r
+                       if (ytl<0)\r
+                         ytl = 0;\r
+                       if (yth>=PORTTILESHIGH)\r
+                         yth = PORTTILESHIGH-1;\r
+\r
+                       if (xtl>xth || ytl>yth)\r
+                               continue;\r
+\r
+               //\r
+               // see if it's visable area covers any non 0 update tiles\r
+               //\r
+                       updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
+                       updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+                       if (sprite->updatecount)\r
+                       {\r
+                               sprite->updatecount--;                  // the sprite was just placed,\r
+                               goto redraw;                                    // so draw it for sure\r
+                       }\r
+\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       if (*updatespot++)\r
+                                               goto redraw;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+                       continue;                                                       // no need to update\r
+\r
+redraw:\r
+               //\r
+               // set the tiles it covers to 3, because those tiles are being\r
+               // updated\r
+               //\r
+                       updatespot = baseupdatespot;\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       *updatespot++ = 3;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+               //\r
+               // draw it!\r
+               //\r
+                       height = sprite->height;\r
+                       sourceofs = sprite->sourceofs;\r
+                       if (porty<0)\r
+                       {\r
+                               height += porty;                                        // clip top off\r
+                               sourceofs -= porty*sprite->width;\r
+                               porty = 0;\r
+                       }\r
+                       else if (porty+height>PORTSCREENHIGH)\r
+                       {\r
+                               height = PORTSCREENHIGH - porty;    // clip bottom off\r
+                       }\r
+\r
+                       dest = bufferofs + ylookup[porty] + portx;\r
+\r
+                       switch (sprite->draw)\r
+                       {\r
+                       case spritedraw:\r
+                               VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height,sprite->planesize);\r
+                               break;\r
+\r
+                       case maskdraw:\r
+                               VW_InverseMask(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height);\r
+                               break;\r
+\r
+                       }\r
+#ifdef PROFILE\r
+                       updatecount++;\r
+#endif\r
+\r
+\r
+               }\r
+       }\r
+#ifdef PROFILE\r
+       strcpy (scratch,"\tSprites:");\r
+       itoa (updatecount,str,10);\r
+       strcat (scratch,str);\r
+       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Refresh   EGA\r
+=\r
+= All routines will draw at the port at bufferofs, possibly copying from\r
+= the port at masterofs.  The EGA version then page flips, while the\r
+= CGA version updates the screen from the buffer port.\r
+=\r
+= Screenpage is the currently displayed page, not the one being drawn\r
+= Otherpage is the page to be worked with now\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Refresh (void)\r
+{\r
+       byte    *newupdate;\r
+\r
+       updateptr = updatestart[otherpage];\r
+\r
+       RFL_AnimateTiles ();            // DEBUG\r
+\r
+//\r
+// update newly scrolled on tiles and animated tiles from the master screen\r
+//\r
+       EGAWRITEMODE(1);\r
+       EGAMAPMASK(15);\r
+       RFL_UpdateTiles ();\r
+       RFL_EraseBlocks ();\r
+\r
+//\r
+// Update is all 0 except where sprites have changed or new area has\r
+// been scrolled on.  Go through all sprites and update the ones that cover\r
+// a non 0 update tile\r
+//\r
+       EGAWRITEMODE(0);\r
+       RFL_UpdateSprites ();\r
+\r
+//\r
+// if the main program has a refresh hook set, call their function before\r
+// displaying the new page\r
+//\r
+       if (refreshvector)\r
+               refreshvector();\r
+\r
+//\r
+// display the changed screen\r
+//\r
+       VW_SetScreen(bufferofs+panadjust,panx & xpanmask);\r
+\r
+//\r
+// prepare for next refresh\r
+//\r
+// Set the update array to the middle position and clear it out to all "0"s\r
+// with an UPDATETERMINATE at the end\r
+//\r
+       updatestart[otherpage] = newupdate = baseupdatestart[otherpage];\r
+asm    mov     ax,ds\r
+asm    mov     es,ax\r
+asm    xor     ax,ax\r
+asm    mov     cx,(UPDATESCREENSIZE-2)/2\r
+asm    mov     di,[newupdate]\r
+asm    rep     stosw\r
+asm    mov     [WORD PTR es:di],UPDATETERMINATE\r
+\r
+       screenpage ^= 1;\r
+       otherpage ^= 1;\r
+       bufferofs = screenstart[otherpage];\r
+       displayofs = screenstart[screenpage];\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       RF_CalcTics ();\r
+}\r
+\r
+#endif         // GRMODE == EGAGR\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       CGA specific routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewPosition   CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewPosition (unsigned x, unsigned y)\r
+{\r
+       int mx,my;\r
+       byte    *spotptr;\r
+       unsigned        updatenum;\r
+\r
+       RFL_BoundNewOrigin (x,y);\r
+\r
+//\r
+// clear out all animating tiles\r
+//\r
+       RFL_InitAnimList ();\r
+\r
+//\r
+// set up the new update arrays at base position\r
+//\r
+       updateptr = baseupdateptr;\r
+\r
+       spotptr = updateptr + PORTTILESWIDE;    // used to stick "0"s after rows\r
+\r
+       updatenum = 0;                          // start at first visable tile\r
+\r
+       for (my=0;my<PORTTILESHIGH;my++)\r
+       {\r
+               for (mx=0;mx<PORTTILESWIDE;mx++)\r
+               {\r
+                       RFL_NewTile(updatenum);                 // puts "1"s in both pages\r
+                       RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
+                       updatenum++;\r
+               }\r
+               updatenum++;\r
+               *spotptr = 0; // set a 0 at end of a line of tiles\r
+               spotptr +=(PORTTILESWIDE+1);\r
+       }\r
+       *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Scroll       CGA\r
+=\r
+= Move the origin x/y global coordinates, readjust the screen panning, and\r
+= scroll if needed.  If the scroll distance is greater than one tile, the\r
+= entire screen will be redrawn (this could be generalized, but scrolling\r
+= more than one tile per refresh is a bad idea!).\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Scroll (int x, int y)\r
+{\r
+       long            neworgx,neworgy;\r
+       int                     i,deltax,deltay,absdx,absdy;\r
+       int                     oldxt,oldyt,move,yy;\r
+       unsigned        updatespot;\r
+       byte            *spotptr;\r
+       unsigned        oldoriginmap,oldscreen,newscreen,screencopy;\r
+       int                     screenmove;\r
+\r
+       oldxt = originxtile;\r
+       oldyt = originytile;\r
+\r
+       RFL_BoundScroll (x,y);\r
+\r
+       deltax = originxtile - oldxt;\r
+       absdx = abs(deltax);\r
+       deltay = originytile - oldyt;\r
+       absdy = abs(deltay);\r
+\r
+       if (absdx>1 || absdy>1)\r
+       {\r
+       //\r
+       // scrolled more than one tile, so start from scratch\r
+       //\r
+               RF_NewPosition(originxglobal,originyglobal);\r
+               return;\r
+       }\r
+\r
+       if (!absdx && !absdy)\r
+               return;                                 // the screen has not scrolled an entire tile\r
+\r
+\r
+//\r
+// float screens\r
+//\r
+       screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
+       bufferofs += screenmove;\r
+       masterofs += screenmove;\r
+\r
+\r
+//\r
+// float the update regions\r
+//\r
+       move = deltax;\r
+       if (deltay==1)\r
+         move += UPDATEWIDE;\r
+       else if (deltay==-1)\r
+         move -= UPDATEWIDE;\r
+\r
+       updateptr+=move;\r
+\r
+//\r
+// draw the new tiles just scrolled on to the master screen, and\r
+// mark them as needing to be copied to each screen next refreshes\r
+// Make sure a zero is at the end of each row in update\r
+//\r
+\r
+       if (deltax)\r
+       {\r
+               if (deltax==1)\r
+               {\r
+                       RFL_NewRow (1);                 // new right row\r
+                       RFL_RemoveAnimsOnX (originxtile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (3);                 // new left row\r
+                       RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
+               }\r
+\r
+               spotptr = updateptr+PORTTILESWIDE;\r
+               for     (yy=0;yy<PORTTILESHIGH;yy++)\r
+               {\r
+                       *spotptr = 0;           // drop a 0 at end of each row\r
+                       spotptr+=UPDATEWIDE;\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       if (deltay)\r
+       {\r
+               if (deltay==1)\r
+               {\r
+                       RFL_NewRow (2);                 // new bottom row\r
+                       *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;\r
+                       RFL_RemoveAnimsOnY (originytile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (0);                 // new top row\r
+                       *(updateptr+PORTTILESWIDE) = 0;\r
+                       RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       //\r
+       // place a new terminator\r
+       //\r
+       spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;\r
+       *spotptr++ = 0;\r
+       *(unsigned *)spotptr = UPDATETERMINATE;\r
+}\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_PlaceSprite  CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+       unsigned spritenumber, drawtype draw, int priority)\r
+{\r
+       spritelisttype  register *sprite,*next;\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        shift,pixx;\r
+       char            str[80],str2[10];\r
+\r
+       if (!spritenumber || spritenumber == (unsigned)-1)\r
+       {\r
+               RF_RemoveSprite (user);\r
+               return;\r
+       }\r
+\r
+       sprite = (spritelisttype *)*user;\r
+\r
+       if      (sprite)\r
+       {\r
+       // sprite allready exists in the list, so we can use it's block\r
+\r
+       //\r
+       // post an erase block to erase the old position by copying\r
+       // screenx,screeny,width,height\r
+       //\r
+               if (!sprite->updatecount)               // may not have been drawn at all yet\r
+                       memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
+\r
+               if (priority != sprite->priority)\r
+               {\r
+               // sprite moved to another priority, so unlink the old one and\r
+               // relink it in the new priority\r
+\r
+                       next = sprite->nextsprite;                      // cut old links\r
+                       if (next)\r
+                               next->prevptr = sprite->prevptr;\r
+                       *sprite->prevptr = next;\r
+                       goto linknewspot;\r
+               }\r
+       }\r
+       else\r
+       {\r
+       // this is a brand new sprite, so allocate a block from the array\r
+\r
+               if (!spritefreeptr)\r
+                       Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
+\r
+               sprite = spritefreeptr;\r
+               spritefreeptr = spritefreeptr->nextsprite;\r
+\r
+linknewspot:\r
+               next = prioritystart[priority];         // stick it in new spot\r
+               if (next)\r
+                       next->prevptr = &sprite->nextsprite;\r
+               sprite->nextsprite = next;\r
+               prioritystart[priority] = sprite;\r
+               sprite->prevptr = &prioritystart[priority];\r
+       }\r
+\r
+//\r
+// write the new info to the sprite\r
+//\r
+       spr = &spritetable[spritenumber-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[spritenumber];\r
+\r
+       if (!block)\r
+       {\r
+               strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");\r
+               itoa (spritenumber,str2,10);\r
+               strcat (str,str2);\r
+               Quit (str);\r
+       }\r
+\r
+\r
+       globaly+=spr->orgy;\r
+       globalx+=spr->orgx;\r
+\r
+       sprite->screenx = globalx >> G_CGASX_SHIFT;\r
+       sprite->screeny = globaly >> G_SY_SHIFT;\r
+       sprite->width = block->width[0];\r
+       sprite->height = spr->height;\r
+       sprite->grseg = spritenumber;\r
+       sprite->sourceofs = block->sourceoffset[0];\r
+       sprite->planesize = block->planesize[0];\r
+       sprite->draw = draw;\r
+       sprite->priority = priority;\r
+       sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
+       sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
+       sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
+               - sprite->tilex + 1;\r
+       sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
+               - sprite->tiley + 1;\r
+\r
+       sprite->updatecount = 1;                // draw on next refresh\r
+\r
+// save the sprite pointer off in the user's pointer so it can be moved\r
+// again later\r
+\r
+       *user = sprite;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RemoveSprite CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RemoveSprite (void **user)\r
+{\r
+       spritelisttype  *sprite,*next;\r
+\r
+       sprite = (spritelisttype *)*user;\r
+       if (!sprite)\r
+               return;\r
+\r
+//\r
+// post an erase block to erase the old position by copying\r
+// screenx,screeny,width,height\r
+//\r
+       if (!sprite->updatecount)\r
+       {\r
+               memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
+       }\r
+\r
+//\r
+// unlink the sprite node\r
+//\r
+       next = sprite->nextsprite;\r
+       if (next)                                               // if (!next), sprite is last in chain\r
+               next->prevptr = sprite->prevptr;\r
+       *sprite->prevptr = next;\r
+\r
+//\r
+// add it back to the free list\r
+//\r
+       sprite->nextsprite = spritefreeptr;\r
+       spritefreeptr = sprite;\r
+\r
+//\r
+// null the users pointer, so next time that actor gets placed, it will\r
+// allocate a new block\r
+//\r
+\r
+       *user = 0;\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_EraseBlocks CGA\r
+=\r
+= Write mode 1 should be set\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_EraseBlocks (void)\r
+{\r
+       eraseblocktype  *block,*done;\r
+       int                     screenxh,screenyh;\r
+       unsigned        pos,xtl,ytl,xth,yth,x,y;\r
+       byte            *updatespot;\r
+       unsigned        updatedelta;\r
+\r
+       block = &eraselist[0][0];\r
+\r
+       done = eraselistptr[0];\r
+\r
+       while (block != done)\r
+       {\r
+\r
+       //\r
+       // clip the block to the current screen view\r
+       //\r
+               block->screenx -= originxscreen;\r
+               block->screeny -= originyscreen;\r
+\r
+               if (block->screenx < 0)\r
+               {\r
+                       block->width += block->screenx;\r
+                       if (block->width<1)\r
+                               goto next;\r
+                       block->screenx = 0;\r
+               }\r
+\r
+               if (block->screeny < 0)\r
+               {\r
+                       block->height += block->screeny;\r
+                       if (block->height<1)\r
+                               goto next;\r
+                       block->screeny = 0;\r
+               }\r
+\r
+               screenxh = block->screenx + block->width;\r
+               screenyh = block->screeny + block->height;\r
+\r
+               if (screenxh > CGAPORTSCREENWIDE)\r
+               {\r
+                       block->width = CGAPORTSCREENWIDE-block->screenx;\r
+                       screenxh = block->screenx + block->width;\r
+               }\r
+\r
+               if (screenyh > PORTSCREENHIGH)\r
+               {\r
+                       block->height = PORTSCREENHIGH-block->screeny;\r
+                       screenyh = block->screeny + block->height;\r
+               }\r
+\r
+               if (block->width<1 || block->height<1)\r
+                       goto next;\r
+\r
+       //\r
+       // erase the block by copying from the master screen\r
+       //\r
+               pos = ylookup[block->screeny]+block->screenx;\r
+               block->width = (block->width + (pos&1) + 1)& ~1;\r
+               pos &= ~1;                              // make sure a word copy gets used\r
+               VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
+                       block->width,block->height);\r
+\r
+       //\r
+       // put 2s in update where the block was, to force sprites to update\r
+       //\r
+               xtl = block->screenx >> SX_T_SHIFT;\r
+               xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
+               ytl = block->screeny >> SY_T_SHIFT;\r
+               yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
+\r
+               updatespot = updateptr + uwidthtable[ytl] + xtl;\r
+               updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+               for (y=ytl;y<=yth;y++)\r
+               {\r
+                       for (x=xtl;x<=xth;x++)\r
+                               *updatespot++ = 2;\r
+                       updatespot += updatedelta;              // down to next line\r
+               }\r
+\r
+next:\r
+               block++;\r
+       }\r
+       eraselistptr[0] = &eraselist[0][0];\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_UpdateSprites      CGA\r
+=\r
+= NOTE: Implement vertical clipping!\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_UpdateSprites (void)\r
+{\r
+       spritelisttype  *sprite;\r
+       int     portx,porty,x,y,xtl,xth,ytl,yth;\r
+       int     priority;\r
+       unsigned dest;\r
+       byte            *updatespot,*baseupdatespot;\r
+       unsigned        updatedelta;\r
+\r
+       unsigned        updatecount;\r
+       unsigned        height,sourceofs;\r
+\r
+#ifdef PROFILE\r
+       updatecount = 0;\r
+#endif\r
+\r
+\r
+       for (priority=0;priority<PRIORITIES;priority++)\r
+       {\r
+               if (priority==MASKEDTILEPRIORITY)\r
+                       RFL_MaskForegroundTiles ();\r
+\r
+               for (sprite = prioritystart[priority]; sprite ;\r
+                       sprite = (spritelisttype *)sprite->nextsprite)\r
+               {\r
+               //\r
+               // see if the sprite has any visable area in the port\r
+               //\r
+\r
+                       portx = sprite->screenx - originxscreen;\r
+                       porty = sprite->screeny - originyscreen;\r
+                       xtl = portx >> SX_T_SHIFT;\r
+                       xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
+                       ytl = porty >> SY_T_SHIFT;\r
+                       yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
+\r
+                       if (xtl<0)\r
+                         xtl = 0;\r
+                       if (xth>=PORTTILESWIDE)\r
+                         xth = PORTTILESWIDE-1;\r
+                       if (ytl<0)\r
+                         ytl = 0;\r
+                       if (yth>=PORTTILESHIGH)\r
+                         yth = PORTTILESHIGH-1;\r
+\r
+                       if (xtl>xth || ytl>yth)\r
+                               continue;\r
+\r
+               //\r
+               // see if it's visable area covers any non 0 update tiles\r
+               //\r
+                       updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
+                       updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+                       if (sprite->updatecount)\r
+                       {\r
+                               sprite->updatecount--;                  // the sprite was just placed,\r
+                               goto redraw;                                    // so draw it for sure\r
+                       }\r
+\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       if (*updatespot++)\r
+                                               goto redraw;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+                       continue;                                                       // no need to update\r
+\r
+redraw:\r
+               //\r
+               // set the tiles it covers to 3, because those tiles are being\r
+               // updated\r
+               //\r
+                       updatespot = baseupdatespot;\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       *updatespot++ = 3;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+               //\r
+               // draw it!\r
+               //\r
+                       height = sprite->height;\r
+                       sourceofs = sprite->sourceofs;\r
+                       if (porty<0)\r
+                       {\r
+                               height += porty;                                        // clip top off\r
+                               sourceofs -= porty*sprite->width;\r
+                               porty = 0;\r
+                       }\r
+                       else if (porty+height>PORTSCREENHIGH)\r
+                       {\r
+                               height = PORTSCREENHIGH - porty;    // clip bottom off\r
+                       }\r
+\r
+                       dest = bufferofs + ylookup[porty] + portx;\r
+\r
+                       switch (sprite->draw)\r
+                       {\r
+                       case spritedraw:\r
+                               VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height,sprite->planesize);\r
+                               break;\r
+\r
+                       case maskdraw:\r
+                               VW_InverseMask(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height);\r
+                               break;\r
+\r
+                       }\r
+#ifdef PROFILE\r
+                       updatecount++;\r
+#endif\r
+\r
+\r
+               }\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Refresh        CGA\r
+=\r
+= All routines will draw at the port at bufferofs, possibly copying from\r
+= the port at masterofs.  The EGA version then page flips, while the\r
+= CGA version updates the screen from the buffer port.\r
+=\r
+= Screenpage is the currently displayed page, not the one being drawn\r
+= Otherpage is the page to be worked with now\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Refresh (void)\r
+{\r
+       long newtime,oldtimecount;\r
+\r
+       RFL_AnimateTiles ();\r
+\r
+//\r
+// update newly scrolled on tiles and animated tiles from the master screen\r
+//\r
+       RFL_UpdateTiles ();\r
+       RFL_EraseBlocks ();\r
+\r
+//\r
+// Update is all 0 except where sprites have changed or new area has\r
+// been scrolled on.  Go through all sprites and update the ones that cover\r
+// a non 0 update tile\r
+//\r
+       RFL_UpdateSprites ();\r
+\r
+//\r
+// if the main program has a refresh hook set, call their function before\r
+// displaying the new page\r
+//\r
+       if (refreshvector)\r
+               refreshvector();\r
+\r
+//\r
+// update everything to the screen\r
+//\r
+       VW_CGAFullUpdate ();\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       RF_CalcTics ();\r
+}\r
+\r
+#endif         // GRMODE == CGAGR\r