1 /* Reconstructed Commander Keen 4-6 Source Code
\r
2 * Copyright (C) 2021 K1n9_Duk3
\r
4 * This file is loosely based on:
\r
5 * Keen Dreams Source Code
\r
6 * Copyright (C) 2014 Javier M. Chavez
\r
8 * This program is free software; you can redistribute it and/or modify
\r
9 * it under the terms of the GNU General Public License as published by
\r
10 * the Free Software Foundation; either version 2 of the License, or
\r
11 * (at your option) any later version.
\r
13 * This program is distributed in the hope that it will be useful,
\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 * GNU General Public License for more details.
\r
18 * You should have received a copy of the GNU General Public License along
\r
19 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
26 =============================================================================
\r
30 =============================================================================
\r
33 boolean scorescreenkludge;
\r
36 =============================================================================
\r
40 =============================================================================
\r
45 Uint8 starcolors[17] = STARPALETTE;
\r
46 Uint16 plaquenum[4] = {IDSOFTPIC, PROGTEAMPIC, ARTISTPIC, DIRECTORPIC};
\r
47 Uint8 termcolors[17] = INTROPALETTE;
\r
48 Uint8 termcolors2[17] = SHRINKPALETTE;
\r
50 Uint8 ortoend[8] = {0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01};
\r
51 Uint8 andtoend[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE};
\r
53 /////////////////////////////////////////////////////////////////////////////
\r
54 // uninitialized variables:
\r
55 /////////////////////////////////////////////////////////////////////////////
\r
63 typedef shapehead _seg * shapeseg;
\r
65 // text crawl variables:
\r
67 void far *linestarts[200];
\r
68 Uint16 sourceline[200];
\r
74 // terminator intro variables:
\r
79 memptr cmdrshifts[8];
\r
80 Sint16 commanderbwide;
\r
84 Uint16 baseplwidth[5];
\r
85 Uint16 baseplheight[5];
\r
88 Uint16 plaquewidthwords;
\r
89 Uint16 plaqueheight;
\r
96 static Uint16 t_dest;
\r
97 static Sint16 plaque;
\r
98 static Sint16 plaquephase;
\r
99 static Sint16 plaquey;
\r
100 static Sint16 lastframe;
\r
101 static Sint16 pageon;
\r
102 static Sint16 prevbottom[2];
\r
106 #endif // if GRMODE == EGAGR
\r
108 //===========================================================================
\r
111 ============================
\r
115 ============================
\r
118 void CheckLastScan(void)
\r
124 playstate = ex_resetgame;
\r
125 restartgame = gd_Normal;
\r
126 IN_ClearKeysDown();
\r
130 else if (LastScan == sc_F1)
\r
140 playstate = ex_resetgame;
\r
142 else if (loadedgame)
\r
144 playstate = ex_loadedgame;
\r
150 #if GRMODE == EGAGR
\r
152 =============================================================================
\r
156 =============================================================================
\r
160 ============================
\r
164 ============================
\r
167 void LoadPlaque(Sint16 index)
\r
169 Sint16 LocatePlaque(Sint16 elapsed);
\r
171 Uint16 chunk, picnum, width, height, planesize, i;
\r
176 // cache the pic and get pic size
\r
178 chunk = plaquenum[index];
\r
179 CA_CacheGrChunk(chunk);
\r
180 picnum = chunk - STARTPICS;
\r
181 baseplwidth[index] = width = pictable[picnum].width;
\r
182 baseplheight[index] = height = pictable[picnum].height;
\r
183 planesize = width * height * 2;
\r
186 // allocate buffer and convert pic into to our format
\r
187 // (convert bytes to word indices for faster shift-drawing)
\r
189 MM_GetPtr(&basepl[index], planesize*2); // 2 planes
\r
190 source = grsegs[chunk];
\r
191 dest = basepl[index];
\r
192 for (i=0; i<planesize; i++)
\r
194 *dest++ = *source++ << 1;
\r
198 // pic in original format is no longer needed
\r
200 MM_FreePtr(&grsegs[chunk]);
\r
204 ============================
\r
208 ============================
\r
211 void DrawPlaque(Sint16 elapsed, Uint16 x)
\r
214 Sint16 y, bottom, oldbottom;
\r
215 Uint16 eraseheight, skip, screenoff;
\r
218 xb = (pageofs + (x / 8)) + (20 - (plaquewidth >> 1));
\r
220 EGAMAPMASK(12); // write to "red" and "intensity" plane (for erasing old pic)
\r
223 // update position (and pic number)
\r
225 y = LocatePlaque(elapsed);
\r
228 // erase leftovers of the previous frame
\r
230 bottom = y + plaqueheight;
\r
233 oldbottom = prevbottom[pageon];
\r
234 if (bottom < 200 && oldbottom > bottom)
\r
236 eraseheight = oldbottom - bottom;
\r
237 screenoff = xb + ylookup[bottom];
\r
241 sub bx, plaquewidthwords;
\r
242 sub bx, plaquewidthwords;
\r
244 mov dx, eraseheight;
\r
245 mov si, plaquewidthwords;
\r
259 prevbottom[pageon] = bottom;
\r
262 // draw the (new) pic at the new position
\r
264 drawheight = plaqueheight;
\r
268 skip = -y * (plaquewidth << 1);
\r
272 else if (y + plaqueheight > 200)
\r
274 drawheight = 200 - y;
\r
276 source2 = skip + plaqueplane;
\r
277 if (drawheight > 0)
\r
279 shiftptr = shifttabletable[shift];
\r
280 t_dest = xb + ylookup[y];
\r
288 mov BYTE PTR ss:planeon, ah;
\r
293 mov al, SC_MAPMASK;
\r
295 mov dx, ss:drawheight;
\r
300 mov cx, ss:plaquewidth;
\r
313 mov WORD PTR es:[di], 0;
\r
314 add di, ss:plaquedelta;
\r
317 mov bx, ss:source2;
\r
318 mov ah, BYTE PTR ss:planeon;
\r
320 mov BYTE PTR ss:planeon, ah;
\r
331 ============================
\r
335 ============================
\r
338 Sint16 LocatePlaque(Sint16 elapsed)
\r
340 switch (plaquephase)
\r
344 // pic starts to appear
\r
346 plaqueseg = basepl[plaque];
\r
347 plaquewidth = baseplwidth[plaque];
\r
348 plaquewidthwords = (plaquewidth + 3) >> 1;
\r
349 plaqueheight = baseplheight[plaque];
\r
350 plaquedelta = linewidth - (plaquewidth + 1);
\r
351 plaqueplane = (plaquewidth * plaqueheight) << 1;
\r
353 lastframe = elapsed;
\r
355 // no break or return here!
\r
358 // pic is moving from the bottom to the center of the screen
\r
360 plaquey -= (elapsed - lastframe) << 1;
\r
366 lastframe = elapsed;
\r
367 return plaquey - (plaqueheight >> 1);
\r
371 // pic is staying at the center position
\r
373 if (elapsed - lastframe > 200)
\r
376 lastframe = elapsed;
\r
378 return 100 - (plaqueheight >> 1);
\r
382 // pic is moving up from the center to the top of the screen
\r
384 plaquey -= (elapsed - lastframe) << 1;
\r
397 lastframe = elapsed;
\r
398 return plaquey - (plaqueheight >> 1);
\r
405 ============================
\r
409 ============================
\r
412 void SlideLetters(void)
\r
414 Sint16 x, cPosX, screenxb;
\r
415 Uint16 elapsed, totaltics, dstofs;
\r
416 Sint16 cStart, cEnd, cTotalMove;
\r
417 Uint16 shift, srcseg, srcofs;
\r
418 Sint16 clearleft, copywidth, clearright;
\r
419 Uint16 srcdif, dstdif;
\r
423 // set up characteristics of the animation
\r
426 EGAREADMAP(0); // useless...
\r
428 keenstart = keen->width + 200;
\r
429 EGAREADMAP(1); // also useless ... I think...
\r
431 cEnd = 120 - commander->width;
\r
433 cTotalMove = cEnd - cStart;
\r
434 totaltics = abs(cTotalMove);
\r
436 pageofs = pageon = 0;
\r
437 lasttimecount = TimeCount;
\r
438 while (TimeCount == lasttimecount);
\r
439 lasttimecount = TimeCount;
\r
441 for (elapsed=0; elapsed <= totaltics; elapsed += tics)
\r
444 // draw the credits pic
\r
446 x = ((Sint32)keenstart * (Sint32)(totaltics-elapsed)) / (Sint32)totaltics;
\r
447 DrawPlaque(elapsed, x);
\r
450 // get ready to draw draw the "COMMANDER" pic
\r
452 cPosX = cStart + ((Sint32)cTotalMove * (Sint32)elapsed) / (Sint32)totaltics;
\r
454 screenxb = (cPosX + 0x800) / 8 + -0x100;
\r
455 shift = (cPosX + 0x800) & 7;
\r
456 srcseg = FP_SEG(cmdrshifts[shift]);
\r
458 dstofs = pageofs + x / 8;
\r
461 clearleft = (screenxb + 1) / 2;
\r
464 copywidth = 21 - clearleft;
\r
467 else if (-commanderbwide + 40 < screenxb)
\r
472 srcofs -= screenxb;
\r
477 copywidth = (commanderbwide + screenxb) / 2;
\r
478 clearright = 21 - copywidth;
\r
479 srcofs -= screenxb;
\r
481 srcdif = commanderbwide - copywidth*2;
\r
482 dstdif = 248 - (clearleft + copywidth + clearright)*2;
\r
485 // draw "COMMANDER" pic
\r
493 mov lastsource, si;
\r
505 mov cx, clearright;
\r
509 mov si, ss:lastsource;
\r
515 mov ss:lastsource, si;
\r
529 VW_SetScreen(pageofs + x / 8, x & 7);
\r
546 tics = now - lasttimecount;
\r
547 } while (tics < 2);
\r
548 lasttimecount = now;
\r
553 if (IN_IsUserInput() && LastScan != sc_F1)
\r
555 LastScan = sc_Space;
\r
561 byteadjust = x / 8;
\r
565 ============================
\r
569 ============================
\r
572 void DrawScan(Sint16 far *source, Uint8 far *dest)
\r
576 register Uint16 val;
\r
583 // first part: puts black pixels (<width> pixels wide)
\r
607 // second part: puts white pixels (<width> pixels wide)
\r
630 val &= andtoend[x];
\r
635 ============================
\r
639 ============================
\r
642 void BuildScaleShape(void)
\r
645 Sint16 far *source;
\r
649 MM_GetPtr((memptr*)&both, 30000);
\r
650 dest = MK_FP(FP_SEG(both), sizeof(shapehead));
\r
652 for (y=0; y<200; y++)
\r
654 both->rowofs[y] = FP_OFF(dest);
\r
657 EGAREADMAP(1); // this is pretty useless, we're not reading from EGA memory here
\r
659 source = (Sint16 far *)((byte _seg *)commander + commander->rowofs[y]);
\r
669 // insert an 80 pixel gap between "COMMANDER" and "KEEN"
\r
671 // This assumes that the rightmost column(s) of the "COMMANDER"
\r
672 // shape are black. Otherwise the gap would be filled with
\r
673 // white pixels and the "KEEN" image would use inverted colors
\r
678 EGAREADMAP(0); // this is pretty useless, we're not reading from EGA memory here
\r
680 source = (Sint16 far *)((byte _seg *)keen + keen->rowofs[y]);
\r
681 source++; // kludgy bit, causes errors when left egde of "KEEN" is no rectangle
\r
690 *dest++ = px; // put last value
\r
691 *dest++ = -1; // put end-of-line
\r
696 ============================
\r
700 ============================
\r
703 void ScalePointScan(Sint16 far *rowptr, Sint16 y, Sint16 toleft, Sint16 far *scaletable)
\r
707 Uint16 w, val, x, right;
\r
708 register Sint16 px, sx;
\r
711 endx = 320 - toleft;
\r
712 dest = MK_FP(0xA000, pageofs + byteadjust + ylookup[y]);
\r
723 sx = scaletable[px];
\r
728 sx = scaletable[px];
\r
739 dest += (toleft >> 3);
\r
741 rowptr++; // the first value is always 0, we need the next value
\r
744 sx = scaletable[px];
\r
747 // draw/add black pixels
\r
757 mov al, BYTE PTR val;
\r
765 mov BYTE PTR val, al;
\r
768 mov WORD PTR dest, di;
\r
773 // stop if the right side of the screen is reached
\r
779 // stop if the end of the image row is reached
\r
781 // This is only checked after drawing the black part, so the
\r
782 // combined shape must not end with white pixels on the right.
\r
783 // That means the rightmost column(s) of the "KEEN" shape must
\r
784 // always be black.
\r
790 sx = scaletable[px];
\r
793 // draw/add white pixels
\r
804 mov al, BYTE PTR val;
\r
812 mov BYTE PTR val, al;
\r
815 mov WORD PTR dest, di;
\r
820 // stop if the right side of the screen is reached
\r
825 val &= andtoend[x];
\r
829 // clear the right side of the screen
\r
850 ============================
\r
854 ============================
\r
857 void ScaleDown(void)
\r
860 Uint16 toleft, ticselapsed, ticstotal, scale, endscale, rownum, rowinc;
\r
862 Sint16 far *rowptr;
\r
863 Uint16 scaleheight, top, bottom, lastbottom[2];
\r
867 // set our new palette
\r
869 SetPalette(termcolors2);
\r
871 EGAREADMAP(1); // this is pretty useless, we're not reading from EGA memory here
\r
873 leftorigin = 120l - commander->width;
\r
875 MM_GetPtr(&scaletable, 2500*sizeof(Uint16));
\r
877 scale = 0x100; // 100%
\r
878 endscale = 0x21; // 13% (scale from 200px to 26px)
\r
879 endscale = 0x21; // redundant
\r
880 lastbottom[0] = lastbottom[1] = 200;
\r
882 ticstotal = 30; // time for the whole shrinking animation
\r
884 while (ticselapsed <= ticstotal)
\r
887 // get current scaling
\r
889 if (ticselapsed == ticstotal)
\r
897 scale = 0x100 - ((0x100-endscale) * ticselapsed) / ticstotal;
\r
898 toleft = (leftorigin * (ticstotal - ticselapsed)) / ticstotal;
\r
899 top = (ticselapsed * 4) / ticstotal;
\r
903 // build scale table: scaletable[i] = (i*scale) / 0x100;
\r
910 mov es, scaletable;
\r
925 // wait... didn't we already do this?
\r
927 if (ticselapsed == ticstotal)
\r
933 toleft = (leftorigin * (ticstotal - ticselapsed)) / ticstotal;
\r
937 // prepare scaled drawing process
\r
939 scaleheight = ((Sint16 _seg *)scaletable)[200];
\r
941 rowinc = 0x10000l / scale;
\r
942 bufferofs = pageofs + byteadjust;
\r
945 // erase stuff at the top
\r
949 VW_Bar(0, 0, 320, top, BLACK);
\r
953 // draw the scaled shape
\r
958 for (i=0; i<scaleheight; i++)
\r
960 rowptr = (Sint16 far *)((byte _seg *)both + both->rowofs[rownum >> 8]);
\r
961 ScalePointScan(rowptr, i+top, toleft, scaletable);
\r
967 // erase leftovers at the bottom of the screen
\r
969 bufferofs = pageofs + byteadjust;
\r
970 bottom = scaleheight + top;
\r
971 if (lastbottom[pageon] > bottom)
\r
973 VW_Bar(0, bottom, 320, lastbottom[pageon] - bottom, BLACK);
\r
974 lastbottom[pageon] = bottom;
\r
980 VW_SetScreen(pageofs+byteadjust, 0);
\r
995 tics = now - lasttimecount;
\r
996 lasttimecount = now;
\r
998 tics = 8; // don't skip too many frames on slow systems
\r
1000 if (ticselapsed == ticstotal)
\r
1003 ticselapsed += tics;
\r
1004 if (ticselapsed > ticstotal)
\r
1005 ticselapsed = ticstotal;
\r
1010 if (IN_IsUserInput() && LastScan != sc_F1)
\r
1012 LastScan = sc_Space;
\r
1015 return; // BUG: buffers aren't freed!
\r
1019 // free the buffers
\r
1021 MM_FreePtr(&scaletable);
\r
1022 MM_FreePtr((memptr*)&both);
\r
1026 ============================
\r
1030 ============================
\r
1033 void FinishPage(void)
\r
1035 Sint16 swap, temp, i, n, x, y;
\r
1037 Sint16 top, bottom, delta;
\r
1038 Uint8 bitmask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
\r
1039 Sint16 xtable[320], ytable[200];
\r
1042 // build lookup tables
\r
1044 for (i=0; i<320; i++)
\r
1048 for (i=0; i<320; i++)
\r
1050 swap = random(320);
\r
1051 temp = xtable[swap];
\r
1052 xtable[swap] = xtable[i];
\r
1055 for (i=0; i<200; i++)
\r
1057 ytable[i] = xtable[i];
\r
1063 VW_SetDefaultColors();
\r
1066 bufferofs = byteadjust + 124;
\r
1067 displayofs = byteadjust;
\r
1071 bufferofs = byteadjust;
\r
1072 displayofs = byteadjust + 124;
\r
1074 VW_SetScreen(displayofs, 0);
\r
1077 // draw title pic to the non-displayed buffer
\r
1079 VW_DrawPic(0, 0, TITLEPICPIC);
\r
1082 // copy "random" pixels from the non-displayed area
\r
1083 // into the displayed area to create the "fizzle" effect
\r
1085 delta = displayofs - bufferofs;
\r
1088 // set ES register for the pixel copying code in the loops
\r
1090 // This is faster than setting the ES register in the loops,
\r
1091 // but you need to make sure nothing in the loops overwrites
\r
1092 // the ES register, otherwise the code won't work correctly.
\r
1094 asm mov es, screenseg;
\r
1096 for (i = 0; i< 360; i++)
\r
1103 if (bottom >= 200)
\r
1106 for (y = top; y <= bottom; y++)
\r
1108 ofs = bufferofs + ylookup[y];
\r
1109 for (n=0; n<2; n++)
\r
1111 x = xtable[ytable[y]];
\r
1112 if (++ytable[y] == 320)
\r
1118 // set bitmask for our x value
\r
1124 asm mov dx, GC_INDEX;
\r
1125 asm mov al, GC_BITMASK;
\r
1126 asm mov ah, BYTE PTR bitmask[si];
\r
1131 // set up source and dest index registers
\r
1139 asm add di, delta;
\r
1142 // copy the pixel data (all 4 planes)
\r
1145 asm mov dx, SC_INDEX;
\r
1146 asm mov ax, SC_MAPMASK + 1*256;
\r
1148 asm mov dx, GC_INDEX;
\r
1149 asm mov ax, GC_READMAP + 0*256;
\r
1151 asm mov bl, es:[si];
\r
1152 asm xchg bl, es:[di];
\r
1154 asm mov dx, SC_INDEX;
\r
1155 asm mov ax, SC_MAPMASK + 2*256;
\r
1157 asm mov dx, GC_INDEX;
\r
1158 asm mov ax, GC_READMAP + 1*256;
\r
1160 asm mov bl, es:[si];
\r
1161 asm xchg bl, es:[di];
\r
1163 asm mov dx, SC_INDEX;
\r
1164 asm mov ax, SC_MAPMASK + 4*256;
\r
1166 asm mov dx, GC_INDEX;
\r
1167 asm mov ax, GC_READMAP + 2*256;
\r
1169 asm mov bl, es:[si];
\r
1170 asm xchg bl, es:[di];
\r
1171 // "intensity" plane:
\r
1172 asm mov dx, SC_INDEX;
\r
1173 asm mov ax, SC_MAPMASK + 8*256;
\r
1175 asm mov dx, GC_INDEX;
\r
1176 asm mov ax, GC_READMAP + 3*256;
\r
1178 asm mov bl, es:[si];
\r
1179 asm xchg bl, es:[di];
\r
1183 VW_WaitVBL(1); // so the fizzle animation won't go super fast
\r
1185 if (IN_IsUserInput() && LastScan != sc_F1)
\r
1187 LastScan = sc_Space;
\r
1198 // clean up EGA registers
\r
1204 // pause for 6 seconds
\r
1206 IN_UserInput(6 * TickBase, false);
\r
1210 ============================
\r
1214 ============================
\r
1217 void Terminator(void)
\r
1219 Uint16 i, shift, bufsize;
\r
1220 Sint16 far *source;
\r
1222 Uint16 srcseg, destseg;
\r
1223 boolean pagefinished;
\r
1224 Uint16 rowofs[200];
\r
1226 pagefinished = false;
\r
1228 SetPaletteEx(colors[0]); // all black
\r
1229 VW_ClearVideo(BLACK);
\r
1230 VW_SetLineWidth(248); // 1984 pixels total, we're using 992 per "page"
\r
1232 CA_CacheGrChunk(TITLEPICPIC);
\r
1233 CA_CacheGrChunk(BIGCOMMANDER);
\r
1234 CA_CacheGrChunk(BIGKEEN);
\r
1235 keen = grsegs[BIGKEEN];
\r
1236 commander = grsegs[BIGCOMMANDER];
\r
1240 keenstart = keen->width + 200;
\r
1241 VW_SetScreen((keenstart/8)+1, 0);
\r
1244 // draw the "KEEN" pic (to first "page")
\r
1246 for (i=0; i<200; i++)
\r
1248 source = (Sint16 far *)((byte _seg *)keen + keen->rowofs[i]);
\r
1249 dest = MK_FP(0xA000, ylookup[i]);
\r
1250 dest += 25; // 25 bytes -> 200 pixels
\r
1251 DrawScan(source, dest);
\r
1254 // copy pic from first "page" to second "page"
\r
1256 VW_ScreenToScreen(0, 124, 109, 200);
\r
1259 // create pre-shifted image buffers for the "COMMANDER" pic
\r
1260 // (only 100 pixels high instead of 200 pixels to save memory)
\r
1262 commanderbwide = (commander->width + 7) / 8;
\r
1263 commanderbwide = (commanderbwide + 3) & ~1;
\r
1264 bufsize = commanderbwide * 100; // half height
\r
1265 for (shift = 0; shift < 8; shift++)
\r
1267 MM_GetPtr(&cmdrshifts[shift], bufsize);
\r
1271 // re-assign shape pointers (memory manager might have moved the buffers)
\r
1273 keen = grsegs[BIGKEEN];
\r
1274 commander = grsegs[BIGCOMMANDER];
\r
1277 // draw the first (unshifted) version of the "COMMANDER" pic to the buffer
\r
1279 for (i=0; i<100; i++)
\r
1281 rowofs[i*2] = rowofs[i*2+1] = i * commanderbwide;
\r
1282 source = (Sint16 far *)((byte _seg *)commander + commander->rowofs[i*2]);
\r
1283 dest = (Uint8 _seg *)cmdrshifts[0] + rowofs[i*2];
\r
1284 DrawScan(source, dest);
\r
1288 // create the shifted versions of the "COMMANDER" pic
\r
1290 for (shift = 1; shift < 8; shift++)
\r
1292 srcseg = FP_SEG(cmdrshifts[shift-1]);
\r
1293 destseg = FP_SEG(cmdrshifts[shift]);
\r
1314 // prepare (and set) the palettes
\r
1316 termcolors[16] = termcolors2[16] = termcolors[16] = bordercolor;
\r
1317 SetPalette(termcolors);
\r
1320 // cache the credits pics (they are converted into a special
\r
1321 // format to make shifted drawing easier during the animation)
\r
1323 for (i=0; i<4; i++)
\r
1329 // play the animation
\r
1331 plaque = lastframe = 0;
\r
1336 // free some of the buffers
\r
1337 // (shrink animation needs additional memory)
\r
1339 for (i=0; i<4; i++)
\r
1341 MM_FreePtr(&basepl[i]);
\r
1343 for (shift=0; shift<8; shift++)
\r
1345 MM_FreePtr(&cmdrshifts[shift]);
\r
1349 // do the shrinking and fizzle fade animations
\r
1350 // (if intro wasn't aborted)
\r
1360 pagefinished = true;
\r
1364 // free the remaining buffers
\r
1366 MM_SetPurge(&grsegs[BIGCOMMANDER], 3);
\r
1367 MM_SetPurge(&grsegs[BIGKEEN], 3);
\r
1370 // switch back to default graphics settings
\r
1372 VW_ClearVideo(BLACK);
\r
1373 VW_SetLineWidth(SCREENWIDTH);
\r
1374 VW_SetDefaultColors();
\r
1379 // handle input and main menu stuff
\r
1381 if (LastScan == sc_None)
\r
1386 if (LastScan == sc_F1)
\r
1392 if (!pagefinished)
\r
1394 RF_FixOfs(); //redundant
\r
1395 CA_CacheGrChunk(TITLEPICPIC);
\r
1396 VW_DrawPic(0, 0, TITLEPICPIC);
\r
1397 VW_SetScreen(bufferofs, 0);
\r
1402 playstate = ex_resetgame;
\r
1403 restartgame = gd_Normal;
\r
1404 IN_ClearKeysDown();
\r
1410 US_ControlPanel();
\r
1413 playstate = ex_resetgame;
\r
1415 else if (loadedgame)
\r
1417 playstate = ex_loadedgame;
\r
1422 =============================================================================
\r
1424 STAR WARS TEXT CRAWL
\r
1426 =============================================================================
\r
1430 ============================
\r
1434 ============================
\r
1437 void BuildBitTables(void)
\r
1439 Uint16 bit1, bit2, i;
\r
1440 Uint8 far *buffer;
\r
1442 MM_GetPtr(&bittables, 0x4000);
\r
1443 buffer = bittables;
\r
1446 // generate a lookup table that maps the bits of the "texture" (bit1)
\r
1447 // to the appropriate bit for the screen position (bit2) to make the
\r
1448 // scaler code faster and smaller
\r
1450 // table[((7-b1)*8+(7-b2))*256+i] = (i & (1 << (7-b1))) ? (1 << (7-b2)) : 0;
\r
1452 for (bit1 = 1; bit1 < 0x100; bit1 <<= 1)
\r
1454 for (bit2 = 1; bit2 < 0x100; bit2 <<= 1)
\r
1456 for (i = 0; i < 0x100; i++, buffer++)
\r
1472 ============================
\r
1476 ============================
\r
1479 void CompileSWUpdate(void)
\r
1482 Uint16 i, width, scalestep, step;
\r
1483 Sint32 scale, rowof, xpos, size;
\r
1485 Uint8 srcoff, srcbit, bitpos;
\r
1486 Uint16 destoff, srcx, left, orindex, lastoff;
\r
1490 MM_GetPtr(&linecode, size);
\r
1491 buffer = linecode;
\r
1493 // Note: You should really lock the pointer to prevent the memmory manager
\r
1494 // from moving the buffer around. This code stores a bunch of pointers to
\r
1495 // this memory block in the linestarts array. Those pointers will not be
\r
1496 // updated when the memory manager moves the buffer around and the game
\r
1497 // might end up crashing (or worse) when trying to run the "code" at the
\r
1498 // memory location after the data was moved. The game starts playing music
\r
1499 // after this function is done, which may or may not cause the memory
\r
1500 // manager to move memory blocks around.
\r
1504 // move the buffer address into ES:DI (and keep it there)
\r
1506 asm mov es, WORD PTR buffer+2;
\r
1507 asm mov di, WORD PTR buffer;
\r
1509 // Since the address is kept in ES:DI, we must save and restore
\r
1510 // the ES register when calling other functions (push es / pop es).
\r
1511 // The Borland C compiler always saves and restores the DI register
\r
1512 // when a function modifies it, so we don't need to worry about
\r
1513 // that register. This is a bit of an ugly hack, but it makes this
\r
1514 // code a little faster and smaller.
\r
1517 scale = 320l << 11;
\r
1518 scalestep = (((Uint32)(320-40) << 11) / 200); // roughly 1.4 pixels per step, going from 320 pixels to 40 pixels in 200 steps
\r
1521 for (y=199; y >= 0; y--)
\r
1524 // draw a blue line for the current row
\r
1527 VW_Hlin(0, 320, y, BLUE);
\r
1531 // update the buffer variable with the current (normalized) ES:DI address
\r
1533 asm mov WORD PTR buffer, di;
\r
1534 asm mov WORD PTR buffer+2, es;
\r
1537 // store the address in the scaler lookup table
\r
1539 linestarts[y] = buffer;
\r
1542 // get current scaling factors
\r
1544 width = ((Uint16)((scale/2) >> 11)) << 1; // some trickery to make sure width is even
\r
1545 sourceline[y] = (rowof >> 11);
\r
1546 step = (336l << 11) / width;
\r
1549 left = 160 - (width >> 1);
\r
1550 destoff = ylookup[y] + left / 8;
\r
1551 bitpos = left & 7;
\r
1554 // generate the machine code
\r
1558 // ADD DI, <destoff>
\r
1561 asm mov ax, 0D18Ch;
\r
1563 asm mov ax, 0D08Eh;
\r
1565 asm mov ax, 0C781h;
\r
1567 asm mov ax, destoff;
\r
1569 asm mov ax, 0C030h;
\r
1573 for (i=0; i<width; i++)
\r
1575 srcx = (xpos >> 11);
\r
1576 srcoff = (srcx / 8);
\r
1577 srcbit = srcx & 7;
\r
1579 orindex = ((7-srcbit)*8 + 7-bitpos) << 8;
\r
1580 if (srcoff != lastoff)
\r
1583 // MOV BL, [BP + <srcoff>]
\r
1585 asm mov ax, 5E8Ah;
\r
1587 asm mov al, srcoff;
\r
1594 // OR AL, [BX + <orindex>]
\r
1596 asm mov ax, 870Ah;
\r
1598 asm mov ax, orindex;
\r
1610 asm mov ax, 30AAh;
\r
1628 // generate end of subroutine
\r
1633 asm mov ax, 0D18Eh;
\r
1639 // normalize ES:DI
\r
1652 // update scale value for next row
\r
1654 scale -= scalestep;
\r
1657 // replace the blue line with the row from the background image
\r
1660 VW_ScreenToScreen(ylookup[y] + 0x8000, ylookup[y], 40, 1);
\r
1669 ============================
\r
1673 ============================
\r
1676 void TranslateString(char *text)
\r
1684 if (c >= 'A' && c <= 'Z')
\r
1688 else if (c >= 'a' && c <= 'z')
\r
1692 else if (c == '.')
\r
1696 else if (c == ',')
\r
1700 else if (c == '-')
\r
1704 else if (c == '"')
\r
1708 else if (c == ' ')
\r
1712 else if (c == '!')
\r
1716 else if (c == '\'')
\r
1720 else if (c != '\n')
\r
1722 c = 84; // any unhandled character is drawn as '.'
\r
1730 ============================
\r
1734 ============================
\r
1737 void DrawSWText(void)
\r
1746 PrintY = 1; // always leave the first line blank
\r
1753 // draw the entire text to video memory
\r
1762 } while (c != '\n' && c != '\0');
\r
1765 TranslateString(strbuf);
\r
1767 US_CPrint(strbuf);
\r
1769 bufferofs += ylookup[PrintY];
\r
1770 masterlines += PrintY;
\r
1775 // allocate a buffer large enough to hold the entire text image
\r
1776 // and move the image data from video memory into that buffer
\r
1778 MM_GetPtr(&sourcepic, bufferofs);
\r
1779 EGAREADMAP(1); // read from "green" plane (doesn't really matter from which plane we read)
\r
1780 movedata(screenseg, 0, FP_SEG(sourcepic), 0, bufferofs);
\r
1783 // erase the (first screen of the) text from video memory.
\r
1784 // we're going to display this area and copy the backgound pic
\r
1785 // here line-by-line as the scalers are generated and we don't
\r
1786 // want to have parts of the text still visible at that point.
\r
1789 VW_Bar(0, 0, 320, 200, BLACK);
\r
1793 ============================
\r
1797 ============================
\r
1800 void ScrollSWText(void)
\r
1806 tics = TimeCount = lasttimecount = 0;
\r
1809 EGAMAPMASK(8); // only draw to the "intensity" plane (so we don't erase the backgound pic)
\r
1812 while (masterlines + 400 >= pos)
\r
1814 for (i = 199; i >= 0; i--)
\r
1816 rowof = pos - sourceline[i];
\r
1817 if (rowof < 0 || rowof >= masterlines)
\r
1819 masterofs = 0; // draw the first (blank) line of the buffer
\r
1823 masterofs = rowof * 42;
\r
1825 routine = linestarts[i];
\r
1827 mov es, screenseg;
\r
1829 mov ds, bittables;
\r
1831 mov bp, ss:masterofs;
\r
1832 mov ax, ss:sourcepic;
\r
1834 cli; // disable interrupts (scaler changes register SS, so interrupts would be fatal!)
\r
1836 sti; // enable interrupts again
\r
1843 VW_SetScreen(pageofs, 0);
\r
1845 pageofs = pageon << 15;
\r
1848 tics = tics + (now - lasttimecount);
\r
1849 lasttimecount = now;
\r
1853 pos = pos + tics / 4;
\r
1856 if (IN_IsUserInput() && LastScan != sc_F1)
\r
1857 LastScan = sc_Space;
\r
1865 ============================
\r
1869 ============================
\r
1872 void StarWars(void)
\r
1874 SetPaletteEx(colors[0]); // all black
\r
1875 VW_ClearVideo(BLACK);
\r
1876 VW_SetLineWidth(42); // 336 pixels
\r
1877 VW_SetScreen(0, 0);
\r
1878 pageon = pageofs = 0;
\r
1880 CA_CacheGrChunk(STARTFONT+2);
\r
1885 CA_CacheGrChunk(SW_BACKGROUNDPIC);
\r
1886 bufferofs = 0x8000;
\r
1887 VW_DrawPic(0, 0, SW_BACKGROUNDPIC);
\r
1889 SetPaletteEx(starcolors);
\r
1891 CompileSWUpdate();
\r
1895 StartMusic(STARWARSMUSIC);
\r
1900 MM_FreePtr(&linecode);
\r
1901 MM_FreePtr(&bittables);
\r
1902 MM_FreePtr(&sourcepic);
\r
1904 VW_ClearVideo(BLACK);
\r
1905 VW_SetLineWidth(SCREENWIDTH);
\r
1906 VW_SetDefaultColors();
\r
1913 #endif // if GRMODE == EGAGR
\r
1915 //===========================================================================
\r
1918 ============================
\r
1922 ============================
\r
1925 void ShowTitle(void)
\r
1928 CA_CacheGrChunk(TITLEPICPIC);
\r
1929 VW_DrawPic(0, 0, TITLEPICPIC);
\r
1930 #if GRMODE == CGAGR
\r
1931 VW_UpdateScreen();
\r
1933 VW_SetScreen(displayofs, 0);
\r
1934 VW_ScreenToScreen(bufferofs, displayofs, 42, 224);
\r
1936 IN_UserInput(6*TickBase, false);
\r
1941 //===========================================================================
\r
1943 #if GRMODE == CGAGR
\r
1945 ============================
\r
1949 ============================
\r
1952 void ShowCredits(void)
\r
1955 CA_CacheGrChunk(SW_BACKGROUNDPIC);
\r
1956 VW_DrawPic(0, 0, SW_BACKGROUNDPIC);
\r
1957 VW_UpdateScreen();
\r
1958 IN_UserInput(6*TickBase, false);
\r
1964 //===========================================================================
\r
1967 ============================
\r
1971 ============================
\r
1974 void RunDemo(Sint16 num)
\r
1976 Uint16 far *demodata;
\r
1980 CA_CacheGrChunk(num);
\r
1981 demodata = grsegs[num];
\r
1982 gamestate.mapon = demodata[0];
\r
1983 DemoSize = demodata[1];
\r
1984 MM_GetPtr(&(memptr)DemoBuffer, DemoSize);
\r
1985 MM_SetLock(&(memptr)DemoBuffer, true);
\r
1986 _fmemcpy(DemoBuffer, ((char _seg *)grsegs[num])+4, DemoSize);
\r
1987 MM_FreePtr(&grsegs[num]);
\r
1988 IN_StartDemoPlayback(DemoBuffer, DemoSize);
\r
1989 SetupGameLevel(true);
\r
1990 if (scorescreenkludge)
\r
1996 MM_FreePtr(&(memptr)DemoBuffer);
\r
1997 VW_FixRefreshBuffer();
\r
2002 //===========================================================================
\r
2005 ============================
\r
2009 ============================
\r
2012 void DrawHighScores(void)
\r
2015 Uint16 width, height;
\r
2017 Uint16 oldbufferofs;
\r
2018 char buf[16], *bufptr;
\r
2020 RF_NewPosition(0, 0);
\r
2021 oldbufferofs = bufferofs;
\r
2022 bufferofs = masterofs;
\r
2024 #if GRMODE == CGAGR
\r
2027 fontcolor = BLUE ^ LIGHTMAGENTA; // blue text on light magenta background (XOR draw mode!)
\r
2030 for (i=0, entry=&Scores[0]; i<MaxScores; i++, entry++)
\r
2032 PrintY = i*16 + HIGHSCORE_TOP;
\r
2033 PrintX = HIGHSCORE_LEFT;
\r
2034 US_Print(entry->name);
\r
2037 for (n=0; n<entry->completed; n++)
\r
2039 VWB_DrawTile8(PrintX, PrintY+1, 71);
\r
2043 ultoa(entry->score, buf, 10);
\r
2044 for (bufptr=buf; *bufptr; bufptr++)
\r
2046 *bufptr = *bufptr + 81;
\r
2048 USL_MeasureString(buf, &width, &height);
\r
2049 PrintX = HIGHSCORE_RIGHT - width;
\r
2052 fontcolor = WHITE; // back to default color
\r
2053 bufferofs = oldbufferofs;
\r
2056 //===========================================================================
\r
2059 ============================
\r
2063 ============================
\r
2066 void CheckHighScore(Sint32 score, Sint16 completed)
\r
2072 strcpy(entry.name, ""); //Note: 'entry.name[0] = 0;' would be more efficient
\r
2073 entry.score = score;
\r
2074 entry.completed = completed;
\r
2075 for (i=0, index=-1; i<MaxScores; i++)
\r
2077 if (Scores[i].score < entry.score ||
\r
2078 (Scores[i].score == entry.score && Scores[i].completed < entry.completed))
\r
2083 Scores[n] = Scores[n-1];
\r
2085 Scores[i] = entry;
\r
2087 HighScoresDirty = true;
\r
2093 scorescreenkludge = true;
\r
2094 gamestate.mapon = HIGHSCORE_MAP;
\r
2095 SetupGameLevel(true);
\r
2098 #if GRMODE == CGAGR
\r
2101 fontcolor = BLUE ^ LIGHTMAGENTA; // blue text on light magenta background (XOR draw mode!)
\r
2106 PrintY = i*16 + HIGHSCORE_TOP;
\r
2107 PrintX = HIGHSCORE_LEFT;
\r
2108 US_LineInput(PrintX, PrintY, Scores[index].name, NULL, true, MaxHighName, 112);
\r
2109 scorescreenkludge = false;
\r
2112 fontcolor = 15; // back to default color (white)
\r
2116 //===========================================================================
\r
2119 ============================
\r
2123 ============================
\r
2126 void ShowHighScores(void)
\r
2128 scorescreenkludge = true;
\r
2129 IN_ClearKeysDown();
\r
2131 scorescreenkludge = false;
\r