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