X-Git-Url: http://4ch.mooo.com/gitweb/?p=16.git;a=blobdiff_plain;f=16%2Fkeen456%2FKEEN4-6%2FCK_DEMO.C;fp=16%2Fkeen456%2FKEEN4-6%2FCK_DEMO.C;h=5004cf34b4e6eb1eb71392445d38bba78791fc07;hp=0000000000000000000000000000000000000000;hb=7d1948e210bb7b58af0a0412e71f2a0a0a2010af;hpb=ebc247a0a67daa69a027f31d9d7d9572db765e56 diff --git a/16/keen456/KEEN4-6/CK_DEMO.C b/16/keen456/KEEN4-6/CK_DEMO.C new file mode 100755 index 00000000..5004cf34 --- /dev/null +++ b/16/keen456/KEEN4-6/CK_DEMO.C @@ -0,0 +1,2132 @@ +/* Reconstructed Commander Keen 4-6 Source Code + * Copyright (C) 2021 K1n9_Duk3 + * + * This file is loosely based on: + * Keen Dreams Source Code + * Copyright (C) 2014 Javier M. Chavez + * + * 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. + */ + +#include "CK_DEF.H" + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean scorescreenkludge; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +#if GRMODE == EGAGR + +Uint8 starcolors[17] = STARPALETTE; +Uint16 plaquenum[4] = {IDSOFTPIC, PROGTEAMPIC, ARTISTPIC, DIRECTORPIC}; +Uint8 termcolors[17] = INTROPALETTE; +Uint8 termcolors2[17] = SHRINKPALETTE; + +Uint8 ortoend[8] = {0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01}; +Uint8 andtoend[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE}; + +///////////////////////////////////////////////////////////////////////////// +// uninitialized variables: +///////////////////////////////////////////////////////////////////////////// + +typedef struct { + Uint16 height; + Uint16 width; + Uint16 rowofs[200]; +} shapehead; + +typedef shapehead _seg * shapeseg; + +// text crawl variables: +memptr linecode; +void far *linestarts[200]; +Uint16 sourceline[200]; +Uint16 masterlines; +void far *routine; +memptr sourcepic; +memptr bittables; + +// terminator intro variables: +shapeseg commander; +shapeseg keen; +shapeseg both; +memptr scaletable; +memptr cmdrshifts[8]; +Sint16 commanderbwide; +Uint16 lastsource; +Uint16 keenstart; +memptr basepl[5]; +Uint16 baseplwidth[5]; +Uint16 baseplheight[5]; +memptr plaqueseg; +Uint16 plaquewidth; +Uint16 plaquewidthwords; +Uint16 plaqueheight; +Uint16 plaqueplane; +Uint16 plaquedelta; +Uint16 *shiftptr; +Uint16 planeon; +Sint16 drawheight; +Uint16 source2; +static Uint16 t_dest; +static Sint16 plaque; +static Sint16 plaquephase; +static Sint16 plaquey; +static Sint16 lastframe; +static Sint16 pageon; +static Sint16 prevbottom[2]; +Uint16 pageofs; +Uint16 byteadjust; + +#endif // if GRMODE == EGAGR + +//=========================================================================== + +/* +============================ += += CheckLastScan += +============================ +*/ + +void CheckLastScan(void) +{ + if (LastScan) + { + if (storedemo) + { + playstate = ex_resetgame; + restartgame = gd_Normal; + IN_ClearKeysDown(); + NewGame(); + } +#ifndef KEEN6 + else if (LastScan == sc_F1) + { + HelpScreens(); + } +#endif + else + { + US_ControlPanel(); + if (restartgame) + { + playstate = ex_resetgame; + } + else if (loadedgame) + { + playstate = ex_loadedgame; + } + } + } +} + +#if GRMODE == EGAGR +/* +============================================================================= + + TERMINATOR INTRO + +============================================================================= +*/ + +/* +============================ += += LoadPlaque += +============================ +*/ + +void LoadPlaque(Sint16 index) +{ + Sint16 LocatePlaque(Sint16 elapsed); + + Uint16 chunk, picnum, width, height, planesize, i; + Uint8 far *source; + Uint16 far *dest; + + // + // cache the pic and get pic size + // + chunk = plaquenum[index]; + CA_CacheGrChunk(chunk); + picnum = chunk - STARTPICS; + baseplwidth[index] = width = pictable[picnum].width; + baseplheight[index] = height = pictable[picnum].height; + planesize = width * height * 2; + + // + // allocate buffer and convert pic into to our format + // (convert bytes to word indices for faster shift-drawing) + // + MM_GetPtr(&basepl[index], planesize*2); // 2 planes + source = grsegs[chunk]; + dest = basepl[index]; + for (i=0; i> 1)); + + EGAMAPMASK(12); // write to "red" and "intensity" plane (for erasing old pic) + + // + // update position (and pic number) + // + y = LocatePlaque(elapsed); + + // + // erase leftovers of the previous frame + // + bottom = y + plaqueheight; + if (bottom < 0) + bottom = 0; + oldbottom = prevbottom[pageon]; + if (bottom < 200 && oldbottom > bottom) + { + eraseheight = oldbottom - bottom; + screenoff = xb + ylookup[bottom]; + asm { + mov es, screenseg; + mov bx, linewidth; + sub bx, plaquewidthwords; + sub bx, plaquewidthwords; + mov di, screenoff; + mov dx, eraseheight; + mov si, plaquewidthwords; + xor ax, ax; + } +eraseloop: + asm { + mov cx, si; + rep stosw; + add di, bx; + dec dx; + jnz eraseloop; + } + } + if (bottom > 200) + bottom = 200; + prevbottom[pageon] = bottom; + + // + // draw the (new) pic at the new position + // + drawheight = plaqueheight; + skip = 0; + if (y < 0) + { + skip = -y * (plaquewidth << 1); + drawheight += y; + y = 0; + } + else if (y + plaqueheight > 200) + { + drawheight = 200 - y; + } + source2 = skip + plaqueplane; + if (drawheight > 0) + { + shiftptr = shifttabletable[shift]; + t_dest = xb + ylookup[y]; + asm { + mov bx, skip; + push bp; + mov bp, shiftptr; + mov es, screenseg; + mov ds, plaqueseg; + mov ah, 4; + mov BYTE PTR ss:planeon, ah; + } +planeloop: + asm { + mov dx, SC_INDEX; + mov al, SC_MAPMASK; + out dx, ax; + mov dx, ss:drawheight; + mov di, ss:t_dest; + } +yloop: + asm { + mov cx, ss:plaquewidth; + xor al, al; + } +xloop: + asm { + mov si, [bx]; + add bx, 2; + xor ah, ah; + or ax, [bp+si]; + stosb; + mov al, ah; + loop xloop; + stosb; + mov WORD PTR es:[di], 0; + add di, ss:plaquedelta; + dec dx; + jnz yloop; + mov bx, ss:source2; + mov ah, BYTE PTR ss:planeon; + shl ah, 1; + mov BYTE PTR ss:planeon, ah; + cmp ah, 16; + jnz planeloop; + pop bp; + mov ax, ss; + mov ds, ax; + } + } +} + +/* +============================ += += LocatePlaque += +============================ +*/ + +Sint16 LocatePlaque(Sint16 elapsed) +{ + switch (plaquephase) + { + case -1: + // + // pic starts to appear + // + plaqueseg = basepl[plaque]; + plaquewidth = baseplwidth[plaque]; + plaquewidthwords = (plaquewidth + 3) >> 1; + plaqueheight = baseplheight[plaque]; + plaquedelta = linewidth - (plaquewidth + 1); + plaqueplane = (plaquewidth * plaqueheight) << 1; + plaquephase++; + lastframe = elapsed; + plaquey = 240; + // no break or return here! + case 0: + // + // pic is moving from the bottom to the center of the screen + // + plaquey -= (elapsed - lastframe) << 1; + if (plaquey < 100) + { + plaquey = 100; + plaquephase++; + } + lastframe = elapsed; + return plaquey - (plaqueheight >> 1); + + case 1: + // + // pic is staying at the center position + // + if (elapsed - lastframe > 200) + { + plaquephase++; + lastframe = elapsed; + } + return 100 - (plaqueheight >> 1); + + case 2: + // + // pic is moving up from the center to the top of the screen + // + plaquey -= (elapsed - lastframe) << 1; + if (plaquey < -40) + { + plaquey = -40; + if (++plaque < 4) + { + plaquephase = -1; + } + else + { + plaquephase = 3; + } + } + lastframe = elapsed; + return plaquey - (plaqueheight >> 1); + } + + return -40; +} + +/* +============================ += += SlideLetters += +============================ +*/ + +void SlideLetters(void) +{ + Sint16 x, cPosX, screenxb; + Uint16 elapsed, totaltics, dstofs; + Sint16 cStart, cEnd, cTotalMove; + Uint16 shift, srcseg, srcofs; + Sint16 clearleft, copywidth, clearright; + Uint16 srcdif, dstdif; + Sint32 now; + + // + // set up characteristics of the animation + // + EGAWRITEMODE(0); + EGAREADMAP(0); // useless... + + keenstart = keen->width + 200; + EGAREADMAP(1); // also useless ... I think... + + cEnd = 120 - commander->width; + cStart = 320; + cTotalMove = cEnd - cStart; + totaltics = abs(cTotalMove); + + pageofs = pageon = 0; + lasttimecount = TimeCount; + while (TimeCount == lasttimecount); + lasttimecount = TimeCount; + + for (elapsed=0; elapsed <= totaltics; elapsed += tics) + { + // + // draw the credits pic + // + x = ((Sint32)keenstart * (Sint32)(totaltics-elapsed)) / (Sint32)totaltics; + DrawPlaque(elapsed, x); + + // + // get ready to draw draw the "COMMANDER" pic + // + cPosX = cStart + ((Sint32)cTotalMove * (Sint32)elapsed) / (Sint32)totaltics; + cPosX += x & 7; + screenxb = (cPosX + 0x800) / 8 + -0x100; + shift = (cPosX + 0x800) & 7; + srcseg = FP_SEG(cmdrshifts[shift]); + srcofs = 0; + dstofs = pageofs + x / 8; + if (screenxb > 0) + { + clearleft = (screenxb + 1) / 2; + if (screenxb & 1) + dstofs--; + copywidth = 21 - clearleft; + clearright = 0; + } + else if (-commanderbwide + 40 < screenxb) + { + clearleft = 0; + copywidth = 21; + clearright = 0; + srcofs -= screenxb; + } + else + { + clearleft = 0; + copywidth = (commanderbwide + screenxb) / 2; + clearright = 21 - copywidth; + srcofs -= screenxb; + } + srcdif = commanderbwide - copywidth*2; + dstdif = 248 - (clearleft + copywidth + clearright)*2; + + // + // draw "COMMANDER" pic + // + EGAMAPMASK(2); + + asm { + mov di, dstofs; + mov es, screenseg; + mov si, srcofs; + mov lastsource, si; + mov ds, srcseg; + mov dx, 200; + } +yloop: + asm { + xor ax, ax; + mov cx, clearleft; + rep stosw; + mov cx, copywidth; + rep movsw; + xor ax, ax; + mov cx, clearright; + rep stosw; + test dx, 1; + jnz oddline; + mov si, ss:lastsource; + jmp nextline; + } +oddline: + asm { + add si, srcdif; + mov ss:lastsource, si; + } +nextline: + asm { + add di, dstdif; + dec dx; + jnz yloop; + mov ax, ss; + mov ds, ax; + } + + // + // page flip + // + VW_SetScreen(pageofs + x / 8, x & 7); + pageon ^= 1; + if (pageon) + { + pageofs = 124; + } + else + { + pageofs = 0; + } + + // + // handle timing + // + do + { + now = TimeCount; + tics = now - lasttimecount; + } while (tics < 2); + lasttimecount = now; + + // + // handle input + // + if (IN_IsUserInput() && LastScan != sc_F1) + { + LastScan = sc_Space; + } + if (LastScan) + return; + } + + byteadjust = x / 8; +} + +/* +============================ += += DrawScan += +============================ +*/ + +void DrawScan(Sint16 far *source, Uint8 far *dest) +{ + register Uint16 x; + register Sint16 w; + register Uint16 val; + register Uint16 i; + + val = x = 0; + for (;;) + { + // + // first part: puts black pixels ( pixels wide) + // + w = *source++; + if (w == -1) + { + *dest++ = val; + *dest = 0; + return; + } + + x += w; + if (x > 7) + { + *dest++ = val; + val = 0; + i = (x / 8) - 1; + while (i--) + { + *dest++ = 0; + } + x &= 7; + } + + // + // second part: puts white pixels ( pixels wide) + // + w = *source++; + if (w == -1) + { + *dest++ = val; + *dest = 0; + return; + } + + val |= ortoend[x]; + x += w; + if (x > 7) + { + *dest++ = val; + val = 0xFF; + i = (x / 8) - 1; + while (i--) + { + *dest++ = 0xFF; + } + x &= 7; + } + val &= andtoend[x]; + } +} + +/* +============================ += += BuildScaleShape += +============================ +*/ + +void BuildScaleShape(void) +{ + Sint16 px, w; + Sint16 far *source; + Sint16 far *dest; + Sint16 y; + + MM_GetPtr((memptr*)&both, 30000); + dest = MK_FP(FP_SEG(both), sizeof(shapehead)); + + for (y=0; y<200; y++) + { + both->rowofs[y] = FP_OFF(dest); + px = 0; + + EGAREADMAP(1); // this is pretty useless, we're not reading from EGA memory here + + source = (Sint16 far *)((byte _seg *)commander + commander->rowofs[y]); + w = *source++; + do + { + *dest++ = px; + px = px + w; + w = *source++; + } while (w != -1); + + // + // insert an 80 pixel gap between "COMMANDER" and "KEEN" + // + // This assumes that the rightmost column(s) of the "COMMANDER" + // shape are black. Otherwise the gap would be filled with + // white pixels and the "KEEN" image would use inverted colors + // as a result. + // + px += 80; + + EGAREADMAP(0); // this is pretty useless, we're not reading from EGA memory here + + source = (Sint16 far *)((byte _seg *)keen + keen->rowofs[y]); + source++; // kludgy bit, causes errors when left egde of "KEEN" is no rectangle + w = *source++; + do + { + *dest++ = px; + px = px + w; + w = *source++; + } while (w != -1); + + *dest++ = px; // put last value + *dest++ = -1; // put end-of-line + } +} + +/* +============================ += += ScalePointScan += +============================ +*/ + +void ScalePointScan(Sint16 far *rowptr, Sint16 y, Sint16 toleft, Sint16 far *scaletable) +{ + Uint8 far *dest; + Sint16 left, endx; + Uint16 w, val, x, right; + register Sint16 px, sx; + + val = x = 0; + endx = 320 - toleft; + dest = MK_FP(0xA000, pageofs + byteadjust + ylookup[y]); + + if (toleft < 0) + { + left = -toleft; + val = 0; + x = 0; + + for (;;) + { + px = *rowptr++; + sx = scaletable[px]; + if (sx > left) + goto drawwhite; + + px = *rowptr++; + sx = scaletable[px]; + if (sx > left) + goto drawblack; + } + } + + // + // regular + // + val = 0; + x = toleft & 7; + dest += (toleft >> 3); + left = 0; + rowptr++; // the first value is always 0, we need the next value +drawloop: + px = *rowptr++; + sx = scaletable[px]; + + // + // draw/add black pixels + // +drawblack: + w = sx - left; + left = sx; + x += w; + if (x > 7) + { + asm { + les di, dest; + mov al, BYTE PTR val; + stosb; + mov cx, x; + shr cx, 1; + shr cx, 1; + shr cx, 1; + dec cx; + xor al, al; + mov BYTE PTR val, al; + rep stosb; + and x, 7; + mov WORD PTR dest, di; + } + } + + // + // stop if the right side of the screen is reached + // + if (sx > endx) + return; + + // + // stop if the end of the image row is reached + // + // This is only checked after drawing the black part, so the + // combined shape must not end with white pixels on the right. + // That means the rightmost column(s) of the "KEEN" shape must + // always be black. + // + px = *rowptr++; + if (px == -1) + goto clearright; + + sx = scaletable[px]; + + // + // draw/add white pixels + // +drawwhite: + w = sx - left; + left = sx; + val |= ortoend[x]; + x += w; + if (x > 7) + { + asm { + les di, dest; + mov al, BYTE PTR val; + stosb; + mov cx, x; + shr cx, 1; + shr cx, 1; + shr cx, 1; + dec cx; + mov al, 255; + mov BYTE PTR val, al; + rep stosb; + and x, 7; + mov WORD PTR dest, di; + } + } + + // + // stop if the right side of the screen is reached + // + if (sx > endx) + return; + + val &= andtoend[x]; + goto drawloop; + + // + // clear the right side of the screen + // +clearright: + w = 320 - left; + x += w; + if (x > 7) + { + *dest++ = val; + val = 0; + right = x / 8 - 1; + while (right--) + { + *dest++ = 0; + } + x &= 7; + return; + } + return; +} + +/* +============================ += += ScaleDown += +============================ +*/ + +void ScaleDown(void) +{ + Uint16 i; + Uint16 toleft, ticselapsed, ticstotal, scale, endscale, rownum, rowinc; + Sint32 now; + Sint16 far *rowptr; + Uint16 scaleheight, top, bottom, lastbottom[2]; + Sint32 leftorigin; + + // + // set our new palette + // + SetPalette(termcolors2); + + EGAREADMAP(1); // this is pretty useless, we're not reading from EGA memory here + + leftorigin = 120l - commander->width; + BuildScaleShape(); + MM_GetPtr(&scaletable, 2500*sizeof(Uint16)); + + scale = 0x100; // 100% + endscale = 0x21; // 13% (scale from 200px to 26px) + endscale = 0x21; // redundant + lastbottom[0] = lastbottom[1] = 200; + ticselapsed = 1; + ticstotal = 30; // time for the whole shrinking animation + + while (ticselapsed <= ticstotal) + { + // + // get current scaling + // + if (ticselapsed == ticstotal) + { + scale = endscale; + toleft = 0; + top = 4; + } + else + { + scale = 0x100 - ((0x100-endscale) * ticselapsed) / ticstotal; + toleft = (leftorigin * (ticstotal - ticselapsed)) / ticstotal; + top = (ticselapsed * 4) / ticstotal; + } + + // + // build scale table: scaletable[i] = (i*scale) / 0x100; + // + asm { + xor ax, ax; + xor dx, dx; + mov cx, 2500; + mov bx, scale; + mov es, scaletable; + xor di, di; + } +l1: + asm { + mov es:[di], ah; + inc di; + mov es:[di], dl; + inc di; + add ax, bx; + adc dx, 0; + loop l1; + } + + // + // wait... didn't we already do this? + // + if (ticselapsed == ticstotal) + { + toleft = 0; + } + else + { + toleft = (leftorigin * (ticstotal - ticselapsed)) / ticstotal; + } + + // + // prepare scaled drawing process + // + scaleheight = ((Sint16 _seg *)scaletable)[200]; + rownum = 0; + rowinc = 0x10000l / scale; + bufferofs = pageofs + byteadjust; + + // + // erase stuff at the top + // + if (top > 0) + { + VW_Bar(0, 0, 320, top, BLACK); + } + + // + // draw the scaled shape + // + EGAWRITEMODE(0); + EGAMAPMASK(15); + + for (i=0; irowofs[rownum >> 8]); + ScalePointScan(rowptr, i+top, toleft, scaletable); + + rownum += rowinc; + } + + // + // erase leftovers at the bottom of the screen + // + bufferofs = pageofs + byteadjust; + bottom = scaleheight + top; + if (lastbottom[pageon] > bottom) + { + VW_Bar(0, bottom, 320, lastbottom[pageon] - bottom, BLACK); + lastbottom[pageon] = bottom; + } + + // + // page flip + // + VW_SetScreen(pageofs+byteadjust, 0); + pageon ^= 1; + if (pageon) + { + pageofs = 124; + } + else + { + pageofs = 0; + } + + // + // handle timing + // + now = TimeCount; + tics = now - lasttimecount; + lasttimecount = now; + if (tics > 8) + tics = 8; // don't skip too many frames on slow systems + + if (ticselapsed == ticstotal) + break; + + ticselapsed += tics; + if (ticselapsed > ticstotal) + ticselapsed = ticstotal; + + // + // handle input + // + if (IN_IsUserInput() && LastScan != sc_F1) + { + LastScan = sc_Space; + } + if (LastScan) + return; // BUG: buffers aren't freed! + } + + // + // free the buffers + // + MM_FreePtr(&scaletable); + MM_FreePtr((memptr*)&both); +} + +/* +============================ += += FinishPage += +============================ +*/ + +void FinishPage(void) +{ + Sint16 swap, temp, i, n, x, y; + Uint16 ofs; + Sint16 top, bottom, delta; + Uint8 bitmask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + Sint16 xtable[320], ytable[200]; + + // + // build lookup tables + // + for (i=0; i<320; i++) + { + xtable[i] = i; + } + for (i=0; i<320; i++) + { + swap = random(320); + temp = xtable[swap]; + xtable[swap] = xtable[i]; + xtable[i] = temp; + } + for (i=0; i<200; i++) + { + ytable[i] = xtable[i]; + } + + // + // set up display + // + VW_SetDefaultColors(); + if (pageon) + { + bufferofs = byteadjust + 124; + displayofs = byteadjust; + } + else + { + bufferofs = byteadjust; + displayofs = byteadjust + 124; + } + VW_SetScreen(displayofs, 0); + + // + // draw title pic to the non-displayed buffer + // + VW_DrawPic(0, 0, TITLEPICPIC); + + // + // copy "random" pixels from the non-displayed area + // into the displayed area to create the "fizzle" effect + // + delta = displayofs - bufferofs; + + // + // set ES register for the pixel copying code in the loops + // + // This is faster than setting the ES register in the loops, + // but you need to make sure nothing in the loops overwrites + // the ES register, otherwise the code won't work correctly. + // + asm mov es, screenseg; + + for (i = 0; i< 360; i++) + { + top = i - 160; + if (top < 0) + top = 0; + + bottom = i; + if (bottom >= 200) + bottom = 199; + + for (y = top; y <= bottom; y++) + { + ofs = bufferofs + ylookup[y]; + for (n=0; n<2; n++) + { + x = xtable[ytable[y]]; + if (++ytable[y] == 320) + { + ytable[y] = 0; + } + + // + // set bitmask for our x value + // + asm mov cx, x; + asm mov si, cx; + asm and si, 7; + asm cli; + asm mov dx, GC_INDEX; + asm mov al, GC_BITMASK; + asm mov ah, BYTE PTR bitmask[si]; + asm out dx, ax; + asm sti; + + // + // set up source and dest index registers + // + asm mov si, ofs; + asm shr cx, 1; + asm shr cx, 1; + asm shr cx, 1; + asm add si, cx; + asm mov di, si; + asm add di, delta; + + // + // copy the pixel data (all 4 planes) + // + // "blue" plane: + asm mov dx, SC_INDEX; + asm mov ax, SC_MAPMASK + 1*256; + asm out dx, ax; + asm mov dx, GC_INDEX; + asm mov ax, GC_READMAP + 0*256; + asm out dx, ax; + asm mov bl, es:[si]; + asm xchg bl, es:[di]; + // "green" plane: + asm mov dx, SC_INDEX; + asm mov ax, SC_MAPMASK + 2*256; + asm out dx, ax; + asm mov dx, GC_INDEX; + asm mov ax, GC_READMAP + 1*256; + asm out dx, ax; + asm mov bl, es:[si]; + asm xchg bl, es:[di]; + // "red" plane: + asm mov dx, SC_INDEX; + asm mov ax, SC_MAPMASK + 4*256; + asm out dx, ax; + asm mov dx, GC_INDEX; + asm mov ax, GC_READMAP + 2*256; + asm out dx, ax; + asm mov bl, es:[si]; + asm xchg bl, es:[di]; + // "intensity" plane: + asm mov dx, SC_INDEX; + asm mov ax, SC_MAPMASK + 8*256; + asm out dx, ax; + asm mov dx, GC_INDEX; + asm mov ax, GC_READMAP + 3*256; + asm out dx, ax; + asm mov bl, es:[si]; + asm xchg bl, es:[di]; + } + } + + VW_WaitVBL(1); // so the fizzle animation won't go super fast + + if (IN_IsUserInput() && LastScan != sc_F1) + { + LastScan = sc_Space; + } + if (LastScan) + { + EGABITMASK(0xFF); + EGAMAPMASK(15); + return; + } + } + + // + // clean up EGA registers + // + EGABITMASK(0xFF); + EGAMAPMASK(15); + + // + // pause for 6 seconds + // + IN_UserInput(6 * TickBase, false); +} + +/* +============================ += += Terminator += +============================ +*/ + +void Terminator(void) +{ + Uint16 i, shift, bufsize; + Sint16 far *source; + Uint8 far *dest; + Uint16 srcseg, destseg; + boolean pagefinished; + Uint16 rowofs[200]; + + pagefinished = false; + CA_SetAllPurge(); + SetPaletteEx(colors[0]); // all black + VW_ClearVideo(BLACK); + VW_SetLineWidth(248); // 1984 pixels total, we're using 992 per "page" + + CA_CacheGrChunk(TITLEPICPIC); + CA_CacheGrChunk(BIGCOMMANDER); + CA_CacheGrChunk(BIGKEEN); + keen = grsegs[BIGKEEN]; + commander = grsegs[BIGCOMMANDER]; + + EGAMAPMASK(1); + + keenstart = keen->width + 200; + VW_SetScreen((keenstart/8)+1, 0); + + // + // draw the "KEEN" pic (to first "page") + // + for (i=0; i<200; i++) + { + source = (Sint16 far *)((byte _seg *)keen + keen->rowofs[i]); + dest = MK_FP(0xA000, ylookup[i]); + dest += 25; // 25 bytes -> 200 pixels + DrawScan(source, dest); + } + // + // copy pic from first "page" to second "page" + // + VW_ScreenToScreen(0, 124, 109, 200); + + // + // create pre-shifted image buffers for the "COMMANDER" pic + // (only 100 pixels high instead of 200 pixels to save memory) + // + commanderbwide = (commander->width + 7) / 8; + commanderbwide = (commanderbwide + 3) & ~1; + bufsize = commanderbwide * 100; // half height + for (shift = 0; shift < 8; shift++) + { + MM_GetPtr(&cmdrshifts[shift], bufsize); + } + + // + // re-assign shape pointers (memory manager might have moved the buffers) + // + keen = grsegs[BIGKEEN]; + commander = grsegs[BIGCOMMANDER]; + + // + // draw the first (unshifted) version of the "COMMANDER" pic to the buffer + // + for (i=0; i<100; i++) + { + rowofs[i*2] = rowofs[i*2+1] = i * commanderbwide; + source = (Sint16 far *)((byte _seg *)commander + commander->rowofs[i*2]); + dest = (Uint8 _seg *)cmdrshifts[0] + rowofs[i*2]; + DrawScan(source, dest); + } + + // + // create the shifted versions of the "COMMANDER" pic + // + for (shift = 1; shift < 8; shift++) + { + srcseg = FP_SEG(cmdrshifts[shift-1]); + destseg = FP_SEG(cmdrshifts[shift]); + asm { + mov ds, srcseg; + mov es, destseg; + mov cx, bufsize; + clc; + xor si, si; + xor di, di; + } +l1: + asm { + lodsb; + rcr al, 1; + stosb; + loop l1; + mov ax, ss; + mov ds, ax; + } + } + + // + // prepare (and set) the palettes + // + termcolors[16] = termcolors2[16] = termcolors[16] = bordercolor; + SetPalette(termcolors); + + // + // cache the credits pics (they are converted into a special + // format to make shifted drawing easier during the animation) + // + for (i=0; i<4; i++) + { + LoadPlaque(i); + } + + // + // play the animation + // + plaque = lastframe = 0; + plaquephase = -1; + SlideLetters(); + + // + // free some of the buffers + // (shrink animation needs additional memory) + // + for (i=0; i<4; i++) + { + MM_FreePtr(&basepl[i]); + } + for (shift=0; shift<8; shift++) + { + MM_FreePtr(&cmdrshifts[shift]); + } + + // + // do the shrinking and fizzle fade animations + // (if intro wasn't aborted) + // + if (!LastScan) + { + ScaleDown(); + } + + if (!LastScan) + { + FinishPage(); + pagefinished = true; + } + + // + // free the remaining buffers + // + MM_SetPurge(&grsegs[BIGCOMMANDER], 3); + MM_SetPurge(&grsegs[BIGKEEN], 3); + + // + // switch back to default graphics settings + // + VW_ClearVideo(BLACK); + VW_SetLineWidth(SCREENWIDTH); + VW_SetDefaultColors(); + RF_FixOfs(); + CA_ClearMarks(); + + // + // handle input and main menu stuff + // + if (LastScan == sc_None) + { + return; + } +#ifndef KEEN6 + if (LastScan == sc_F1) + { + HelpScreens(); + return; + } +#endif + if (!pagefinished) + { + RF_FixOfs(); //redundant + CA_CacheGrChunk(TITLEPICPIC); + VW_DrawPic(0, 0, TITLEPICPIC); + VW_SetScreen(bufferofs, 0); + IN_Ack(); + CA_ClearMarks(); + if (storedemo) + { + playstate = ex_resetgame; + restartgame = gd_Normal; + IN_ClearKeysDown(); + NewGame(); + return; + } + } + + US_ControlPanel(); + if (restartgame) + { + playstate = ex_resetgame; + } + else if (loadedgame) + { + playstate = ex_loadedgame; + } +} + +/* +============================================================================= + + STAR WARS TEXT CRAWL + +============================================================================= +*/ + +/* +============================ += += BuildBitTables += +============================ +*/ + +void BuildBitTables(void) +{ + Uint16 bit1, bit2, i; + Uint8 far *buffer; + + MM_GetPtr(&bittables, 0x4000); + buffer = bittables; + + // + // generate a lookup table that maps the bits of the "texture" (bit1) + // to the appropriate bit for the screen position (bit2) to make the + // scaler code faster and smaller + // + // table[((7-b1)*8+(7-b2))*256+i] = (i & (1 << (7-b1))) ? (1 << (7-b2)) : 0; + // + for (bit1 = 1; bit1 < 0x100; bit1 <<= 1) + { + for (bit2 = 1; bit2 < 0x100; bit2 <<= 1) + { + for (i = 0; i < 0x100; i++, buffer++) + { + if (i & bit1) + { + *buffer = bit2; + } + else + { + *buffer = 0; + } + } + } + } +} + +/* +============================ += += CompileSWUpdate += +============================ +*/ + +void CompileSWUpdate(void) +{ + Sint16 y; + Uint16 i, width, scalestep, step; + Sint32 scale, rowof, xpos, size; + void far *buffer; + Uint8 srcoff, srcbit, bitpos; + Uint16 destoff, srcx, left, orindex, lastoff; + + BuildBitTables(); + size = 190000; + MM_GetPtr(&linecode, size); + buffer = linecode; + // + // Note: You should really lock the pointer to prevent the memmory manager + // from moving the buffer around. This code stores a bunch of pointers to + // this memory block in the linestarts array. Those pointers will not be + // updated when the memory manager moves the buffer around and the game + // might end up crashing (or worse) when trying to run the "code" at the + // memory location after the data was moved. The game starts playing music + // after this function is done, which may or may not cause the memory + // manager to move memory blocks around. + // + + // + // move the buffer address into ES:DI (and keep it there) + // + asm mov es, WORD PTR buffer+2; + asm mov di, WORD PTR buffer; + // + // Since the address is kept in ES:DI, we must save and restore + // the ES register when calling other functions (push es / pop es). + // The Borland C compiler always saves and restores the DI register + // when a function modifies it, so we don't need to worry about + // that register. This is a bit of an ugly hack, but it makes this + // code a little faster and smaller. + // + + scale = 320l << 11; + scalestep = (((Uint32)(320-40) << 11) / 200); // roughly 1.4 pixels per step, going from 320 pixels to 40 pixels in 200 steps + rowof = 0; + + for (y=199; y >= 0; y--) + { + // + // draw a blue line for the current row + // + asm push es; + VW_Hlin(0, 320, y, BLUE); + asm pop es; + + // + // update the buffer variable with the current (normalized) ES:DI address + // + asm mov WORD PTR buffer, di; + asm mov WORD PTR buffer+2, es; + + // + // store the address in the scaler lookup table + // + linestarts[y] = buffer; + + // + // get current scaling factors + // + width = ((Uint16)((scale/2) >> 11)) << 1; // some trickery to make sure width is even + sourceline[y] = (rowof >> 11); + step = (336l << 11) / width; + xpos = 0; + rowof += step; + left = 160 - (width >> 1); + destoff = ylookup[y] + left / 8; + bitpos = left & 7; + + // + // generate the machine code + // + // MOV CX, SS + // MOV SS, AX + // ADD DI, + // XOR AL, AL + // + asm mov ax, 0D18Ch; + asm stosw; + asm mov ax, 0D08Eh; + asm stosw; + asm mov ax, 0C781h; + asm stosw; + asm mov ax, destoff; + asm stosw; + asm mov ax, 0C030h; + asm stosw; + + lastoff = -1; + for (i=0; i> 11); + srcoff = (srcx / 8); + srcbit = srcx & 7; + + orindex = ((7-srcbit)*8 + 7-bitpos) << 8; + if (srcoff != lastoff) + { + // + // MOV BL, [BP + ] + // + asm mov ax, 5E8Ah; + asm stosw; + asm mov al, srcoff; + asm stosb; + + lastoff = srcoff; + } + + // + // OR AL, [BX + ] + // + asm mov ax, 870Ah; + asm stosw; + asm mov ax, orindex; + asm stosw; + + bitpos++; + if (bitpos == 8) + { + bitpos = 0; + + // + // STOSB + // XOR AL, AL + // + asm mov ax, 30AAh; + asm stosw; + asm mov al, 0C0h; + asm stosb; + } + + xpos += step; + } + + if (bitpos) + { + // + // STOSB + // + asm mov al, 0AAh; + asm stosb; + } + // + // generate end of subroutine + // + // MOV SS, CX + // RETF + // + asm mov ax, 0D18Eh; + asm stosw; + asm mov al, 0CBh; + asm stosb; + + // + // normalize ES:DI + // + asm mov ax, di; + asm shr ax, 1; + asm shr ax, 1; + asm shr ax, 1; + asm shr ax, 1; + asm mov bx, es; + asm add ax, bx; + asm mov es, ax; + asm and di, 0Fh; + + // + // update scale value for next row + // + scale -= scalestep; + + // + // replace the blue line with the row from the background image + // + asm push es; + VW_ScreenToScreen(ylookup[y] + 0x8000, ylookup[y], 40, 1); + asm pop es; + + if (LastScan) + return; + } +} + +/* +============================ += += TranslateString += +============================ +*/ + +void TranslateString(char *text) +{ + char c; + + while (*text) + { + c = *text; + + if (c >= 'A' && c <= 'Z') + { + c = c + -33; + } + else if (c >= 'a' && c <= 'z') + { + c = c + -39; + } + else if (c == '.') + { + c = 84; + } + else if (c == ',') + { + c = 85; + } + else if (c == '-') + { + c = 86; + } + else if (c == '"') + { + c = 87; + } + else if (c == ' ') + { + c = 88; + } + else if (c == '!') + { + c = 89; + } + else if (c == '\'') + { + c = 90; + } + else if (c != '\n') + { + c = 84; // any unhandled character is drawn as '.' + } + + *text++ = c; + } +} + +/* +============================ += += DrawSWText += +============================ +*/ + +void DrawSWText(void) +{ + char far *text; + char *ptr; + char c; + char strbuf[80]; + + WindowX = 0; + WindowW = 336; + PrintY = 1; // always leave the first line blank + bufferofs = 0; + panadjust = 0; + text = swtext; + masterlines = 0; + + // + // draw the entire text to video memory + // + while (*text) + { + ptr = strbuf; + do + { + c = *text++; + *ptr++ = c; + } while (c != '\n' && c != '\0'); + *ptr = '\0'; + + TranslateString(strbuf); + + US_CPrint(strbuf); + + bufferofs += ylookup[PrintY]; + masterlines += PrintY; + PrintY = 0; + } + + // + // allocate a buffer large enough to hold the entire text image + // and move the image data from video memory into that buffer + // + MM_GetPtr(&sourcepic, bufferofs); + EGAREADMAP(1); // read from "green" plane (doesn't really matter from which plane we read) + movedata(screenseg, 0, FP_SEG(sourcepic), 0, bufferofs); + + // + // erase the (first screen of the) text from video memory. + // we're going to display this area and copy the backgound pic + // here line-by-line as the scalers are generated and we don't + // want to have parts of the text still visible at that point. + // + bufferofs = 0; + VW_Bar(0, 0, 320, 200, BLACK); +} + +/* +============================ += += ScrollSWText += +============================ +*/ + +void ScrollSWText(void) +{ + Sint32 now; + Uint16 pos; + Sint16 i, rowof; + + tics = TimeCount = lasttimecount = 0; + + EGAWRITEMODE(0); + EGAMAPMASK(8); // only draw to the "intensity" plane (so we don't erase the backgound pic) + + pos = 0; + while (masterlines + 400 >= pos) + { + for (i = 199; i >= 0; i--) + { + rowof = pos - sourceline[i]; + if (rowof < 0 || rowof >= masterlines) + { + masterofs = 0; // draw the first (blank) line of the buffer + } + else + { + masterofs = rowof * 42; + } + routine = linestarts[i]; + asm { + mov es, screenseg; + mov di, pageofs; + mov ds, bittables; + push bp; + mov bp, ss:masterofs; + mov ax, ss:sourcepic; + xor bh, bh; + cli; // disable interrupts (scaler changes register SS, so interrupts would be fatal!) + call ss:routine; + sti; // enable interrupts again + pop bp; + mov ax, ss; + mov ds, ax; + } + } + + VW_SetScreen(pageofs, 0); + pageon ^= 1; + pageofs = pageon << 15; + + now = TimeCount; + tics = tics + (now - lasttimecount); + lasttimecount = now; + if (tics > 20) + tics = 20; + + pos = pos + tics / 4; + tics &= 3; + + if (IN_IsUserInput() && LastScan != sc_F1) + LastScan = sc_Space; + + if (LastScan) + break; + } +} + +/* +============================ += += StarWars += +============================ +*/ + +void StarWars(void) +{ + SetPaletteEx(colors[0]); // all black + VW_ClearVideo(BLACK); + VW_SetLineWidth(42); // 336 pixels + VW_SetScreen(0, 0); + pageon = pageofs = 0; + CA_SetAllPurge(); + CA_CacheGrChunk(STARTFONT+2); + fontnumber = 2; + DrawSWText(); + fontnumber = 0; + + CA_CacheGrChunk(SW_BACKGROUNDPIC); + bufferofs = 0x8000; + VW_DrawPic(0, 0, SW_BACKGROUNDPIC); + CA_SetAllPurge(); + SetPaletteEx(starcolors); + bufferofs = 0; + CompileSWUpdate(); + + if (!LastScan) + { + StartMusic(STARWARSMUSIC); + ScrollSWText(); + StopMusic(); + } + + MM_FreePtr(&linecode); + MM_FreePtr(&bittables); + MM_FreePtr(&sourcepic); + + VW_ClearVideo(BLACK); + VW_SetLineWidth(SCREENWIDTH); + VW_SetDefaultColors(); + RF_FixOfs(); + CA_ClearMarks(); + + CheckLastScan(); +} + +#endif // if GRMODE == EGAGR + +//=========================================================================== + +/* +============================ += += ShowTitle += +============================ +*/ + +void ShowTitle(void) +{ + panadjust = 0; + CA_CacheGrChunk(TITLEPICPIC); + VW_DrawPic(0, 0, TITLEPICPIC); +#if GRMODE == CGAGR + VW_UpdateScreen(); +#else + VW_SetScreen(displayofs, 0); + VW_ScreenToScreen(bufferofs, displayofs, 42, 224); +#endif + IN_UserInput(6*TickBase, false); + CA_ClearMarks(); + CheckLastScan(); +} + +//=========================================================================== + +#if GRMODE == CGAGR +/* +============================ += += ShowCredits += +============================ +*/ + +void ShowCredits(void) +{ + panadjust = 0; + CA_CacheGrChunk(SW_BACKGROUNDPIC); + VW_DrawPic(0, 0, SW_BACKGROUNDPIC); + VW_UpdateScreen(); + IN_UserInput(6*TickBase, false); + CA_ClearMarks(); + CheckLastScan(); +} +#endif + +//=========================================================================== + +/* +============================ += += RunDemo += +============================ +*/ + +void RunDemo(Sint16 num) +{ + Uint16 far *demodata; + + NewGame(); + num += DEMO0; + CA_CacheGrChunk(num); + demodata = grsegs[num]; + gamestate.mapon = demodata[0]; + DemoSize = demodata[1]; + MM_GetPtr(&(memptr)DemoBuffer, DemoSize); + MM_SetLock(&(memptr)DemoBuffer, true); + _fmemcpy(DemoBuffer, ((char _seg *)grsegs[num])+4, DemoSize); + MM_FreePtr(&grsegs[num]); + IN_StartDemoPlayback(DemoBuffer, DemoSize); + SetupGameLevel(true); + if (scorescreenkludge) + { + DrawHighScores(); + } + PlayLoop(); + IN_StopDemo(); + MM_FreePtr(&(memptr)DemoBuffer); + VW_FixRefreshBuffer(); + CA_ClearMarks(); + CheckLastScan(); +} + +//=========================================================================== + +/* +============================ += += DrawHighScores += +============================ +*/ + +void DrawHighScores(void) +{ + Uint16 i, n; + Uint16 width, height; + HighScore *entry; + Uint16 oldbufferofs; + char buf[16], *bufptr; + + RF_NewPosition(0, 0); + oldbufferofs = bufferofs; + bufferofs = masterofs; +#ifdef KEEN5 +#if GRMODE == CGAGR + fontcolor = 2; +#else + fontcolor = BLUE ^ LIGHTMAGENTA; // blue text on light magenta background (XOR draw mode!) +#endif +#endif + for (i=0, entry=&Scores[0]; iname); +#ifdef KEEN4 + PrintX = 152; + for (n=0; ncompleted; n++) + { + VWB_DrawTile8(PrintX, PrintY+1, 71); + PrintX += 8; + } +#endif + ultoa(entry->score, buf, 10); + for (bufptr=buf; *bufptr; bufptr++) + { + *bufptr = *bufptr + 81; + } + USL_MeasureString(buf, &width, &height); + PrintX = HIGHSCORE_RIGHT - width; + US_Print(buf); + } + fontcolor = WHITE; // back to default color + bufferofs = oldbufferofs; +} + +//=========================================================================== + +/* +============================ += += CheckHighScore += +============================ +*/ + +void CheckHighScore(Sint32 score, Sint16 completed) +{ + Uint16 i, n; + Sint16 index; + HighScore entry; + + strcpy(entry.name, ""); //Note: 'entry.name[0] = 0;' would be more efficient + entry.score = score; + entry.completed = completed; + for (i=0, index=-1; i i) + { + Scores[n] = Scores[n-1]; + } + Scores[i] = entry; + index = i; + HighScoresDirty = true; + break; + } + } + if (index != -1) + { + scorescreenkludge = true; + gamestate.mapon = HIGHSCORE_MAP; + SetupGameLevel(true); + DrawHighScores(); +#ifdef KEEN5 +#if GRMODE == CGAGR + fontcolor = 2; +#else + fontcolor = BLUE ^ LIGHTMAGENTA; // blue text on light magenta background (XOR draw mode!) +#endif +#endif + RF_Refresh(); + RF_Refresh(); + PrintY = i*16 + HIGHSCORE_TOP; + PrintX = HIGHSCORE_LEFT; + US_LineInput(PrintX, PrintY, Scores[index].name, NULL, true, MaxHighName, 112); + scorescreenkludge = false; + } +#ifdef KEEN5 + fontcolor = 15; // back to default color (white) +#endif +} + +//=========================================================================== + +/* +============================ += += ShowHighScores += +============================ +*/ + +void ShowHighScores(void) +{ + scorescreenkludge = true; + IN_ClearKeysDown(); + RunDemo(4); + scorescreenkludge = false; +}