--- /dev/null
+/* 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