From aa97f4995dc7dbea3ecc1794a2d4d4354839cad4 Mon Sep 17 00:00:00 2001 From: Richard Mandel Date: Thu, 5 Jun 2014 19:53:35 -0400 Subject: [PATCH] Catacomb 3D open source release. --- AUDIOC3D.H | 86 ++ C3DADICT.OBJ | Bin 0 -> 1151 bytes C3DAHEAD.OBJ | Bin 0 -> 501 bytes C3DEDICT.OBJ | Bin 0 -> 1149 bytes C3DEHEAD.OBJ | Bin 0 -> 1579 bytes C3DMHEAD.OBJ | Bin 0 -> 746 bytes C3_ACT1.C | 1259 +++++++++++++++++ C3_ASM.ASM | 197 +++ C3_DEBUG.C | 606 +++++++++ C3_DEF.H | 533 ++++++++ C3_DRAW.C | 1592 ++++++++++++++++++++++ C3_GAME.C | 1199 ++++++++++++++++ C3_MAIN.C | 756 +++++++++++ C3_PLAY.C | 584 ++++++++ C3_SCALE.C | 690 ++++++++++ C3_SCA_A.ASM | 153 +++ C3_STATE.C | 546 ++++++++ C3_TRACE.C | 872 ++++++++++++ C3_WIZ.C | 2046 ++++++++++++++++++++++++++++ CAT3D.PRJ | Bin 0 -> 11498 bytes COPYING | 339 +++++ GFXE_C3D.EQU | 327 +++++ GFXE_C3D.H | 353 +++++ ID_ASM.EQU | 114 ++ ID_CA.C | 2133 +++++++++++++++++++++++++++++ ID_CA.H | 124 ++ ID_HEADS.H | 124 ++ ID_IN.C | 1112 +++++++++++++++ ID_IN.H | 208 +++ ID_MM.C | 1130 ++++++++++++++++ ID_MM.H | 108 ++ ID_RF.C | 2845 ++++++++++++++++++++++++++++++++++++++ ID_RF.H | 153 +++ ID_RF_A.ASM | 690 ++++++++++ ID_SD.C | 1295 ++++++++++++++++++ ID_SD.H | 205 +++ ID_STRS.H | 128 ++ ID_US.C | 3691 ++++++++++++++++++++++++++++++++++++++++++++++++++ ID_US.H | 147 ++ ID_US_1.C | 1285 ++++++++++++++++++ ID_US_2.C | 1818 +++++++++++++++++++++++++ ID_US_A.ASM | 117 ++ ID_VW.C | 1541 +++++++++++++++++++++ ID_VW.H | 370 +++++ ID_VW_A.ASM | 732 ++++++++++ ID_VW_AC.ASM | 1485 ++++++++++++++++++++ ID_VW_AE.ASM | 1752 ++++++++++++++++++++++++ INTROSCN.OBJ | Bin 0 -> 4160 bytes JABHACK.ASM | 115 ++ MAPSC3D.H | 55 + NOTES.TXT | 42 + README.md | 13 + 52 files changed, 35670 insertions(+) create mode 100644 AUDIOC3D.H create mode 100644 C3DADICT.OBJ create mode 100644 C3DAHEAD.OBJ create mode 100644 C3DEDICT.OBJ create mode 100644 C3DEHEAD.OBJ create mode 100644 C3DMHEAD.OBJ create mode 100644 C3_ACT1.C create mode 100644 C3_ASM.ASM create mode 100644 C3_DEBUG.C create mode 100644 C3_DEF.H create mode 100644 C3_DRAW.C create mode 100644 C3_GAME.C create mode 100644 C3_MAIN.C create mode 100644 C3_PLAY.C create mode 100644 C3_SCALE.C create mode 100644 C3_SCA_A.ASM create mode 100644 C3_STATE.C create mode 100644 C3_TRACE.C create mode 100644 C3_WIZ.C create mode 100644 CAT3D.PRJ create mode 100644 COPYING create mode 100644 GFXE_C3D.EQU create mode 100644 GFXE_C3D.H create mode 100644 ID_ASM.EQU create mode 100644 ID_CA.C create mode 100644 ID_CA.H create mode 100644 ID_HEADS.H create mode 100644 ID_IN.C create mode 100644 ID_IN.H create mode 100644 ID_MM.C create mode 100644 ID_MM.H create mode 100644 ID_RF.C create mode 100644 ID_RF.H create mode 100644 ID_RF_A.ASM create mode 100644 ID_SD.C create mode 100644 ID_SD.H create mode 100644 ID_STRS.H create mode 100644 ID_US.C create mode 100644 ID_US.H create mode 100644 ID_US_1.C create mode 100644 ID_US_2.C create mode 100644 ID_US_A.ASM create mode 100644 ID_VW.C create mode 100644 ID_VW.H create mode 100644 ID_VW_A.ASM create mode 100644 ID_VW_AC.ASM create mode 100644 ID_VW_AE.ASM create mode 100644 INTROSCN.OBJ create mode 100644 JABHACK.ASM create mode 100644 MAPSC3D.H create mode 100644 NOTES.TXT create mode 100644 README.md diff --git a/AUDIOC3D.H b/AUDIOC3D.H new file mode 100644 index 0000000..44da68f --- /dev/null +++ b/AUDIOC3D.H @@ -0,0 +1,86 @@ +/* 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. + */ + +///////////////////////////////////////////////// +// +// MUSE Header for .C3D +// Created Wed Oct 30 22:44:13 1991 +// +///////////////////////////////////////////////// + +#define NUMSOUNDS 30 +#define NUMSNDCHUNKS 91 + +// +// Sound names & indexes +// +typedef enum { + HITWALLSND, // 0 + WARPUPSND, // 1 + WARPDOWNSND, // 2 + GETBOLTSND, // 3 + GETNUKESND, // 4 + GETPOTIONSND, // 5 + GETKEYSND, // 6 + GETSCROLLSND, // 7 + GETPOINTSSND, // 8 + USEBOLTSND, // 9 + USENUKESND, // 10 + USEPOTIONSND, // 11 + USEKEYSND, // 12 + NOITEMSND, // 13 + WALK1SND, // 14 + WALK2SND, // 15 + TAKEDAMAGESND, // 16 + MONSTERMISSSND, // 17 + GAMEOVERSND, // 18 + SHOOTSND, // 19 + BIGSHOOTSND, // 20 + SHOOTWALLSND, // 21 + SHOOTMONSTERSND, // 22 + TAKEDMGHURTSND, // 23 + BALLBOUNCESND, // 24 + COMPSCOREDSND, // 25 + KEENSCOREDSND, // 26 + COMPPADDLESND, // 27 + KEENPADDLESND, // 28 + NOWAYSND, // 29 + LASTSOUND + } soundnames; + +// +// Base offsets +// +#define STARTPCSOUNDS 0 +#define STARTADLIBSOUNDS 30 +#define STARTDIGISOUNDS 60 +#define STARTMUSIC 90 + +// +// Music names & indexes +// +typedef enum { + TOOHOT_MUS, // 0 + LASTMUSIC + } musicnames; + +///////////////////////////////////////////////// +// +// Thanks for playing with MUSE! +// +///////////////////////////////////////////////// diff --git a/C3DADICT.OBJ b/C3DADICT.OBJ new file mode 100644 index 0000000000000000000000000000000000000000..c2f9fdaf068880229749bf824d8262c028112355 GIT binary patch literal 1151 zcmW-gRd^Nz07cIk|Hui_B_SZ4QX-*a8wdy^M#mT-T{;>8X;2AKx|HtjPNloM1qJC8 zar$uY%lW?Za=+U@DIuY?!z)FGheZ_(D;mzQWI)xJb}b{zSI*k4NWmg^$K@t|c*UBL zwW|k5hlfUm2L3C8qoX3~MFqkl!y^(_2(9@)Sv(;DpAiU(7YLaYz_-SXPL3vyj_KN> zO{W%Zn#abu-_8gGgoP{ym&W1s1i6uT9wOjLf;~-q&yv6kB=i!AyiH>7lf)N<_&4wP z4@uojGPkjp^IYLNH@U@K>eG?Fyz4S{@GHqZLJCv*ipDgjC122!2`pj>$2h}9E^~)e z{>fwBP?I{;r3tC+!4O67)+MAMYEw5K=UF`h|GXATS4 z%I{>b0vT<=d$ywseHg)LGWj!^&Eh$-`o4dV&F8$N0S#%*SM*~bgE`7ILdZl`N|D_h zcHjfQq&wd*fSe8|mp_o(Jnkm12gqlBpOAs~h@c{kXhtWdFpHfOFeBOcm@+9#Rh-7>R<)XuJ|zL^$xZ<( zQJww_;};T=g6e)ierj0LT6SgzQNAXY+SV}_C8=vY>)XJFHnOozOiNKdC)(8Hq%uv7 zaTU!x&ihoQ6R+h_wV=?Phm-*wbG2b`5>}+P?O)zgf7!Hx6*1gB=FYkb(zauVLVp4%GIuAjcW~~80%cedN;VyO>TCJ zvej*FcZWOOf}DnU~X^@vP@O?*%V<$;)2x zs@J^k4R3nO+y3qy@A`*-de8g*yX(@Tcm zXI2k7WEjDs`y0*>(lCBpF$_*t1iPrCo4YY=-~w-GpsR%uY~mj8Xy6;ER>?)cl=?A+ zD$ekTXM{ac9BE{+f_+51(mJm3g}6`J#sfak>!%YpSO`c*xWp}*=m|0Bf{!M1VX|?!(x{UuK9nnc1A2J5Qr8%5N}ohKiYIcLNs<{)Iy6k&AUaJ z8!ZR~gfnbn3)A?Oi>xIRIEji>VlgXO%4lY>m8wjnAZN)*L)OrMZy3wBQM&q^rVq=A zW@eT!l}4Q491Gb-WukkB;moBhdnrI20oyUEWlFKwaCAUM!<9Uvf*L;4@D4w&LtSsUe?$MiI@>{^Z zY#=WM{hLB2qOiMZOA*I0lcJ8Gn76r0ao3ZIaN1GAIE*1KJt^s7O8Jn|=BA9l^EJUX zNKzm4GpCTRcK^C8XHM!zVZf#;e19q%F#X}CS&Z#SjYLT-#Ok}ykdeAo#bRM zF~vtrb(+(i;Y??_kJ*089GfuLdCqr%3ti-5ml%zuF0&IKSndi}T834wc8zOY=Xy67 zV56Jd>=xg#)opG!HapzuE_b`fy?*aL_j|yD9`dm7`N7isXgU&d#2_B?n*IFAQIC1t zft>Ise>N5;J>@S-bWVH5v!3(3gZPJEt<7();&(52(Mw+TidX%^KfUI4Z+O$c{M%dJ s_KtVG=Y1de&__P@iBEmzb6@z2pnBtwWJij+nu5O6ra;eNOr z?r^w=VKGv3g+QW=fk4jGnL=9^bD7{6!_bZ9Bry8tuYUET_s8=-d+&SpYF7$NA72kY z9}jOgFQvD$$g>tg?20_B4)pYQKJM=3K3TO*n0fmK2ZjWhMtFOuJWLirm`134LRBVS zf!;pmZ+Zkj&8%9O6cH02dEijeL3L!bI{tXgGO@qG)O?vqK&iEm(mZ0JVllO_L)&a( zA{V?6DN^K1aZ-wMGvT*FEVmGLmSW6OR9K1bl|r#f%&Za)t3`mV@PAJ1eqQWaBkryd zL3W};A%>%bx>3a56gQuUZdd85lzX?!jc>^BRC0ZsjMK`~23g!DeY>UEWx4i_+;T_W zyCb9S%GL>KHznVjkry9I#h-HT6PX4Kn!(-zI%{09#mfpDv&U&?jJv>o17bGeygNby z5FLQT-S{*J1HqV5p>Geq48?F5^7dgq9B)UUJ_;Ar`1v3daqvHa*FJ(O5jH8v)Zmd8 z>$8xNgWh~h=n$It+JhR`{Jwr`Pp1y)z_{x#gb4)qO;e-FP~Xc@z~ zas2)x?0&`Tzai)z<|n}^xJ)Bv204G=)rZ(OhnP7eKgQ#E=*YC?)LHS64V88reSt4I z^4vN`Z(#C9e(ug=Tj;ixh1=-AgP-iAxfg{m=lmJGn_X{F5khS!`@>mth+!YlFM;X= zW+$*Sfwz)5`!P*Z7?#4p6k4S+E0x2ke2~gbX{=47lZJ^J&S@BuPGdSBq%&B{9xdme znr#M?GM4yg20b&EczY%-vX=P1EcXAy|L*!9|BDK!n_Hw5CeJ#}R!2`AD|NiDqcWQ% z*&NTNa}JN>@Ny2Fav7V;!CbD$h~Y)l7qPd9lSNA(<50}6i@BqO*Gd>w%CS<~8>lhxhJhYstSRGk84Jqk zQo;TT?yuydN|sk~Lp4XL8C%1V8g8%UiCXTgXEucMQZUl_Tzp5^u2*uYB-3~yZA zHP^_bCQdg|+05!@&Nef?g_A9GYh^|2(lO7{h=cJ_V7?hYC|sXN8; uGfX6i;msg+)L$o+Fqb_KTQX?e2_MmcwmHkeqh#pep3J;68-{bac-6X literal 0 HcmV?d00001 diff --git a/C3DMHEAD.OBJ b/C3DMHEAD.OBJ new file mode 100644 index 0000000000000000000000000000000000000000..16079143eac2ac95a187c91a061312471e7af25b GIT binary patch literal 746 zcmZqRW8m=(arF(*3-Jw5P*7m#;Adc9@J-B4^>^}8C^OVEd^OF8fz8D|$UiiIHQvQB z#E}I=FtEmlxJHDqIQzS}a=1ALfn_*-6AL_26H`))jAyVjBxEshu(LEx;AdcD|GY6SG3aA?bjQ>#~ Pbxo(2X|`QV3=AazmN+zZ literal 0 HcmV?d00001 diff --git a/C3_ACT1.C b/C3_ACT1.C new file mode 100644 index 0000000..2706b4f --- /dev/null +++ b/C3_ACT1.C @@ -0,0 +1,1259 @@ +/* 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. + */ + +// C3_PLAY.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +dirtype dirtable[9] = {northwest,north,northeast,west,nodir,east, + southwest,south,southeast}; + + + +/* +============================================================================= + + BONUS ITEMS + +============================================================================= +*/ + +extern statetype s_boltbonus2; +extern statetype s_nukebonus2; + +statetype s_boltbonus = {BOLTOBJPIC,8,NULL,&s_boltbonus2}; +statetype s_boltbonus2 = {BOLTOBJ2PIC,8,NULL,&s_boltbonus}; + +statetype s_nukebonus = {NUKEOBJPIC,8,NULL,&s_nukebonus2}; +statetype s_nukebonus2 = {NUKEOBJ2PIC,8,NULL,&s_nukebonus}; + +statetype s_potionbonus = {POTIONOBJPIC,0,NULL,&s_potionbonus}; +statetype s_rkeybonus = {RKEYOBJPIC,0,NULL,&s_rkeybonus}; +statetype s_ykeybonus = {YKEYOBJPIC,0,NULL,&s_ykeybonus}; +statetype s_gkeybonus = {GKEYOBJPIC,0,NULL,&s_gkeybonus}; +statetype s_bkeybonus = {BKEYOBJPIC,0,NULL,&s_bkeybonus}; +statetype s_scrollbonus = {SCROLLOBJPIC,0,NULL,&s_scrollbonus}; +statetype s_chestbonus = {CHESTOBJPIC,0,NULL,&s_chestbonus}; +statetype s_goalbonus = {NEMESISPIC,0,NULL,&s_goalbonus}; + +/* +=============== += += SpawnBonus += +=============== +*/ + +void SpawnBonus (int tilex, int tiley, int number) +{ + statetype *state; + + if (number == B_BOLT) + state = &s_boltbonus; + else if (number == B_NUKE) + state = &s_nukebonus; + else if (number == B_POTION) + state = &s_potionbonus; + else if (number == B_RKEY) + state = &s_rkeybonus; + else if (number == B_YKEY) + state = &s_ykeybonus; + else if (number == B_GKEY) + state = &s_gkeybonus; + else if (number == B_BKEY) + state = &s_bkeybonus; + else if (number >= B_SCROLL1 && number <= B_SCROLL8) + state = &s_scrollbonus; + else if (number == B_CHEST) + state = &s_chestbonus; + else if (number == B_GOAL) + state = &s_goalbonus; + + SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2); + new->tileobject = true; + new->temp1 = number; + new->obclass = bonusobj; + new->shootable = false; +} + + +/* +============================================================================= + + EXPLODING WALL + +============================================================================= +*/ + + +void T_WallDie (objtype *ob); + +extern statetype s_walldie1; +extern statetype s_walldie2; +extern statetype s_walldie3; +extern statetype s_walldie4; +extern statetype s_walldie5; +extern statetype s_walldie6; + +statetype s_walldie1 = {0,20,NULL,&s_walldie2}; +statetype s_walldie2 = {0,-1,T_WallDie,&s_walldie3}; +statetype s_walldie3 = {0,20,NULL,&s_walldie4}; +statetype s_walldie4 = {0,-1,T_WallDie,&s_walldie5}; +statetype s_walldie5 = {0,20,NULL,&s_walldie6}; +statetype s_walldie6 = {0,-1,T_WallDie,NULL}; + +/* +================ += += ExplodeWall += +================ +*/ + +void ExplodeWall (int tilex, int tiley) +{ + SpawnNewObj (tilex,tiley,&s_walldie1,0); + new->obclass = inertobj; + new->active = true; + (unsigned)actorat[new->tilex][new->tiley] = tilemap[new->tilex][new->tiley] = + *(mapsegs[0]+farmapylookup[new->tiley]+new->tilex) = WALLEXP; +} + + +/* +================ += += T_WallDie += +================ +*/ + +void T_WallDie (objtype *ob) +{ + unsigned tile,other; + + if (++ob->temp1 == 3) + tile = 0; + else + tile = WALLEXP-1 + ob->temp1; + + (unsigned)actorat[ob->tilex][ob->tiley] = tilemap[ob->tilex][ob->tiley] = + *(mapsegs[0]+farmapylookup[ob->tiley]+ob->tilex) = tile; + + if (ob->temp1 == 1) + { + // + // blow up nearby walls + // + other = tilemap[ob->tilex-1][ob->tiley]; + if ((unsigned)(other-EXPWALLSTART)tilex-1,ob->tiley); + other = tilemap[ob->tilex+1][ob->tiley]; + if ((unsigned)(other-EXPWALLSTART)tilex+1,ob->tiley); + other = tilemap[ob->tilex][ob->tiley-1]; + if ((unsigned)(other-EXPWALLSTART)tilex,ob->tiley-1); + other = tilemap[ob->tilex][ob->tiley+1]; + if ((unsigned)(other-EXPWALLSTART)tilex,ob->tiley+1); + } +} + + +/* +============================================================================= + + WARP GATE + +============================================================================= +*/ + +void T_Gate (objtype *ob); + +extern statetype s_gate1; +extern statetype s_gate2; +extern statetype s_gate3; +extern statetype s_gate4; + +extern statetype s_fgate1; +extern statetype s_fgate2; +extern statetype s_fgate3; +extern statetype s_fgate4; + +statetype s_gate1 = {WARP1PIC,12,T_Gate,&s_gate2}; +statetype s_gate2 = {WARP2PIC,12,T_Gate,&s_gate3}; +statetype s_gate3 = {WARP3PIC,12,T_Gate,&s_gate4}; +statetype s_gate4 = {WARP4PIC,12,T_Gate,&s_gate1}; + +statetype s_fgate1 = {WARP1PIC,6,T_Gate,&s_fgate2}; +statetype s_fgate2 = {WARP2PIC,6,T_Gate,&s_fgate3}; +statetype s_fgate3 = {WARP3PIC,6,T_Gate,&s_fgate4}; +statetype s_fgate4 = {WARP4PIC,6,T_Gate,&s_fgate1}; + +/* +=============== += += SpawnWarp += +=============== +*/ + +void SpawnWarp (int tilex, int tiley, int type) +{ + if (type) + SpawnNewObj (tilex,tiley,&s_fgate1,TILEGLOBAL/3); + else + SpawnNewObj (tilex,tiley,&s_gate1,TILEGLOBAL/3); + new->obclass = gateobj; + new->temp1 = type; +} + + +/* +=============== += += T_Gate += +=============== +*/ + +#define STATUSCOLOR 4 + +void T_Gate (objtype *ob) +{ + int spot; + objtype *check; + unsigned temp; + + if (CheckHandAttack (ob) && !playstate) + { + // + // warp + // + temp = bufferofs; + bufferofs = 0; + VW_Bar (26,4,232,9,STATUSCOLOR); // clear text description + bufferofs = temp; + IN_ClearKeysDown (); + if (ob->temp1) + { + // + // teleport inside level + // + for (check=player->next;check;check=check->next) + if (check->obclass==gateobj && check->temp1==ob->temp1 && + check != ob) + { + player->x = check->x; + player->y = check->y; + Thrust (player->angle,TILEGLOBAL/2); // move forwards + Thrust (player->angle,TILEGLOBAL/2); // move forwards + Thrust (player->angle,TILEGLOBAL/2); // move forwards + fizzlein=true; + } + } + else + { + // + // teleport out of level + // + playstate = ex_warped; + spot = *(mapsegs[0]+farmapylookup[ob->tiley]+ob->tilex)-NAMESTART; + if (spot<1) + gamestate.mapon++; + else + gamestate.mapon=spot-1; + SD_PlaySound(WARPUPSND); + } + } +} + + +/* +============================================================================= + + TROLLS + +============================================================================= +*/ + +void T_Troll (objtype *ob); + +extern statetype s_trollpause; + +extern statetype s_troll1; +extern statetype s_troll2; +extern statetype s_troll3; +extern statetype s_troll4; + +extern statetype s_trollattack1; +extern statetype s_trollattack2; +extern statetype s_trollattack3; + +extern statetype s_trollouch; + +extern statetype s_trolldie1; +extern statetype s_trolldie2; +extern statetype s_trolldie3; + + +statetype s_trollpause = {TROLL1PIC,40,NULL,&s_troll2}; + +statetype s_troll1 = {TROLL1PIC,13,T_Troll,&s_troll2}; +statetype s_troll2 = {TROLL2PIC,13,T_Troll,&s_troll3}; +statetype s_troll3 = {TROLL3PIC,13,T_Troll,&s_troll4}; +statetype s_troll4 = {TROLL4PIC,13,T_Troll,&s_troll1}; + +statetype s_trollattack1 = {TROLLATTACK1PIC,20,NULL,&s_trollattack2}; +statetype s_trollattack2 = {TROLLATTACK2PIC,10,T_DoDamage,&s_trollattack3}; +statetype s_trollattack3 = {TROLLATTACK2PIC,40,NULL,&s_trollpause}; + +statetype s_trollouch = {TROLLOUCHPIC,8,NULL,&s_troll1}; + +statetype s_trolldie1 = {TROLLDIE1PIC,8,NULL,&s_trolldie2}; +statetype s_trolldie2 = {TROLLDIE2PIC,8,NULL,&s_trolldie3}; +statetype s_trolldie3 = {TROLLDIE3PIC,0,NULL,&s_trolldie3}; + + +/* +=============== += += SpawnTroll += +=============== +*/ + +void SpawnTroll (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_troll1,40*PIXRADIUS); + new->speed = 2500; + new->obclass = trollobj; + new->shootable = true; + new->hitpoints = 10; +} + + +/* +=============== += += T_Troll += +=============== +*/ + +void T_Troll (objtype *ob) +{ + if (Chase (ob,true)) + { + ob->state = &s_trollattack1; + ob->ticcount = ob->state->tictime; + return; + } +} + + + +/* +============================================================================= + + ORCS + +============================================================================= +*/ + +void T_Orc (objtype *ob); + +extern statetype s_orcpause; + +extern statetype s_orc1; +extern statetype s_orc2; +extern statetype s_orc3; +extern statetype s_orc4; + +extern statetype s_orcattack1; +extern statetype s_orcattack2; +extern statetype s_orcattack3; + +extern statetype s_orcouch; + +extern statetype s_orcdie1; +extern statetype s_orcdie2; +extern statetype s_orcdie3; + + + +statetype s_orcpause = {ORC1PIC,40,NULL,&s_orc2}; + +statetype s_orc1 = {ORC1PIC,20,T_Orc,&s_orc2}; +statetype s_orc2 = {ORC2PIC,20,T_Orc,&s_orc3}; +statetype s_orc3 = {ORC3PIC,20,T_Orc,&s_orc4}; +statetype s_orc4 = {ORC4PIC,20,T_Orc,&s_orc1}; + +statetype s_orcattack1 = {ORCATTACK1PIC,20,NULL,&s_orcattack2}; +statetype s_orcattack2 = {ORCATTACK2PIC,10,T_DoDamage,&s_orcattack3}; +statetype s_orcattack3 = {ORCATTACK2PIC,40,NULL,&s_orcpause}; + +statetype s_orcouch = {ORCOUCHPIC,10,NULL,&s_orc1}; + +statetype s_orcdie1 = {ORCDIE1PIC,8,NULL,&s_orcdie2}; +statetype s_orcdie2 = {ORCDIE2PIC,8,NULL,&s_orcdie3}; +statetype s_orcdie3 = {ORCDIE3PIC,0,NULL,&s_orcdie3}; + + +/* +=============== += += SpawnOrc += +=============== +*/ + +void SpawnOrc (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_orc1,PIXRADIUS*32); + new->obclass = orcobj; + new->speed = 1536; + new->shootable = true; + new->hitpoints = 3; +} + + +/* +=============== += += T_Orc += +=============== +*/ + +void T_Orc (objtype *ob) +{ + if (Chase (ob,true)) + { + ob->state = &s_orcattack1; + ob->ticcount = ob->state->tictime; + return; + } +} + + +/* +============================================================================= + + DEMON + +============================================================================= +*/ + +void T_Demon (objtype *ob); + + +extern statetype s_demonpause; + +extern statetype s_demon1; +extern statetype s_demon2; +extern statetype s_demon3; +extern statetype s_demon4; + +extern statetype s_demonattack1; +extern statetype s_demonattack2; +extern statetype s_demonattack3; + +extern statetype s_demonouch; + +extern statetype s_demondie1; +extern statetype s_demondie2; +extern statetype s_demondie3; + + +statetype s_demonpause = {DEMON1PIC,40,NULL,&s_demon2}; + +statetype s_demon1 = {DEMON1PIC,20,T_Demon,&s_demon2}; +statetype s_demon2 = {DEMON2PIC,20,T_Demon,&s_demon3}; +statetype s_demon3 = {DEMON3PIC,20,T_Demon,&s_demon4}; +statetype s_demon4 = {DEMON4PIC,20,T_Demon,&s_demon1}; + +statetype s_demonattack1 = {DEMONATTACK1PIC,20,NULL,&s_demonattack2}; +statetype s_demonattack2 = {DEMONATTACK2PIC,20,T_DoDamage,&s_demonattack3}; +statetype s_demonattack3 = {DEMONATTACK3PIC,30,NULL,&s_demonpause}; + +statetype s_demonouch = {DEMONOUCHPIC,10,NULL,&s_demon1}; + +statetype s_demondie1 = {DEMONDIE1PIC,20,NULL,&s_demondie2}; +statetype s_demondie2 = {DEMONDIE2PIC,20,NULL,&s_demondie3}; +statetype s_demondie3 = {DEMONDIE3PIC,0,NULL,&s_demondie3}; + + + +/* +=============== += += SpawnDemon += +=============== +*/ + +void SpawnDemon (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_demon1,TILEGLOBAL/2); + new->obclass = demonobj; + new->speed = 2048; + new->shootable = true; + new->hitpoints = 50; +} + + +/* +=============== += += T_Demon += +=============== +*/ + +void T_Demon (objtype *ob) +{ + if (Chase (ob,true)) + { + ob->state = &s_demonattack1; + ob->ticcount = ob->state->tictime; + return; + } +} + +/* +============================================================================= + + MSHOTS + +temp1 = dir + +============================================================================= +*/ + +#define MSHOTDAMAGE 2 +#define MSHOTSPEED 10000 + +void T_Mshot (objtype *ob); + + +extern statetype s_mshot1; +extern statetype s_mshot2; + +statetype s_mshot1 = {PSHOT1PIC,8,&T_Mshot,&s_mshot2}; +statetype s_mshot2 = {PSHOT2PIC,8,&T_Mshot,&s_mshot1}; + + +/* +=============== += += T_Mshot += +=============== +*/ + +void T_Mshot (objtype *ob) +{ + objtype *check; + long xmove,ymove,speed; + + xmove = ymove = 0; + + switch (ob->dir) + { + case north: + ymove = -ob->speed*tics; + break; + case east: + xmove = ob->speed*tics; + break; + case south: + ymove = ob->speed*tics; + break; + case west: + xmove = -ob->speed*tics; + break; + } + + ob->x+=xmove; + ob->y+=ymove; + + CalcBounds (ob); + + ob->tilex = ob->x>>TILESHIFT; + ob->tiley = ob->y>>TILESHIFT; + + if (tilemap[ob->tilex][ob->tiley]) + { + SD_PlaySound (SHOOTWALLSND); + ob->state = NULL; + return; + } + +// +// check final position for monsters hit +// + if ( ob->xl <= player->xh + && ob->xh >= player->xl + && ob->yl <= player->yh + && ob->yh >= player->yl) + { + TakeDamage (MSHOTDAMAGE*2); + ob->state = NULL; + return; + } + + for (check = player->next; check; check=check->next) + if (ob->shootable && ob->obclass != mageobj + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + ShootActor (check,MSHOTDAMAGE); + ob->state = NULL; + return; + } +} + + + + +/* +============================================================================= + + MAGE + +============================================================================= +*/ + + +void T_Mage (objtype *ob); +void T_MageShoot (objtype *ob); + +extern statetype s_magepause; + +extern statetype s_mage1; +extern statetype s_mage2; + +extern statetype s_mageattack1; +extern statetype s_mageattack2; +extern statetype s_mageattack3; + +extern statetype s_mageouch; + +extern statetype s_magedie1; +extern statetype s_magedie2; + + +statetype s_magepause = {MAGE1PIC,100,NULL,&s_mage2}; + +statetype s_mage1 = {MAGE1PIC,20,T_Mage,&s_mage2}; +statetype s_mage2 = {MAGE2PIC,20,T_Mage,&s_mage1}; + +statetype s_mageattack1 = {MAGEATTACKPIC,20,NULL,&s_mageattack2}; +statetype s_mageattack2 = {MAGEATTACKPIC,-1,T_MageShoot,&s_mageattack3}; +statetype s_mageattack3 = {MAGEATTACKPIC,30,NULL,&s_magepause}; + +statetype s_mageouch = {MAGEOUCHPIC,10,NULL,&s_mage1}; + +statetype s_magedie1 = {MAGEDIE1PIC,20,NULL,&s_magedie2}; +statetype s_magedie2 = {MAGEDIE2PIC,0,NULL,&s_magedie2}; + + +/* +=============== += += SpawnMage += +=============== +*/ + +void SpawnMage (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_mage1,TILEGLOBAL/2); + new->obclass = mageobj; + new->speed = 2048; + new->shootable = true; + new->hitpoints = 5; +} + + +/* +=============== += += T_Mage += +=============== +*/ + +void T_Mage (objtype *ob) +{ + Chase (ob,false); +// +// check for line up with player +// + + if (ob->x-PIXRADIUS*14 < player->xh + && ob->x+PIXRADIUS > player->xl) + { + ob->temp1 = 1; + ob->state = &s_mageattack1; + } + else if (ob->y-PIXRADIUS*14 < player->yh + && ob->y+PIXRADIUS > player->yl) + { + ob->temp1 = 0; + ob->state = &s_mageattack1; + } +} + + +/* +=============== += += T_MageShoot += +=============== +*/ + +void T_MageShoot (objtype *ob) +{ + SpawnNewObjFrac (ob->x,ob->y,&s_mshot1,PIXRADIUS*14); + new->obclass = mshotobj; + new->speed = MSHOTSPEED; + if (ob->temp1) + { + if (ob->tiley < player->tiley) + new->dir = south; + else + new->dir = north; + } + else + { + if (ob->tilex < player->tilex) + new->dir = east; + else + new->dir = west; + } +} + + +/* +============================================================================= + + nemesis + +============================================================================= +*/ + + +void T_Nemesis (objtype *ob); +void T_NemesisShoot (objtype *ob); + +extern statetype s_grelpause; + +extern statetype s_grel1; +extern statetype s_grel2; + +extern statetype s_grelattack1; +extern statetype s_grelattack2; +extern statetype s_grelattack3; + +extern statetype s_grelouch; + +extern statetype s_greldie1; +extern statetype s_greldie2; +extern statetype s_greldie3; +extern statetype s_greldie4; +extern statetype s_greldie5; +extern statetype s_greldie6; + + +statetype s_grelpause = {GREL1PIC,50,NULL,&s_grel2}; + +statetype s_grel1 = {GREL1PIC,20,T_Nemesis,&s_grel2}; +statetype s_grel2 = {GREL2PIC,20,T_Nemesis,&s_grel1}; + +statetype s_grelattack1 = {GRELATTACKPIC,20,NULL,&s_grelattack2}; +statetype s_grelattack2 = {GRELATTACKPIC,-1,T_NemesisShoot,&s_grelattack3}; +statetype s_grelattack3 = {GRELATTACKPIC,30,NULL,&s_grelpause}; + +statetype s_grelouch = {GRELHITPIC,6,NULL,&s_grel1}; + +statetype s_greldie1 = {GRELDIE1PIC,20,NULL,&s_greldie2}; +statetype s_greldie2 = {GRELDIE2PIC,20,NULL,&s_greldie3}; +statetype s_greldie3 = {GRELDIE3PIC,20,NULL,&s_greldie4}; +statetype s_greldie4 = {GRELDIE4PIC,20,NULL,&s_greldie5}; +statetype s_greldie5 = {GRELDIE5PIC,20,NULL,&s_greldie6}; +statetype s_greldie6 = {GRELDIE6PIC,0,NULL,&s_greldie6}; + + +/* +=============== += += SpawnNemesis += +=============== +*/ + +void SpawnNemesis (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_grel1,PIXRADIUS*56); + new->obclass = grelmobj; + new->speed = 2048; + new->shootable = true; + new->hitpoints = 100; +} + + +/* +=============== += += T_Nemesis += +=============== +*/ + +void T_Nemesis (objtype *ob) +{ + Chase (ob,false); +// +// check for line up with player +// + if (ob->tilex == player->tilex) + { + ob->temp1 = 1; + ob->state = &s_grelattack1; + } + else if (ob->tiley == player->tiley) + { + ob->temp1 = 0; + ob->state = &s_grelattack1; + } +} + + +/* +=============== += += T_NemesisShoot += +=============== +*/ + +void T_NemesisShoot (objtype *ob) +{ + SpawnNewObjFrac (ob->x,ob->y,&s_mshot1,PIXRADIUS*14); + new->obclass = mshotobj; + new->speed = MSHOTSPEED; + if (ob->temp1) + { + if (ob->tiley < player->tiley) + new->dir = south; + else + new->dir = north; + } + else + { + if (ob->tilex < player->tilex) + new->dir = east; + else + new->dir = west; + } +} + + +/* +============================================================================= + + BAT + +============================================================================= +*/ + +void T_Bat (objtype *ob); +void T_BatPast (objtype *ob); + +extern statetype s_bat1; +extern statetype s_bat2; +extern statetype s_bat3; +extern statetype s_bat4; + +extern statetype s_batdie1; +extern statetype s_batdie2; + + +statetype s_bat1 = {BAT1PIC,6,T_Bat,&s_bat2}; +statetype s_bat2 = {BAT2PIC,6,T_Bat,&s_bat3}; +statetype s_bat3 = {BAT3PIC,6,T_Bat,&s_bat4}; +statetype s_bat4 = {BAT4PIC,6,T_Bat,&s_bat1}; + +statetype s_batpast = {BAT4PIC,80,T_BatPast,&s_bat1}; + +statetype s_batdie1 = {BATDIE1PIC,8,NULL,&s_batdie2}; +statetype s_batdie2 = {BATDIE2PIC,8,NULL,NULL}; + + +/* +=============== += += SpawnBat += +=============== +*/ + +void SpawnBat (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_bat1,PIXRADIUS*24); + new->obclass =batobj; + new->shootable = true; + + new->hitpoints = 1; + new->speed = 2000; +} + + +/* +================================== += += BatChaseThink += +================================== +*/ + +void BatChaseThink (objtype *obj) +{ + int deltax,deltay; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + if (deltax>0) + deltax = 2; + else if (deltax<0) + deltax = 0; + else deltax = 1; + + if (deltay>0) + deltay = 2; + else if (deltay<0) + deltay = 0; + else deltay = 1; + + obj->dir = dirtable[deltay*3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[deltay*3+1]; + if (Walk(obj)) + return; + + obj->dir = nodir; +} + + +void BatRunThink (objtype *obj) +{ + int deltax,deltay; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + if (deltax>=0) + deltax = 0; + else + deltax = 2; + + if (deltay>=0) + deltay = 0; + else + deltay = 2; + + obj->dir = dirtable[deltay*3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[deltay*3+1]; + Walk(obj); +} + + + +/* +=============== += += T_Bat += +=============== +*/ + +void T_Bat (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = ob->speed*tics; + size = (long)ob->size + player->size + move; + + + do + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size && !ob->temp1) + { + TakeDamage (4); + ob->temp1 = 2; + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + if (ob->dir == nodir) + ob->dir = north; + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (ob->temp1) + { + Walk (ob); // go straight + if (!--ob->temp1) + { + ob->state = &s_batpast; + ob->ticcount = ob->state->tictime; + } + } + else + BatChaseThink (ob); // head towards player + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } while (0); // just once + CalcBounds (ob); +} + + +/* +=============== += += T_BatPast += +=============== +*/ + +void T_BatPast (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = ob->speed*tics; + + do + { + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + BatRunThink (ob); + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } while (0); //(move) + CalcBounds (ob); +} + + + +/* +============================================================================= + + BOUNCE + +temp2 = set when hit player, reset when hit wall + +============================================================================= +*/ + +#define SPDBOUNCE 4096 +#define DMGBOUNCE 10 + +void T_Bounce (objtype *ob); + +extern statetype s_bounce1; +extern statetype s_bounce2; + + +statetype s_bounce1 = {BIGPSHOT1PIC,8,T_Bounce,&s_bounce2}; +statetype s_bounce2 = {BIGPSHOT2PIC,8,T_Bounce,&s_bounce1}; + +/* +=============== += += SpawnBounce += +=============== +*/ + +void SpawnBounce (int tilex, int tiley, boolean towest) +{ + SpawnNewObj(tilex,tiley,&s_bounce1,24*PIXRADIUS); + new->obclass = bounceobj; + if (towest) + new->dir = west; + else + new->dir = north; +} + + +/* +=============== += += T_Bounce += +=============== +*/ + +void T_Bounce (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = SPDBOUNCE*tics; + size = (long)ob->size + player->size + move; + + while (move) + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size && !ob->temp2) + { + ob->temp2 = 1; + TakeDamage (DMGBOUNCE); + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + // + // bounce if hit wall + // + switch (ob->dir) + { + case north: + if (tilemap[ob->tilex][--ob->tiley]) + { + ob->dir = south; + ob->tiley+=2; + ob->temp2 = 0; + } + break; + case east: + if (tilemap[++ob->tilex][ob->tiley]) + { + ob->dir = west; + ob->tilex-=2; + ob->temp2 = 0; + } + break; + case south: + if (tilemap[ob->tilex][++ob->tiley]) + { + ob->dir = north; + ob->tiley-=2; + ob->temp2 = 0; + } + break; + case west: + if (tilemap[--ob->tilex][ob->tiley]) + { + ob->dir = east; + ob->tilex+=2; + ob->temp2 = 0; + } + break; + } + + ob->distance = TILEGLOBAL; + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } + CalcBounds (ob); +} + diff --git a/C3_ASM.ASM b/C3_ASM.ASM new file mode 100644 index 0000000..d9da6eb --- /dev/null +++ b/C3_ASM.ASM @@ -0,0 +1,197 @@ +; 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. + +IDEAL + +MODEL MEDIUM,C + +VIEWWIDTH = (33*8) +GC_INDEX = 03CEh + +DATASEG +EVEN + +;=================== Tables filled in by DrawVWall ========================== + +; +; wallheight has the height (scale number) of that collumn of scaled wall +; it is pre bounded to 1-MAXSCALE (the actuial height on screen is 2*height) +; +wallheight dw VIEWWIDTH dup (?) + +; +; wallwidth has the pixel width (1-7) of that collumn +; +wallwidth dw VIEWWIDTH dup (?) + +; +; wallseg has the segment of the wall picture +; +wallseg dw VIEWWIDTH dup (?) + +; +; wallofs has the offset of the wall picture +; +wallofs dw VIEWWIDTH dup (?) + +;============================================================================ + +; +; screenbyte is just position/8 +; +LABEL screenbyte WORD +pos = 0 +REPT VIEWWIDTH + dw pos/8 +pos = pos+1 +ENDM + +; +; screenbit is (position&7)*16 +; +LABEL screenbit WORD +pos = 0 +REPT VIEWWIDTH + dw (pos AND 7)*16 +pos = pos+1 +ENDM + +; +; Use offset: screenbit[]+pixwidth*2 +; acess from bitmasks-2+offset for one biased pixwidth +; the low byte of bitmasks is for the first screen byte, the high byte +; is the bitmask for the second screen byte (if non 0) +; + +bitmasks dw 0080h,00c0h,00e0h,00f0h,00f8h,00fch,00feh,00ffh + dw 0040h,0060h,0070h,0078h,007ch,007eh,007fh,807fh + dw 0020h,0030h,0038h,003ch,003eh,003fh,803fh,0c03fh + dw 0010h,0018h,001ch,001eh,001fh,801fh,0c01fh,0e01fh + dw 0008h,000ch,000eh,000fh,800fh,0c00fh,0e00fh,0f00fh + dw 0004h,0006h,0007h,8007h,0c007h,0e007h,0f007h,0f807h + dw 0002h,0003h,8003h,0c003h,0e003h,0f003h,0f803h,0fc03h + dw 0001h,8001h,0c001h,0e001h,0f001h,0f801h,0fc01h,0fe01h + + +; +; wallscalecall is a far pointer to the start of a compiled scaler +; The low word will never change, while the high word is set to +; compscaledirectory[scale] +; +wallscalecall dd (65*6) ; offset of t_compscale->code[0] + + +PUBLIC wallheight,wallwidth,wallseg,wallofs,screenbyte,screenbit +PUBLIC bitmasks,wallscalecall + + +EXTRN scaledirectory:WORD ; array of MAXSCALE segment pointers to + ; compiled scalers +EXTRN screenseg:WORD ; basically just 0xa000 +EXTRN bufferofs:WORD ; offset of the current work screen + +CODESEG + +;============================================================================ +; +; ScaleWalls +; +; AX AL is scratched in bit mask setting and scaling +; BX table index +; CX pixwidth*2 +; DX GC_INDEX +; SI offset into wall data to scale from, allways 0,64,128,...4032 +; DI byte at top of screen that the collumn is contained in +; BP x pixel * 2, index into VIEWWIDTH wide tables +; DS segment of the wall data to texture map +; ES screenseg +; SS addressing DGROUP variables +; +;============================================================================ + +PROC ScaleWalls +PUBLIC ScaleWalls +USES SI,DI,BP + + xor bp,bp ; start at location 0 in the tables + mov dx,GC_INDEX+1 + mov es,[screenseg] + +; +; scale one collumn of data, possibly across two bytes +; +nextcollumn: + + mov bx,[wallheight+bp] ; height of walls (1-MAXSCALE) + shl bx,1 + mov ax,[ss:scaledirectory+bx] ; segment of the compiled scaler + mov [WORD PTR ss:wallscalecall+2],ax + + mov cx,[wallwidth+bp] + or cx,cx + jnz okwidth + mov cx,2 + jmp next + +okwidth: + shl cx,1 + mov ds,[wallseg+bp] + mov si,[wallofs+bp] + + mov di,[screenbyte+bp] ; byte at the top of the scaled collumn + add di,[ss:bufferofs] ; offset of current page flip + mov bx,[screenbit+bp] ; 0-7 << 4 + add bx,cx + mov ax,[ss:bitmasks-2+bx] + out dx,al ; set bit mask register + call [DWORD PTR ss:wallscalecall] ; scale the line of pixels + or ah,ah ; is there anything in the second byte? + jnz secondbyte +; +; next +; +next: + add bp,cx + cmp bp,VIEWWIDTH*2 + jb nextcollumn + jmp done + +; +; draw a second byte for vertical strips that cross two bytes +; +secondbyte: + mov al,ah + inc di ; next byte over + out dx,al ; set bit mask register + call [DWORD PTR ss:wallscalecall] ; scale the line of pixels +; +; next +; + add bp,cx + cmp bp,VIEWWIDTH*2 + jb nextcollumn + +done: + mov ax,ss + mov ds,ax + ret + +ENDP + + +END + diff --git a/C3_DEBUG.C b/C3_DEBUG.C new file mode 100644 index 0000000..4aa6831 --- /dev/null +++ b/C3_DEBUG.C @@ -0,0 +1,606 @@ +/* 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. + */ + +// C3_DEBUG.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define VIEWTILEX 20 +#define VIEWTILEY (VIEWHEIGHT/16) + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +int maporgx; +int maporgy; +enum {mapview,tilemapview,actoratview,visview} viewtype; + +void ViewMap (void); + +//=========================================================================== + + + +/* +================== += += DebugMemory += +================== +*/ + +void DebugMemory (void) +{ + int i; + char scratch[80],str[10]; + long mem; + spritetype _seg *block; + + VW_FixRefreshBuffer (); + US_CenterWindow (16,7); + +#if 0 + CA_OpenDebug (); + for (i=0;i=0;y--) + VW_ScreenToScreen (source+y*64,y*40,40,1); + } + + IN_Shutdown (); + + VW_WaitVBL(70); + bioskey(0); + VW_WaitVBL(70); + Quit (NULL); +} + + +//=========================================================================== + +/* +================ += += ShapeTest += +================ +*/ + +void ShapeTest (void) +{ + +} + + +//=========================================================================== + +#define sc_1 0x02 +#define sc_2 0x03 +#define sc_3 0x04 +#define sc_4 0x05 +#define sc_5 0x06 +#define sc_6 0x07 +#define sc_7 0x08 +#define sc_8 0x09 +#define sc_9 0x0a +#define sc_0 0x0b + + + +/* +================ += += DebugKeys += +================ +*/ + +int DebugKeys (void) +{ + boolean esc; + int level,i; + + if (Keyboard[sc_B]) // B = border color + { + CenterWindow(24,3); + PrintY+=6; + US_Print(" Border color (0-15):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=15) + VW_ColorBorder (level); + } + return 1; + } + +#if 0 + + if (Keyboard[sc_C]) // C = count objects + { + CountObjects(); + return 1; + } + + + if (Keyboard[sc_D]) // D = start / end demo record + { + if (DemoMode == demo_Off) + StartDemoRecord (); + else if (DemoMode == demo_Record) + { + EndDemoRecord (); + playstate = ex_completed; + } + return 1; + } + +#endif + + if (Keyboard[sc_E]) // E = quit level + { + if (tedlevel) + TEDDeath(); + playstate = ex_warped; + gamestate.mapon++; + } + + if (Keyboard[sc_F]) // F = facing spot + { + CenterWindow (12,4); + US_Print ("X:"); + US_PrintUnsigned (player->x); + US_Print ("Y:"); + US_PrintUnsigned (player->y); + US_Print ("A:"); + US_PrintUnsigned (player->angle); + VW_UpdateScreen(); + IN_Ack(); + return 1; + } + + if (Keyboard[sc_G]) // G = god mode + { + CenterWindow (12,2); + if (godmode) + US_PrintCentered ("God mode OFF"); + else + US_PrintCentered ("God mode ON"); + VW_UpdateScreen(); + IN_Ack(); + godmode ^= 1; + return 1; + } + if (Keyboard[sc_H]) // H = hurt self + { + TakeDamage (5); + } + else if (Keyboard[sc_I]) // I = item cheat + { + CenterWindow (12,3); + US_PrintCentered ("Free items!"); + VW_UpdateScreen(); + for (i=0;i<4;i++) + { + GiveBolt (); + GiveNuke (); + GivePotion (); + if (!gamestate.keys[i]) + GiveKey (i); + } + for (i=0;i<8;i++) + GiveScroll (i,false); + + IN_Ack (); + return 1; + } + else if (Keyboard[sc_M]) // M = memory info + { + DebugMemory(); + return 1; + } + else if (Keyboard[sc_O]) // O = overhead + { + ViewMap(); + return 1; + } + else if (Keyboard[sc_P]) // P = pause with no screen disruptioon + { + PicturePause (); + return 1; + } + else if (Keyboard[sc_S]) // S = slow motion + { + singlestep^=1; + CenterWindow (18,3); + if (singlestep) + US_PrintCentered ("Slow motion ON"); + else + US_PrintCentered ("Slow motion OFF"); + VW_UpdateScreen(); + IN_Ack (); + return 1; + } + else if (Keyboard[sc_S]) // T = shape test + { + ShapeTest (); + return 1; + } + else if (Keyboard[sc_V]) // V = extra VBLs + { + CenterWindow(30,3); + PrintY+=6; + US_Print(" Add how many extra VBLs(0-8):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=8) + extravbls = level; + } + return 1; + } + else if (Keyboard[sc_W]) // W = warp to level + { + CenterWindow(26,3); + PrintY+=6; + US_Print(" Warp to which level(1-21):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>0 && level<21) + { + gamestate.mapon = level-1; + playstate = ex_warped; + } + } + return 1; + } + else if (Keyboard[sc_X]) // X = item cheat + { + CenterWindow (12,3); + US_PrintCentered ("Extra stuff!"); + VW_UpdateScreen(); + for (i=0;i<4;i++) + { + GiveBolt (); + GiveNuke (); + GivePotion (); + } + IN_Ack (); + return 1; + } + else if (Keyboard[sc_Z]) // Z = game over + { + + } + else if (LastScan >= sc_1 && LastScan <= sc_8) // free scrolls + { + GiveScroll (LastScan-sc_1,false); + IN_ClearKeysDown (); + } + + return 0; +} + + +/* +===================== += += LatchDrawChar += +===================== +*/ + +void LatchDrawChar (unsigned x, unsigned y, unsigned picnum) +{ + unsigned source, dest; + + dest = bufferofs + ylookup[y]+x; + source = latchpics[0]+picnum*8; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm dec bx + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] + +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + + +/* +===================== += += LatchDrawTile += +===================== +*/ + +void LatchDrawTile (unsigned x, unsigned y, unsigned picnum) +{ + unsigned source, dest; + + dest = bufferofs + ylookup[y]+x; + source = tileoffsets[picnum]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,2 + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] +asm mov dx,16 + +lineloop: +asm movsb +asm movsb +asm add di,bx + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + + +/* +=================== += += OverheadRefresh += +=================== +*/ + +void OverheadRefresh (void) +{ + unsigned x,y,endx,endy,sx,sy; + unsigned tile; + + + if (++screenpage == 3) + screenpage = 0; + + bufferofs = screenloc[screenpage]; + + endx = maporgx+VIEWTILEX; + endy = maporgy+VIEWTILEY; + + for (y=maporgy;y>12)); + LatchDrawChar(sx+1,sy,NUMBERCHARS+((tile&0x0f00)>>8)); + LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4)); + LatchDrawChar(sx+1,sy+8,NUMBERCHARS+(tile&0x000f)); + } + } + + VW_SetScreen (bufferofs,0); + displayofs = bufferofs; +} + + +/* +=================== += += ViewMap += +=================== +*/ + +void ViewMap (void) +{ + boolean button0held; + + viewtype = actoratview; + button0held = false; + + + maporgx = player->tilex - VIEWTILEX/2; + if (maporgx<0) + maporgx = 0; + maporgy = player->tiley - VIEWTILEY/2; + if (maporgy<0) + maporgy = 0; + + do + { +// +// let user pan around +// + IN_ReadControl(0,&c); + if (c.xaxis == -1 && maporgx>0) + maporgx--; + if (c.xaxis == 1 && maporgx0) + maporgy--; + if (c.yaxis == 1 && maporgyvisview) + viewtype = mapview; + } + if (!c.button0) + button0held = false; + + + OverheadRefresh (); + + } while (!Keyboard[sc_Escape]); + + IN_ClearKeysDown (); + DrawPlayScreen (); +} + + diff --git a/C3_DEF.H b/C3_DEF.H new file mode 100644 index 0000000..c0fe07f --- /dev/null +++ b/C3_DEF.H @@ -0,0 +1,533 @@ +/* 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. + */ + +#include "ID_HEADS.H" +#include +#include + +//#define PROFILE + +/* +============================================================================= + + GLOBAL CONSTANTS + +============================================================================= +*/ + +#define NAMESTART 180 + + +#define UNMARKGRCHUNK(chunk) (grneeded[chunk]&=~ca_levelbit) + +#define MOUSEINT 0x33 + +#define EXPWALLSTART 8 +#define NUMEXPWALLS 7 +#define WALLEXP 15 +#define NUMFLOORS 36 + +#define NUMFLOORS 36 + +#define NUMLATCHPICS 100 +#define NUMSCALEPICS 100 +#define NUMSCALEWALLS 30 + + +#define FLASHCOLOR 5 +#define FLASHTICS 4 + + +#define NUMLEVELS 20 + +#define VIEWX 0 // corner of view window +#define VIEWY 0 +#define VIEWWIDTH (33*8) // size of view window +#define VIEWHEIGHT (18*8) +#define VIEWXH (VIEWX+VIEWWIDTH-1) +#define VIEWYH (VIEWY+VIEWHEIGHT-1) + +#define CENTERX (VIEWX+VIEWWIDTH/2-1) // middle of view window +#define CENTERY (VIEWY+VIEWHEIGHT/2-1) + +#define GLOBAL1 (1l<<16) +#define TILEGLOBAL GLOBAL1 +#define TILESHIFT 16l + +#define MINDIST (2*GLOBAL1/5) +#define FOCALLENGTH (TILEGLOBAL) // in global coordinates + +#define ANGLES 360 // must be divisable by 4 + +#define MAPSIZE 64 // maps are 64*64 max +#define MAXACTORS 150 // max number of tanks, etc / map + +#define NORTH 0 +#define EAST 1 +#define SOUTH 2 +#define WEST 3 + +#define SIGN(x) ((x)>0?1:-1) +#define ABS(x) ((int)(x)>0?(x):-(x)) +#define LABS(x) ((long)(x)>0?(x):-(x)) + +#define MAXSCALE (VIEWWIDTH/2) + + +#define MAXBODY 64 +#define MAXSHOTPOWER 56 + +#define SCREEN1START 0 +#define SCREEN2START 8320 + +#define PAGE1START 0x900 +#define PAGE2START 0x2000 +#define PAGE3START 0x3700 +#define FREESTART 0x4e00 + +#define PIXRADIUS 512 + +#define STATUSLINES (200-VIEWHEIGHT) + +enum bonusnumbers {B_BOLT,B_NUKE,B_POTION,B_RKEY,B_YKEY,B_GKEY,B_BKEY,B_SCROLL1, + B_SCROLL2,B_SCROLL3,B_SCROLL4,B_SCROLL5,B_SCROLL6,B_SCROLL7,B_SCROLL8, + B_GOAL,B_CHEST}; + + +/* +============================================================================= + + GLOBAL TYPES + +============================================================================= +*/ + +enum {BLANKCHAR=9,BOLTCHAR,NUKECHAR,POTIONCHAR,KEYCHARS,SCROLLCHARS=17, + NUMBERCHARS=25}; + +typedef long fixed; + +typedef struct {int x,y;} tilept; +typedef struct {fixed x,y;} globpt; + +typedef struct +{ + int x1,x2,leftclip,rightclip;// first pixel of wall (may not be visable) + unsigned height1,height2,color,walllength,side; + long planecoord; +} walltype; + +typedef enum + {nothing,playerobj,bonusobj,orcobj,batobj,skeletonobj,trollobj,demonobj, + mageobj,pshotobj,bigpshotobj,mshotobj,inertobj,bounceobj,grelmobj + ,gateobj} classtype; + +typedef enum {north,east,south,west,northeast,southeast,southwest, + northwest,nodir} dirtype; // a catacombs 2 carryover + + +typedef struct statestruct +{ + int shapenum; + int tictime; + void (*think) (); + struct statestruct *next; +} statetype; + + +typedef struct objstruct +{ + enum {no,yes} active; + int ticcount; + classtype obclass; + statetype *state; + + boolean shootable; + boolean tileobject; // true if entirely inside one tile + + long distance; + dirtype dir; + fixed x,y; + unsigned tilex,tiley; + int viewx; + unsigned viewheight; + + int angle; + int hitpoints; + long speed; + + unsigned size; // global radius for hit rect calculation + fixed xl,xh,yl,yh; // hit rectangle + + int temp1,temp2; + struct objstruct *next,*prev; +} objtype; + + +typedef struct +{ + int difficulty; + int mapon; + int bolts,nukes,potions,keys[4],scrolls[8]; + long score; + int body,shotpower; +} gametype; + +typedef enum {ex_stillplaying,ex_died,ex_warped,ex_resetgame + ,ex_loadedgame,ex_victorious,ex_abort} exittype; + + +/* +============================================================================= + + C3_MAIN DEFINITIONS + +============================================================================= +*/ + +extern char str[80],str2[20]; +extern unsigned tedlevelnum; +extern boolean tedlevel; +extern gametype gamestate; +extern exittype playstate; + + +void NewGame (void); +boolean SaveTheGame(int file); +boolean LoadTheGame(int file); +void ResetGame(void); +void ShutdownId (void); +void InitGame (void); +void Quit (char *error); +void TEDDeath(void); +void DemoLoop (void); +void SetupScalePic (unsigned picnum); +void SetupScaleWall (unsigned picnum); +void SetupScaling (void); +void main (void); + +/* +============================================================================= + + C3_GAME DEFINITIONS + +============================================================================= +*/ + +extern unsigned latchpics[NUMLATCHPICS]; +extern unsigned tileoffsets[NUMTILE16]; +extern unsigned textstarts[27]; + + +#define L_CHARS 0 +#define L_NOSHOT 1 +#define L_SHOTBAR 2 +#define L_NOBODY 3 +#define L_BODYBAR 4 + + +void ScanInfoPlane (void); +void ScanText (void); +void SetupGameLevel (void); +void Victory (void); +void Died (void); +void NormalScreen (void); +void DrawPlayScreen (void); +void LoadLatchMem (void); +void FizzleFade (unsigned source, unsigned dest, + unsigned width,unsigned height, boolean abortable); +void FizzleOut (int showlevel); +void FreeUpMemory (void); +void GameLoop (void); + + +/* +============================================================================= + + C3_PLAY DEFINITIONS + +============================================================================= +*/ + +extern ControlInfo c; +extern boolean running,slowturn; + +extern int bordertime; + +extern byte tilemap[MAPSIZE][MAPSIZE]; +extern objtype *actorat[MAPSIZE][MAPSIZE]; +extern byte spotvis[MAPSIZE][MAPSIZE]; + +extern objtype objlist[MAXACTORS],*new,*obj,*player; + +extern unsigned farmapylookup[MAPSIZE]; +extern byte *nearmapylookup[MAPSIZE]; +extern byte update[]; + +extern boolean godmode,singlestep; +extern int extravbls; + +extern int mousexmove,mouseymove; +extern int pointcount,pointsleft; + + +void CenterWindow(word w,word h); +void DebugMemory (void); +void PicturePause (void); +int DebugKeys (void); +void CheckKeys (void); +void InitObjList (void); +void GetNewObj (boolean usedummy); +void RemoveObj (objtype *gone); +void PollControlls (void); +void PlayLoop (void); + + +/* +============================================================================= + + C3_STATE DEFINITIONS + +============================================================================= +*/ + +void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size); +void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size); +boolean CheckHandAttack (objtype *ob); +void T_DoDamage (objtype *ob); +boolean Walk (objtype *ob); +void ChaseThink (objtype *obj, boolean diagonal); +void MoveObj (objtype *ob, long move); +boolean Chase (objtype *ob, boolean diagonal); + +extern dirtype opposite[9]; + +/* +============================================================================= + + C3_TRACE DEFINITIONS + +============================================================================= +*/ + +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max); +int BackTrace (int finish); +void ForwardTrace (void); +int FinishWall (void); +void InsideCorner (void); +void OutsideCorner (void); +void FollowWalls (void); + +extern boolean aborttrace; + +/* +============================================================================= + + C3_DRAW DEFINITIONS + +============================================================================= +*/ + +#define MAXWALLS 50 +#define DANGERHIGH 45 + +#define MIDWALL (MAXWALLS/2) + +//========================================================================== + +extern tilept tile,lasttile,focal,left,mid,right; + +extern globpt edge,view; + +extern unsigned screenloc[3]; +extern unsigned freelatch; + +extern int screenpage; + +extern boolean fizzlein; + +extern long lasttimecount; + +extern int firstangle,lastangle; + +extern fixed prestep; + +extern int traceclip,tracetop; + +extern fixed sintable[ANGLES+ANGLES/4],*costable; + +extern fixed viewx,viewy,viewsin,viewcos; // the focal point +extern int viewangle; + +extern fixed scale,scaleglobal; +extern unsigned slideofs; + +extern int zbuffer[VIEWXH+1]; + +extern walltype walls[MAXWALLS],*leftwall,*rightwall; + + +extern fixed tileglobal; +extern fixed focallength; +extern fixed mindist; +extern int viewheight; +extern fixed scale; + +extern int walllight1[NUMFLOORS]; +extern int walldark1[NUMFLOORS]; +extern int walllight2[NUMFLOORS]; +extern int walldark2[NUMFLOORS]; + +//========================================================================== + +void DrawLine (int xl, int xh, int y,int color); +void DrawWall (walltype *wallptr); +void TraceRay (unsigned angle); +fixed FixedByFrac (fixed a, fixed b); +void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight); +fixed TransformX (fixed gx, fixed gy); +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max); +void ForwardTrace (void); +int FinishWall (void); +int TurnClockwise (void); +int TurnCounterClockwise (void); +void FollowWall (void); + +void NewScene (void); +void BuildTables (void); + + +/* +============================================================================= + + C3_SCALE DEFINITIONS + +============================================================================= +*/ + + +#define COMPSCALECODESTART (65*6) // offset to start of code in comp scaler + +typedef struct +{ + unsigned codeofs[65]; + unsigned start[65]; + unsigned width[65]; + byte code[]; +} t_compscale; + +typedef struct +{ + unsigned width; + unsigned codeofs[64]; +} t_compshape; + + +extern unsigned scaleblockwidth, + scaleblockheight, + scaleblockdest; + +extern byte plotpix[8]; +extern byte bitmasks1[8][8]; +extern byte bitmasks2[8][8]; + + +extern t_compscale _seg *scaledirectory[MAXSCALE+1]; +extern t_compshape _seg *shapedirectory[NUMSCALEPICS]; +extern memptr walldirectory[NUMSCALEWALLS]; +extern unsigned shapesize[MAXSCALE+1]; + +void DeplanePic (int picnum); +void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale); +unsigned BuildCompShape (t_compshape _seg **finalspot); + + +/* +============================================================================= + + C3_ASM DEFINITIONS + +============================================================================= +*/ + +extern unsigned wallheight [VIEWWIDTH]; +extern unsigned wallwidth [VIEWWIDTH]; +extern unsigned wallseg [VIEWWIDTH]; +extern unsigned wallofs [VIEWWIDTH]; +extern unsigned screenbyte [VIEWWIDTH]; +extern unsigned screenbit [VIEWWIDTH]; +extern unsigned bitmasks [64]; + +extern long wallscalecall; + +void ScaleWalls (void); + +/* +============================================================================= + + C3_WIZ DEFINITIONS + +============================================================================= +*/ + +#define MAXHANDHEIGHT 72 + +extern long lastnuke; +extern int handheight; +extern int boltsleft; + +/* +============================================================================= + + C3_ACT1 DEFINITIONS + +============================================================================= +*/ + +extern statetype s_trollouch; +extern statetype s_trolldie1; + + +extern statetype s_orcpause; + +extern statetype s_orc1; +extern statetype s_orc2; +extern statetype s_orc3; +extern statetype s_orc4; + +extern statetype s_orcattack1; +extern statetype s_orcattack2; +extern statetype s_orcattack3; + +extern statetype s_orcouch; + +extern statetype s_orcdie1; +extern statetype s_orcdie2; +extern statetype s_orcdie3; + + +extern statetype s_demonouch; +extern statetype s_demondie1; + +extern statetype s_mageouch; +extern statetype s_magedie1; + +extern statetype s_grelouch; +extern statetype s_greldie1; + +extern statetype s_batdie1; diff --git a/C3_DRAW.C b/C3_DRAW.C new file mode 100644 index 0000000..dfb055e --- /dev/null +++ b/C3_DRAW.C @@ -0,0 +1,1592 @@ +/* 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. + */ + +// C3_DRAW.C + +#include "C3_DEF.H" +#pragma hdrstop + +//#define DRAWEACH // draw walls one at a time for debugging + +unsigned highest; +unsigned mostwalls,numwalls; + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define PI 3.141592657 +#define ANGLEQUAD (ANGLES/4) + +unsigned oldend; + +#define FINEANGLES 3600 + +#define MINRATIO 16 + + +const unsigned MAXSCALEHEIGHT = (VIEWWIDTH/2); +const unsigned MAXVISHEIGHT = (VIEWHEIGHT/2); +const unsigned BASESCALE = 32; + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +// +// calculate location of screens in video memory so they have the +// maximum possible distance seperating them (for scaling overflow) +// + +unsigned screenloc[3]= {0x900,0x2000,0x3700}; +unsigned freelatch = 0x4e00; + +boolean fizzlein; + +long scaleshapecalll; +long scaletablecall; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +long bytecount,endcount; // for profiling +int animframe; +int pixelangle[VIEWWIDTH]; +int far finetangent[FINEANGLES+1]; +int fineviewangle; +unsigned viewxpix,viewypix; + +/* +============================================================================ + + 3 - D DEFINITIONS + +============================================================================ +*/ + +fixed tileglobal = TILEGLOBAL; +fixed focallength = FOCALLENGTH; +fixed mindist = MINDIST; +int viewheight = VIEWHEIGHT; +fixed scale; + + +tilept tile,lasttile, // tile of wall being followed + focal, // focal point in tiles + left,mid,right; // rightmost tile in view + +globpt edge,view; + +int segstart[VIEWHEIGHT], // addline tracks line segment and draws + segend[VIEWHEIGHT], + segcolor[VIEWHEIGHT]; // only when the color changes + + +walltype walls[MAXWALLS],*leftwall,*rightwall; + + +//========================================================================== + +// +// refresh stuff +// + +int screenpage; + +long lasttimecount; + +// +// rendering stuff +// + +int firstangle,lastangle; + +fixed prestep; + +fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4); + +fixed viewx,viewy; // the focal point +int viewangle; +fixed viewsin,viewcos; + +int zbuffer[VIEWXH+1]; // holds the height of the wall at that point + +//========================================================================== + +void DrawLine (int xl, int xh, int y,int color); +void DrawWall (walltype *wallptr); +void TraceRay (unsigned angle); +fixed FixedByFrac (fixed a, fixed b); +fixed FixedAdd (void); +fixed TransformX (fixed gx, fixed gy); +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max); +int BackTrace (int finish); +void ForwardTrace (void); +int TurnClockwise (void); +int TurnCounterClockwise (void); +void FollowWall (void); + +void NewScene (void); +void BuildTables (void); + +//========================================================================== + + +/* +================== += += DrawLine += += Must be in write mode 2 with all planes enabled += The bit mask is left set to the end value, so clear it after all lines are += drawn += += draws a black dot at the left edge of the line += +================== +*/ + +unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1}; +unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1}; +unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void DrawLine (int xl, int xh, int y,int color) +{ + unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid; + + xlb=xl/8; + xhb=xh/8; + + if (xhVIEWYH) + Quit("DrawLine: y>VIEWYH"); + + xlp = xl&7; + maskleft = leftmask[xlp]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + // + // set the GC index register to point to the bit mask register + // + asm mov al,GC_BITMASK + asm mov dx,GC_INDEX + asm out dx,al + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + asm mov dx,GC_INDEX+1 + + asm mov al,[BYTE PTR maskleft] + asm out dx,al // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + return; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX+1 +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,[BYTE PTR maskleft] +asm out dx,al // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels +asm inc di + +// +// draw middle +// +asm mov al,255 +asm out dx,al // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,[BYTE PTR maskright] +asm out dx,al // mask off pixels +asm xchg bh,[es:di] // load latches and write pixels + +} + +//========================================================================== + +void DrawLineDot (int xl, int xh, int y,int color) +{ + unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid; + + xlb=xl/8; + xhb=xh/8; + + if (xhVIEWYH) + Quit("DrawLine: y>VIEWYH"); + + xlp = xl&7; + maskdot = dotmask[xlp]; + maskleft = leftmask[xlp]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + // + // set the GC index register to point to the bit mask register + // + asm mov al,GC_BITMASK + asm mov dx,GC_INDEX + asm out dx,al + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + asm mov dx,GC_INDEX+1 + + asm mov al,[BYTE PTR maskleft] + asm out dx,al // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + + // + // write the black dot at the start + // + asm mov al,[BYTE PTR maskdot] + asm out dx,al // mask off pixels + + asm xor al,al + asm xchg al,[es:di] // load latches and write pixels + + + return; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX+1 +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,[BYTE PTR maskleft] +asm out dx,al // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels + +// +// write the black dot at the start +// +asm mov al,[BYTE PTR maskdot] +asm out dx,al // mask off pixels +asm xor al,al +asm xchg al,[es:di] // load latches and write pixels +asm inc di + +// +// draw middle +// +asm mov al,255 +asm out dx,al // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,[BYTE PTR maskright] +asm out dx,al // mask off pixels +asm xchg bh,[es:di] // load latches and write pixels + +} + +//========================================================================== + + +long wallscalesource; + +#ifdef DRAWEACH +/* +==================== += += ScaleOneWall += +==================== +*/ + +void near ScaleOneWall (int xl, int xh) +{ + int x,pixwidth,height; + + *(((unsigned *)&wallscalesource)+1) = wallseg[xl]; + + for (x=xl;x<=xh;x+=pixwidth) + { + height = wallheight[x]; + pixwidth = wallwidth[x]; + (unsigned)wallscalesource = wallofs[x]; + + *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height]; + (unsigned)scaletablecall = scaledirectory[height]->codeofs[0]; + + // + // scale a byte wide strip of wall + // + asm mov bx,[x] + asm mov di,bx + asm shr di,1 + asm shr di,1 + asm shr di,1 // X in bytes + asm add di,[bufferofs] + asm and bx,7 + asm shl bx,1 + asm shl bx,1 + asm shl bx,1 + asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1 + asm dec bx + asm mov al,BYTE PTR [bitmasks1+bx] + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + asm mov es,[screenseg] + asm lds si,[wallscalesource] + asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels + + asm mov al,BYTE PTR [ss:bitmasks2+bx] + asm or al,al + asm jz nosecond + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm out dx,al // set bit mask register + asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels + nosecond: + asm mov ax,ss + asm mov ds,ax + } +} + +#endif + +int walllight1[NUMFLOORS] = {0, + WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC, + WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC, + EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC, + RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC, + YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC, + GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC, + BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC}; + +int walldark1[NUMFLOORS] = {0, + WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC, + WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC, + EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC, + RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC, + YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC, + GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC, + BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC}; + +int walllight2[NUMFLOORS] = {0, + WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC, + WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC, + EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC, + RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC, + YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC, + GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC, + BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC}; + +int walldark2[NUMFLOORS] = {0, + WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC, + WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC, + EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC, + RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC, + YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC, + GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC, + BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC}; + +/* +===================== += += DrawVWall += += Draws a wall by vertical segments, for texture mapping! += += wallptr->side is true for east/west walls (constant x) += += fracheight and fracstep are 16.16 bit fractions += +===================== +*/ + +void DrawVWall (walltype *wallptr) +{ + int x,i; + unsigned source; + unsigned width,sourceint; + unsigned wallpic,wallpicseg; + unsigned skip; + long fracheight,fracstep,longheightchange; + unsigned height; + int heightchange; + unsigned slope,distance; + int traceangle,angle; + int mapadd; + unsigned lastpix,lastsource,lastwidth; + + + if (wallptr->rightclip < wallptr->leftclip) + Quit ("DrawVWall: Right < Left"); + +// +// setup for height calculation +// + wallptr->height1 >>= 1; + wallptr->height2 >>= 1; + wallptr->planecoord>>=10; // remove non significant bits + + width = wallptr->x2 - wallptr->x1; + if (width) + { + heightchange = wallptr->height2 - wallptr->height1; + asm mov ax,[heightchange] + asm mov WORD PTR [longheightchange+2],ax + asm mov WORD PTR [longheightchange],0 // avoid long shift by 16 + fracstep = longheightchange/width; + } + + fracheight = ((long)wallptr->height1<<16)+0x8000; + skip = wallptr->leftclip - wallptr->x1; + if (skip) + fracheight += fracstep*skip; + +// +// setup for texture mapping +// +// mapadd is 64*64 (to keep source positive) + the origin wall intercept +// distance has 6 unit bits, and 6 frac bits +// traceangle is the center view angle in FINEANGLES, moved to be in +// the +-90 degree range (to thew right of origin) +// + traceangle = fineviewangle; + // + // find wall picture to map from + // + if (wallptr->side) + { // east or west wall + if (animframe) + wallpic = walllight2[wallptr->color]; + else + wallpic = walllight1[wallptr->color]; + + if (wallptr->planecoord < viewxpix) + { + distance = viewxpix-wallptr->planecoord; + traceangle -= FINEANGLES/2; + mapadd = (64-viewypix&63); // the pixel spot of the origin + } + else + { + distance = wallptr->planecoord-viewxpix; + // traceangle is correct + mapadd = viewypix&63; // the pixel spot of the origin + } + } + else + { // north or south wall + if (animframe) + wallpic = walldark2[wallptr->color]; + else + wallpic = walldark1[wallptr->color]; + + if (wallptr->planecoord < viewypix) + { + distance = viewypix-wallptr->planecoord; + traceangle -= FINEANGLES/4; + mapadd = viewxpix&63; // the pixel spot of the origin + } + else + { + distance = wallptr->planecoord-viewypix; + traceangle -= FINEANGLES*3/4; + mapadd = (64-viewxpix&63); // the pixel spot of the origin + } + } + + mapadd = 64*64-mapadd; // make sure it stays positive + + wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC]; + if (traceangle > FINEANGLES/2) + traceangle -= FINEANGLES; + +// +// calculate everything +// +// IMPORTANT! This loop is executed around 5000 times / second! +// + lastpix = lastsource = (unsigned)-1; + + for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++) + { + // + // height + // + asm mov ax,WORD PTR [fracheight] + asm mov dx,WORD PTR [fracheight+2] + asm mov cx,dx + asm add ax,WORD PTR [fracstep] + asm adc dx,WORD PTR [fracstep+2] + asm mov WORD PTR [fracheight],ax + asm mov WORD PTR [fracheight+2],dx + asm mov bx,[x] + asm shl bx,1 + asm cmp cx,MAXSCALEHEIGHT + asm jbe storeheight + asm mov cx,MAXSCALEHEIGHT +storeheight: + asm mov WORD PTR [wallheight+bx],cx + asm mov WORD PTR [zbuffer+bx],cx + +// height = fracheight>>16; +// fracheight += fracstep; +// if (height > MAXSCALEHEIGHT) +// height = MAXSCALEHEIGHT; +// wallheight[x] = zbuffer[x] = height; + + // + // texture map + // + angle = pixelangle[x]+traceangle; + if (angle<0) + angle+=FINEANGLES; + + slope = finetangent[angle]; + +// +// distance is an unsigned 6.6 bit number (12 pixel bits) +// slope is a signed 5.10 bit number +// result is a signed 11.16 bit number +// + +#if 0 + source = distance*slope; + source >>=20; + + source += mapadd; + source &= 63; // mask off the unused units + source = 63-source; + source <<= 6; // multiply by 64 for offset into pic +#endif + asm mov ax,[distance] + asm imul [slope] // ax is the source pixel + asm mov al,ah + asm shr al,1 + asm shr al,1 // low 6 bits is now pixel number + asm add ax,[mapadd] + asm and ax,63 + asm mov dx,63 + asm sub dx,ax // otherwise it is backwards + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 // *64 to index into shape + asm mov [source],dx + + if (source != lastsource) + { + if (lastpix != (unsigned)-1) + { + wallofs[lastpix] = lastsource; + wallseg[lastpix] = wallpicseg; + wallwidth[lastpix] = lastwidth; + } + lastpix = x; + lastsource = source; + lastwidth = 1; + } + else + lastwidth++; // optimized draw, same map as last one + } + wallofs[lastpix] = lastsource; + wallseg[lastpix] = wallpicseg; + wallwidth[lastpix] = lastwidth; +} + + +//========================================================================== + + +/* +================= += += TraceRay += += Used to find the left and rightmost tile in the view area to be traced from += Follows a ray of the given angle from viewx,viewy in the global map until += it hits a solid tile += sets: += tile.x,tile.y : tile coordinates of contacted tile += tilecolor : solid tile's color += +================== +*/ + +int tilecolor; + +void TraceRay (unsigned angle) +{ + long tracex,tracey,tracexstep,traceystep,searchx,searchy; + fixed fixtemp; + int otx,oty,searchsteps; + + tracexstep = costable[angle]; + traceystep = sintable[angle]; + +// +// advance point so it is even with the view plane before we start checking +// + fixtemp = FixedByFrac(prestep,tracexstep); + tracex = viewx+fixtemp; + fixtemp = FixedByFrac(prestep,traceystep); + tracey = viewy-fixtemp; + + tile.x = tracex>>TILESHIFT; // starting point in tiles + tile.y = tracey>>TILESHIFT; + + + if (tracexstep<0) // use 2's complement, not signed magnitude + tracexstep = -(tracexstep&0x7fffffff); + + if (traceystep<0) // use 2's complement, not signed magnitude + traceystep = -(traceystep&0x7fffffff); + +// +// we assume viewx,viewy is not inside a solid tile, so go ahead one step +// + + do // until a solid tile is hit + { + otx = tile.x; + oty = tile.y; + spotvis[otx][oty] = true; + tracex += tracexstep; + tracey -= traceystep; + tile.x = tracex>>TILESHIFT; + tile.y = tracey>>TILESHIFT; + + if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) ) + { + // + // trace crossed two solid tiles, so do a binary search along the line + // to find a spot where only one tile edge is crossed + // + searchsteps = 0; + searchx = tracexstep; + searchy = traceystep; + do + { + searchx/=2; + searchy/=2; + if (tile.x!=otx && tile.y!=oty) + { + // still too far + tracex -= searchx; + tracey += searchy; + } + else + { + // not far enough, no tiles crossed + tracex += searchx; + tracey -= searchy; + } + + // + // if it is REAL close, go for the most clockwise intersection + // + if (++searchsteps == 16) + { + tracex = (long)otx<0) + { + if (traceystep<0) + { + tracex += TILEGLOBAL-1; + tracey += TILEGLOBAL; + } + else + { + tracex += TILEGLOBAL; + } + } + else + { + if (traceystep<0) + { + tracex --; + tracey += TILEGLOBAL-1; + } + else + { + tracey --; + } + } + } + + tile.x = tracex>>TILESHIFT; + tile.y = tracey>>TILESHIFT; + + } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) ); + } + } while (!(tilecolor = tilemap[tile.x][tile.y]) ); + +} + +//========================================================================== + + +/* +======================== += += FixedByFrac += += multiply a 16/16 bit, 2's complement fixed point number by a 16 bit += fraction, passed as a signed magnitude 32 bit number += +======================== +*/ + +#pragma warn -rvl // I stick the return value in with ASMs + +fixed FixedByFrac (fixed a, fixed b) +{ + fixed value; + +// +// setup +// +asm mov si,[WORD PTR b+2] // sign of result = sign of fraction + +asm mov ax,[WORD PTR a] +asm mov cx,[WORD PTR a+2] + +asm or cx,cx +asm jns aok: // negative? +asm not ax +asm not cx +asm add ax,1 +asm adc cx,0 +asm xor si,0x8000 // toggle sign of result +aok: + +// +// multiply cx:ax by bx +// +asm mov bx,[WORD PTR b] +asm mul bx // fraction*fraction +asm mov di,dx // di is low word of result +asm mov ax,cx // +asm mul bx // units*fraction +asm add ax,di +asm adc dx,0 + +// +// put result dx:ax in 2's complement +// +asm test si,0x8000 // is the result negative? +asm jz ansok: +asm not ax +asm not dx +asm add ax,1 +asm adc dx,0 + +ansok:; + +} + +#pragma warn +rvl + +//========================================================================== + + +/* +======================== += += TransformPoint += += Takes paramaters: += gx,gy : globalx/globaly of point += += globals: += viewx,viewy : point of view += viewcos,viewsin : sin/cos of viewangle += += += defines: += CENTERX : pixel location of center of view window += TILEGLOBAL : size of one += FOCALLENGTH : distance behind viewx/y for center of projection += scale : conversion from global value to screen value += += returns: += screenx,screenheight: projected edge location and size += +======================== +*/ + +void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight) +{ + int ratio; + fixed gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = gx-viewx; + gy = gy-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt; + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + if (nx<0) + nx = 0; + + ratio = nx*scale/FOCALLENGTH; + + if (ratio<=MINRATIO) + ratio = MINRATIO; + + *screenx = CENTERX + ny/ratio; + + *screenheight = TILEGLOBAL/ratio; + +} + + +// +// transform actor +// +void TransformActor (objtype *ob) +{ + int ratio; + fixed gx,gy,gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = ob->x-viewx; + gy = ob->y-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt-ob->size; + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + if (nx<0) + nx = 0; + + ratio = nx*scale/FOCALLENGTH; + + if (ratio<=MINRATIO) + ratio = MINRATIO; + + ob->viewx = CENTERX + ny/ratio; + + ob->viewheight = TILEGLOBAL/ratio; +} + +//========================================================================== + +fixed TransformX (fixed gx, fixed gy) +{ + int ratio; + fixed gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = gx-viewx; + gy = gy-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + + return gxt-gyt; +} + +//========================================================================== + +/* +================== += += BuildTables += += Calculates: += += scale projection constant += sintable/costable overlapping fractional tables += firstangle/lastangle angles from focalpoint to left/right view edges += prestep distance from focal point before checking for tiles += +================== +*/ + +void BuildTables (void) +{ + int i; + long intang; + long x; + float angle,anglestep,radtoint; + double tang; + fixed value; + +// +// calculate the angle offset from view angle of each pixel's ray +// + radtoint = (float)FINEANGLES/2/PI; + for (i=0;iMAXINT) + intang = 0x8f00 | (intang & 0xff); + else if (intangx1 = VIEWXH+1; + rightwall->height1 = 32000; + (rightwall+1)->x1 = 32000; + + leftx = -1; + + for (wall=&walls[1];wall= wall->x2) + continue; + + rightclip = wall->x2; + + check = wall+1; + while (check->x1 <= rightclip && check->height1 >= wall->height2) + { + rightclip = check->x1-1; + check++; + } + + if (rightclip>VIEWXH) + rightclip=VIEWXH; + + if (leftx < wall->x1 - 1) + newleft = wall->x1-1; // there was black space between walls + else + newleft = leftx; + + if (rightclip > newleft) + { + wall->leftclip = newleft+1; + wall->rightclip = rightclip; + DrawVWall (wall); + leftx = rightclip; + } + } + +#ifndef DRAWEACH + ScaleWalls (); // draw all the walls +#endif +} + +//========================================================================== + +/* +===================== += += DrawScaleds += += Draws all objects that are visable += +===================== +*/ + +objtype *depthsort[MAXACTORS]; + +void DrawScaleds (void) +{ + int i,j,least,numvisable,height; + objtype *obj,**vislist,*farthest; + memptr shape; + byte *tilespot,*visspot; + + numvisable = 0; + +// +// calculate base positions of all objects +// + vislist = &depthsort[0]; + + for (obj = player->next;obj;obj=obj->next) + { + if (!obj->state->shapenum) + continue; + + tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley; + visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley; + // + // could be in any of the nine surrounding tiles + // + if (*visspot + || ( *(visspot-1) && !*(tilespot-1) ) + || ( *(visspot+1) && !*(tilespot+1) ) + || ( *(visspot-65) && !*(tilespot-65) ) + || ( *(visspot-64) && !*(tilespot-64) ) + || ( *(visspot-63) && !*(tilespot-63) ) + || ( *(visspot+65) && !*(tilespot+65) ) + || ( *(visspot+64) && !*(tilespot+64) ) + || ( *(visspot+63) && !*(tilespot+63) ) ) + { + obj->active = true; + TransformActor (obj); + if (!obj->viewheight || obj->viewheight > VIEWWIDTH) + continue; // too close or far away + + *vislist++ = obj; + numvisable++; + } + } + + if (vislist == &depthsort[0]) + return; // no visable objects + +// +// draw from back to front +// + for (i = 0; iviewheight; + if (height < least) + { + least = height; + farthest = depthsort[j]; + } + } + // + // draw farthest + // + shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC]; + ScaleShape(farthest->viewx,shape,farthest->viewheight); + farthest->viewheight = 32000; + } +} + +//========================================================================== + + +/* +===================== += += CalcTics += +===================== +*/ + +void CalcTics (void) +{ + long newtime,oldtimecount; + + +#ifdef PROFILE + tics = 1; + return; +#endif + +// +// 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; + } + } +} + + +//========================================================================== + + +/* +======================== += += DrawHand += +======================== +*/ + +void DrawHand (void) +{ + int picnum; + memptr source; + unsigned dest,width,height; + + picnum = HAND1PICM; + if (gamestate.shotpower || boltsleft) + picnum += (((unsigned)TimeCount>>3)&1); + + source = grsegs[picnum]; + dest = ylookup[VIEWHEIGHT-handheight]+12+bufferofs; + width = picmtable[picnum-STARTPICM].width; + height = picmtable[picnum-STARTPICM].height; + + VW_MaskBlock(source,0,dest,width,handheight,width*height); + EGAMAPMASK(15); +} + +//========================================================================== + + +/* +======================== += += ThreeDRefresh += +======================== +*/ + +void ThreeDRefresh (void) +{ + int tracedir; + +restart: + aborttrace = false; + +// +// clear out the traced array +// +asm mov ax,ds +asm mov es,ax +asm mov di,OFFSET spotvis +asm xor ax,ax +asm mov cx,[mapwidth] // mapheight*32 words +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm rep stosw + + +// +// set up variables for this view +// + + viewangle = player->angle; + fineviewangle = viewangle*(FINEANGLES/ANGLES); + viewsin = sintable[viewangle]; + viewcos = costable[viewangle]; + viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos); + viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin); + viewx &= 0xfffffc00; // stop on a pixel boundary + viewy &= 0xfffffc00; + viewx += 0x180; + viewy += 0x180; + viewxpix = viewx>>10; + viewypix = viewy>>10; + + focal.x = viewx>>TILESHIFT; + focal.y = viewy>>TILESHIFT; + +// +// find the rightmost visable tile in view +// + tracedir = viewangle + lastangle; + if (tracedir<0) + tracedir+=ANGLES; + else if (tracedir>=ANGLES) + tracedir-=ANGLES; + TraceRay( tracedir ); + right.x = tile.x; + right.y = tile.y; + +// +// find the leftmost visable tile in view +// + tracedir = viewangle + firstangle; + if (tracedir<0) + tracedir+=ANGLES; + else if (tracedir>=ANGLES) + tracedir-=ANGLES; + TraceRay( tracedir ); + +// +// follow the walls from there to the right +// + rightwall = &walls[1]; + FollowWalls (); + + if (aborttrace) + goto restart; + +// +// actually draw stuff +// + if (++screenpage == 3) + screenpage = 0; + + bufferofs = screenloc[screenpage]; + + EGAWRITEMODE(2); + EGAMAPMASK(15); + +// +// draw the wall list saved be FollowWalls () +// + animframe = (TimeCount&8)>>3; + +// +// draw all the scaled images +// + asm mov dx,GC_INDEX + + asm mov ax,GC_COLORDONTCARE + asm out dx,ax // don't look at any of the planes + + asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2 + asm out dx,ax + + asm mov al,GC_BITMASK + asm out dx,al + + DrawWallList(); + DrawScaleds(); + + EGAWRITEMODE(0); + EGABITMASK(0xff); + +// +// draw hand +// + if (handheight) + DrawHand (); + +// +// show screen and time last cycle +// + if (fizzlein) + { + fizzlein = false; + FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true); + lasttimecount = TimeCount; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + +asm cli +asm mov cx,[bufferofs] +asm mov dx,3d4h // CRTC address register +asm mov al,0ch // start address high register +asm out dx,al +asm inc dx +asm mov al,ch +asm out dx,al // set the high byte +asm dec dx +asm mov al,0dh // start address low register +asm out dx,al +asm inc dx +asm mov al,cl +asm out dx,al // set the low byte +asm sti + + displayofs = bufferofs; + + CalcTics (); + +} + diff --git a/C3_GAME.C b/C3_GAME.C new file mode 100644 index 0000000..a71d254 --- /dev/null +++ b/C3_GAME.C @@ -0,0 +1,1199 @@ +/* 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. + */ + +// C3_GAME.C + +#include "C3_DEF.H" +#pragma hdrstop + +#ifdef PROFILE +#include "TIME.H" +#endif + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define NUMLUMPS 25 + +#define CONTROLSLUMP 0 +#define ORCLUMP 1 +#define TROLLLUMP 2 +#define WARPLUMP 3 +#define BOLTLUMP 4 +#define NUKELUMP 5 +#define POTIONLUMP 6 +#define RKEYLUMP 7 +#define YKEYLUMP 8 +#define GKEYLUMP 9 +#define BKEYLUMP 10 +#define SCROLLLUMP 11 +#define CHESTLUMP 12 +#define PLAYERLUMP 13 +#define WALL1LUMP 14 +#define WALL2LUMP 15 +#define BDOORLUMP 16 +#define DEMONLUMP 17 +#define MAGELUMP 18 +#define BATLUMP 19 +#define GRELLUMP 20 +#define GOALLUMP 21 + + +int lumpstart[NUMLUMPS] = { +CONTROLS_LUMP_START, +ORC_LUMP_START, +TROLL_LUMP_START, +WARP_LUMP_START, +BOLT_LUMP_START, +NUKE_LUMP_START, +POTION_LUMP_START, +RKEY_LUMP_START, +YKEY_LUMP_START, +GKEY_LUMP_START, +BKEY_LUMP_START, +SCROLL_LUMP_START, +CHEST_LUMP_START, +PLAYER_LUMP_START, +WALL1_LUMP_START, +WALL2_LUMP_START, +BDOOR_LUMP_START, +DEMON_LUMP_START, +MAGE_LUMP_START, +BAT_LUMP_START, +GREL_LUMP_START, +NEMESISPIC +}; + + +int lumpend[NUMLUMPS] = { +CONTROLS_LUMP_END, +ORC_LUMP_END, +TROLL_LUMP_END, +WARP_LUMP_END, +BOLT_LUMP_END, +NUKE_LUMP_END, +POTION_LUMP_END, +RKEY_LUMP_END, +YKEY_LUMP_END, +GKEY_LUMP_END, +BKEY_LUMP_END, +SCROLL_LUMP_END, +CHEST_LUMP_END, +PLAYER_LUMP_END, +WALL1_LUMP_END, +WALL2_LUMP_END, +BDOOR_LUMP_END, +DEMON_LUMP_END, +MAGE_LUMP_END, +BAT_LUMP_END, +GREL_LUMP_END, +NEMESISPIC +}; + + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +unsigned latchpics[NUMLATCHPICS]; +unsigned tileoffsets[NUMTILE16]; +unsigned textstarts[27]; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +boolean lumpneeded[NUMLUMPS]; + + +//=========================================================================== + + +/* +========================== += += ScanInfoPlane += += Spawn all actors and mark down special places += +========================== +*/ + +void ScanInfoPlane (void) +{ + unsigned x,y,i,j; + int tile; + unsigned far *start; + + InitObjList(); // start spawning things with a clean slate + + memset (lumpneeded,0,sizeof(lumpneeded)); + + start = mapsegs[2]; + for (y=0;ywidth; + mapheight = mapheaderseg[mapon]->height; + +// +// make a lookup table for the maps left edge +// + spot = 0; + for (y=0;y=EXPWALLSTART && tile0) + (unsigned)actorat[x][y] = tile; + } + } + + +// +// decide which graphics are needed and spawn actors +// + ScanInfoPlane (); + +// +// have the caching manager load and purge stuff to make sure all marks +// are in memory +// + CA_LoadAllSounds (); + +} + + +//========================================================================== + +/* +===================== += += LatchDrawPic += +===================== +*/ + +void LatchDrawPic (unsigned x, unsigned y, unsigned picnum) +{ + unsigned wide, height, source, dest; + + wide = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + dest = bufferofs + ylookup[y]+x; + source = latchpics[picnum-FIRSTLATCHPIC]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,[wide] + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] +asm mov dx,[height] // scan lines to draw +asm mov ax,[wide] + +lineloop: +asm mov cx,ax +asm rep movsb +asm add di,bx + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + + +//========================================================================== + +/* +===================== += += Victory += +===================== +*/ + +void Victory (void) +{ + FreeUpMemory (); + NormalScreen (); + CA_CacheGrChunk (FINALEPIC); + VWB_DrawPic (0,0,FINALEPIC); + UNMARKGRCHUNK(FINALEPIC); + VW_UpdateScreen (); + SD_PlaySound (GETBOLTSND); + SD_WaitSoundDone (); + SD_PlaySound (GETNUKESND); + SD_WaitSoundDone (); + SD_PlaySound (GETPOTIONSND); + SD_WaitSoundDone (); + SD_PlaySound (GETKEYSND); + SD_WaitSoundDone (); + SD_PlaySound (GETSCROLLSND); + SD_WaitSoundDone (); + SD_PlaySound (GETPOINTSSND); + SD_WaitSoundDone (); + IN_ClearKeysDown (); + IN_Ack(); +} + +//========================================================================== + +/* +=================== += += Died += +=================== +*/ + +void Died (void) +{ + unsigned page1,page2; +// +// fizzle fade screen to grey +// + FreeUpMemory (); + SD_PlaySound (GAMEOVERSND); + bufferofs = screenloc[(screenpage+1)%3]; + LatchDrawPic(0,0,DEADPIC); + FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false); + IN_ClearKeysDown(); + IN_Ack(); + VW_SetScreen (bufferofs,0); +} + +//========================================================================== + +/* +=================== += += NormalScreen += +=================== +*/ + +void NormalScreen (void) +{ + VW_SetSplitScreen (200); + bufferofs = displayofs = SCREEN1START; + VW_Bar(0,0,320,200,0); + bufferofs = SCREEN2START; + VW_Bar(0,0,320,200,0); + VW_SetScreen (displayofs,0); +} + +//========================================================================== + +/* +=================== += += DrawPlayScreen += +=================== +*/ + +void DrawPlayScreen (void) +{ + int i,j,p,m; + + screenpage = 0; + + bufferofs = 0; + VW_Bar (0,0,320,STATUSLINES,7); + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_Bar (0,0,320,VIEWHEIGHT,7); + } + + + VW_SetSplitScreen(144); + VW_SetScreen(screenloc[0],0); + bufferofs = 0; + + CA_CacheGrChunk (STATUSPIC); + CA_CacheGrChunk (SIDEBARSPIC); + + VW_DrawPic (0,0,STATUSPIC); + + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_DrawPic (33,0,SIDEBARSPIC); + } + + grneeded[STATUSPIC]&= ~ca_levelbit; + grneeded[SIDEBARSPIC]&= ~ca_levelbit; + MM_SetPurge(&grsegs[STATUSPIC],3); + MM_SetPurge(&grsegs[SIDEBARSPIC],3); + + RedrawStatusWindow (); + bufferofs = displayofs = screenloc[0]; +} + + +//========================================================================== + +/* +=================== += += LoadLatchMem += +=================== +*/ + +void LoadLatchMem (void) +{ + int i,j,p,m; + byte far *src, far *dest; + unsigned destoff; + + EGAWRITEMODE(0); + +// +// draw some pics into latch memory +// + +// +// tile 8s +// + latchpics[0] = freelatch; + src = (byte _seg *)grsegs[STARTTILE8]; + dest = MK_FP(0xa000,freelatch); + + for (i=0;iwidth || y>height) + continue; + drawofs = source+ylookup[y]; + + asm mov cx,[x] + asm mov si,cx + asm and si,7 + asm mov dx,GC_INDEX + asm mov al,GC_BITMASK + asm mov ah,BYTE PTR [maskb+si] + asm out dx,ax + + asm mov si,[drawofs] + asm shr cx,1 + asm shr cx,1 + asm shr cx,1 + asm add si,cx + asm mov di,si + asm add di,[pagedelta] + + asm mov dx,GC_INDEX + asm mov al,GC_READMAP // leave GC_INDEX set to READMAP + asm out dx,al + + asm mov dx,SC_INDEX+1 + asm mov al,1 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,0 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,2 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,1 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,4 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,2 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,8 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,3 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + if (rndval == 1) // entire sequence has been completed + { + EGABITMASK(255); + EGAMAPMASK(15); + return; + }; + } + frame++; + while (TimeCountname); + + // + // level + // + ultoa(s->completed,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (25 * 8) - 8 - w; + US_Print(buffer); + + // + // score + // + ultoa(s->score,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (34 * 8) - 8 - w; + US_Print(buffer); + } + + fontcolor = F_BLACK; +} + + + +/* +======================= += += CheckHighScore += +======================= +*/ + +void CheckHighScore (long score,word other) +{ + word i,j; + int n; + HighScore myscore; + + strcpy(myscore.name,""); + myscore.score = score; + myscore.completed = other; + + for (i = 0,n = -1;i < MaxScores;i++) + { + if + ( + (myscore.score > Scores[i].score) + || ( + (myscore.score == Scores[i].score) + && (myscore.completed > Scores[i].completed) + ) + ) + { + for (j = MaxScores;--j > i;) + Scores[j] = Scores[j - 1]; + Scores[i] = myscore; + n = i; + HighScoresDirty = true; + break; + } + } + + if (n != -1) + { + // + // got a high score + // + DrawHighScores (); + PrintY = 68 + (16 * n); + PrintX = 60; + US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100); + } +} + + +//========================================================================== + +/* +=================== += += GameLoop += +=================== +*/ + +void GameLoop (void) +{ + int i,xl,yl,xh,yh; + char num[20]; +#ifdef PROFILE + clock_t start,end; +#endif + + DrawPlayScreen (); + +restart: + if (!loadedgame) + { + gamestate.difficulty = restartgame; + restartgame = gd_Continue; + DrawEnterScreen (); + } + + do + { + playstate = gd_Continue; + if (!loadedgame) + SetupGameLevel (); + else + loadedgame = false; + + CacheScaleds (); + +#ifdef PROFILE +start = clock(); +while (start == clock()); +start++; +#endif + PlayLoop (); +#ifdef PROFILE +end = clock(); +itoa(end-start,str,10); + Quit (str); +#endif + + + switch (playstate) + { + case ex_died: + Died (); + NormalScreen (); + FreeUpMemory (); + CheckHighScore (gamestate.score,gamestate.mapon+1); + return; + case ex_warped: + FizzleOut (true); + if (gamestate.mapon >= NUMLEVELS) + { + Victory (); + FreeUpMemory (); + CheckHighScore(gamestate.score,gamestate.mapon+1); + return; + } + break; + case ex_abort: + FreeUpMemory (); + return; + case ex_resetgame: + case ex_loadedgame: + goto restart; + case ex_victorious: + Victory (); + FreeUpMemory(); + CheckHighScore(gamestate.score,gamestate.mapon+1); + return; + } + + } while (1); + +} diff --git a/C3_MAIN.C b/C3_MAIN.C new file mode 100644 index 0000000..684cb32 --- /dev/null +++ b/C3_MAIN.C @@ -0,0 +1,756 @@ +/* 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. + */ + +// C3_MAIN.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + CATACOMB 3-D + + An Id Software production + + by John Carmack + +============================================================================= +*/ + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +memptr scalesegs[NUMPICS]; +char str[80],str2[20]; +unsigned tedlevelnum; +boolean tedlevel; +gametype gamestate; +exittype playstate; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + +//=========================================================================== + +// JAB Hack begin +#define MyInterrupt 0x60 +void interrupt (*intaddr)(); +void interrupt (*oldintaddr)(); + char *JHParmStrings[] = {"no386",nil}; + +void +jabhack(void) +{ +extern void far jabhack2(void); +extern int far CheckIs386(void); + + int i; + + oldintaddr = getvect(MyInterrupt); + + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],JHParmStrings) == 0) + return; + + if (CheckIs386()) + { + jabhack2(); + setvect(MyInterrupt,intaddr); + } +} + +void +jabunhack(void) +{ + setvect(MyInterrupt,oldintaddr); +} +// JAB Hack end + +//=========================================================================== + +/* +===================== += += NewGame += += Set up new game to start from the beginning += +===================== +*/ + +void NewGame (void) +{ + memset (&gamestate,0,sizeof(gamestate)); + gamestate.mapon = 0; + gamestate.body = MAXBODY; +} + +//=========================================================================== + +#define RLETAG 0xABCD + +/* +================== += += SaveTheGame += +================== +*/ + +boolean SaveTheGame(int file) +{ + word i,compressed,expanded; + objtype *o; + memptr bigbuffer; + + if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i+=2) // Write planes 0 and 2 + { +// +// leave a word at start of compressed data for compressed length +// + compressed = (unsigned)CA_RLEWCompress ((unsigned huge *)mapsegs[i] + ,expanded,((unsigned huge *)bigbuffer)+1,RLETAG); + + *(unsigned huge *)bigbuffer = compressed; + + if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + } + + for (o = player;o;o = o->next) + if (!CA_FarWrite(file,(void far *)o,sizeof(objtype))) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + MM_FreePtr (&bigbuffer); + + return(true); +} + +//=========================================================================== + +/* +================== += += LoadTheGame += +================== +*/ + +boolean LoadTheGame(int file) +{ + unsigned i,x,y; + objtype *obj,*prev,*next,*followed; + unsigned compressed,expanded; + unsigned far *map,tile; + memptr bigbuffer; + + if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + SetupGameLevel (); // load in and cache the base old level + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i+=2) // Read planes 0 and 2 + { + if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + if (!CA_FarRead(file,(void far *)bigbuffer,compressed) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + CA_RLEWexpand ((unsigned huge *)bigbuffer, + (unsigned huge *)mapsegs[i],expanded,RLETAG); + } + + MM_FreePtr (&bigbuffer); +// +// copy the wall data to a data segment array again, to handle doors and +// bomb walls that are allready opened +// + memset (tilemap,0,sizeof(tilemap)); + memset (actorat,0,sizeof(actorat)); + map = mapsegs[0]; + for (y=0;y0) + (unsigned)actorat[x][y] = tile; + } + } + + + // Read the object list back in - assumes at least one object in list + + InitObjList (); + new = player; + while (true) + { + prev = new->prev; + next = new->next; + if (!CA_FarRead(file,(void far *)new,sizeof(objtype))) + return(false); + followed = new->next; + new->prev = prev; + new->next = next; + actorat[new->tilex][new->tiley] = new; // drop a new marker + + if (followed) + GetNewObj (false); + else + break; + } + + return(true); +} + +//=========================================================================== + +/* +================== += += ResetGame += +================== +*/ + +void ResetGame(void) +{ + NewGame (); + + ca_levelnum--; + ca_levelbit>>=1; + CA_ClearMarks(); + ca_levelbit<<=1; + ca_levelnum++; +} + +//=========================================================================== + + +/* +========================== += += ShutdownId += += Shuts down all ID_?? managers += +========================== +*/ + +void ShutdownId (void) +{ + US_Shutdown (); +#ifndef PROFILE + SD_Shutdown (); + IN_Shutdown (); +#endif + VW_Shutdown (); + CA_Shutdown (); + MM_Shutdown (); +} + + +//=========================================================================== + +/* +========================== += += InitGame += += Load a few things right away += +========================== +*/ + +void InitGame (void) +{ + unsigned segstart,seglength; + int i,x,y; + unsigned *blockstart; + +// US_TextScreen(); + + MM_Startup (); + VW_Startup (); +#ifndef PROFILE + IN_Startup (); + SD_Startup (); +#endif + US_Startup (); + +// US_UpdateTextScreen(); + + CA_Startup (); + US_Setup (); + + US_SetLoadSaveHooks(LoadTheGame,SaveTheGame,ResetGame); + +// +// load in and lock down some basic chunks +// + + CA_ClearMarks (); + + CA_MarkGrChunk(STARTFONT); + CA_MarkGrChunk(STARTTILE8); + CA_MarkGrChunk(STARTTILE8M); + CA_MarkGrChunk(HAND1PICM); + CA_MarkGrChunk(HAND2PICM); + CA_MarkGrChunk(ENTERPLAQUEPIC); + + CA_CacheMarks (NULL); + + MM_SetLock (&grsegs[STARTFONT],true); + MM_SetLock (&grsegs[STARTTILE8],true); + MM_SetLock (&grsegs[STARTTILE8M],true); + MM_SetLock (&grsegs[HAND1PICM],true); + MM_SetLock (&grsegs[HAND2PICM],true); + MM_SetLock (&grsegs[ENTERPLAQUEPIC],true); + + fontcolor = WHITE; + + +// +// build some tables +// + for (i=0;i= MINMEMORY) + return; + + CA_CacheGrChunk (OUTOFMEM); + finscreen = (unsigned)grsegs[OUTOFMEM]; + ShutdownId (); + movedata (finscreen,7,0xb800,0,4000); + gotoxy (1,24); + exit(1); +} + +//=========================================================================== + + +/* +========================== += += main += +========================== +*/ + +void main (void) +{ + short i; + + if (stricmp(_argv[1], "/VER") == 0) + { + printf("Catacomb 3-D version 1.22 (Rev 1)\n"); + printf("Copyright 1991-93 Softdisk Publishing\n"); + printf("Developed for use with 100%% IBM compatibles\n"); + printf("that have 640K memory and DOS version 3.3 or later\n"); + printf("and EGA graphics or better.\n"); + exit(0); + } + + if (stricmp(_argv[1], "/?") == 0) + { + printf("Catacomb 3-D version 1.22\n"); + printf("Copyright 1991-93 Softdisk Publishing\n\n"); + printf("Syntax:\n"); + printf("CAT3D [/]\n\n"); + printf("Switch What it does\n"); + printf("/? This Information\n"); + printf("/VER Display Program Version Information\n"); + printf("/COMP Fix problems with SVGA screens\n"); + printf("/NOAL No AdLib or SoundBlaster detection\n"); + printf("/NOJOYS Tell program to ignore joystick\n"); + printf("/NOMOUSE Tell program to ignore mouse\n"); + printf("/HIDDENCARD Overrides video detection\n\n"); + printf("Each switch must include a '/' and multiple switches\n"); + printf("must be seperated by at least one space.\n\n"); + + exit(0); + } + + jabhack(); + + InitGame (); + + CheckMemory (); + + LoadLatchMem (); + +#ifdef PROFILE + NewGame (); + GameLoop (); +#endif + +//NewGame (); +//GameLoop (); + + DemoLoop(); + Quit("Demo loop exited???"); +} diff --git a/C3_PLAY.C b/C3_PLAY.C new file mode 100644 index 0000000..112041f --- /dev/null +++ b/C3_PLAY.C @@ -0,0 +1,584 @@ +/* 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. + */ + +// C3_PLAY.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define POINTTICS 6 + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +ControlInfo c; +boolean running,slowturn; + +int bordertime; +objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist; + +unsigned farmapylookup[MAPSIZE]; +byte *nearmapylookup[MAPSIZE]; + +boolean singlestep,godmode; +int extravbls; + +// +// replacing refresh manager +// +unsigned mapwidth,mapheight,tics; +boolean compatability; +byte *updateptr; +unsigned mapwidthtable[64]; +unsigned uwidthtable[UPDATEHIGH]; +unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2) +#define UPDATESPARESIZE (UPDATEWIDE*2+4) +#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE) +byte update[UPDATESIZE]; + +int mousexmove,mouseymove; +int pointcount,pointsleft; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +void CalcBounds (objtype *ob); +void DrawPlayScreen (void); + + +// +// near data map array (wall values only, get text number from far data) +// +byte tilemap[MAPSIZE][MAPSIZE]; +byte spotvis[MAPSIZE][MAPSIZE]; +objtype *actorat[MAPSIZE][MAPSIZE]; + +objtype dummyobj; + +int bordertime; +int objectcount; + +void StopMusic(void); +void StartMusic(void); + + +//========================================================================== + +/////////////////////////////////////////////////////////////////////////// +// +// CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// + +#define MAXX 264 +#define MAXY 146 + +void CenterWindow(word w,word h) +{ + US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h); +} + +//=========================================================================== + + +/* +===================== += += CheckKeys += +===================== +*/ + +void CheckKeys (void) +{ + if (screenfaded) // don't do anything with a faded screen + return; + +// +// pause key wierdness can't be checked as a scan code +// + if (Paused) + { + CenterWindow (8,3); + US_PrintCentered ("PAUSED"); + VW_UpdateScreen (); + SD_MusicOff(); + IN_Ack(); + SD_MusicOn(); + Paused = false; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + +// +// F1-F7/ESC to enter control panel +// + if ( (LastScan >= sc_F1 && LastScan <= sc_F7) || LastScan == sc_Escape) + { + StopMusic (); + NormalScreen (); + FreeUpMemory (); + US_CenterWindow (20,8); + US_CPrint ("Loading"); + VW_UpdateScreen (); + US_ControlPanel(); + if (abortgame) + { + playstate = ex_abort; + return; + } + StartMusic (); + IN_ClearKeysDown(); + if (restartgame) + playstate = ex_resetgame; + if (loadedgame) + playstate = ex_loadedgame; + DrawPlayScreen (); + CacheScaleds (); + lasttimecount = TimeCount; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + +// +// F10-? debug keys +// + if (Keyboard[sc_F10]) + { + DebugKeys(); + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + lasttimecount = TimeCount; + } + +} + + +//=========================================================================== + +/* +############################################################################# + + The objlist data structure + +############################################################################# + +objlist containt structures for every actor currently playing. The structure +is accessed as a linked list starting at *player, ending when ob->next == +NULL. GetNewObj inserts a new object at the end of the list, meaning that +if an actor spawn another actor, the new one WILL get to think and react the +same frame. RemoveObj unlinks the given object and returns it to the free +list, but does not damage the objects ->next pointer, so if the current object +removes itself, a linked list following loop can still safely get to the +next element. + + + +############################################################################# +*/ + + +/* +========================= += += InitObjList += += Call to clear out the entire object list, returning them all to the free += list. Allocates a special spot for the player. += +========================= +*/ + +void InitObjList (void) +{ + int i; + + for (i=0;iprev; + memset (new,0,sizeof(*new)); + + if (lastobj) + lastobj->next = new; + new->prev = lastobj; // new->next is allready NULL from memset + + new->active = false; + lastobj = new; + + objectcount++; +} + +//=========================================================================== + +/* +========================= += += RemoveObj += += Add the given object back into the free list, and unlink it from it's += neighbors += +========================= +*/ + +void RemoveObj (objtype *gone) +{ + objtype **spotat; + + if (gone == player) + Quit ("RemoveObj: Tried to remove the player!"); + +// +// fix the next object's back link +// + if (gone == lastobj) + lastobj = (objtype *)gone->prev; + else + gone->next->prev = gone->prev; + +// +// fix the previous object's forward link +// + gone->prev->next = gone->next; + +// +// add it back in to the free list +// + gone->prev = objfreelist; + objfreelist = gone; +} + +//========================================================================== + +/* +=================== += += PollControls += +=================== +*/ + +void PollControls (void) +{ + unsigned buttons; + + IN_ReadControl(0,&c); + + if (MousePresent) + { + Mouse(MButtons); + buttons = _BX; + Mouse(MDelta); + mousexmove = _CX; + mouseymove = _DX; + + if (buttons&1) + c.button0 = 1; + if (buttons&2) + c.button1 = 1; + + } + + if (Controls[0]==ctrl_Joystick) + { + if (c.x>120 || c.x <-120 || c.y>120 || c.y<-120) + running = true; + else + running = false; + if (c.x>-48 && c.x<48) + slowturn = true; + else + slowturn = false; + } + else + { + if (Keyboard[sc_RShift]) + running = true; + else + running = false; + if (c.button0) + slowturn = true; + else + slowturn = false; + } +} + +//========================================================================== + +/* +================= += += StopMusic += +================= +*/ + +void StopMusic(void) +{ + int i; + + SD_MusicOff(); + for (i = 0;i < LASTMUSIC;i++) + if (audiosegs[STARTMUSIC + i]) + { + MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3); + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false); + } +} + +//========================================================================== + + +/* +================= += += StartMusic += +================= +*/ + +// JAB - Cache & start the appropriate music for this level +void StartMusic(void) +{ + musicnames chunk; + + SD_MusicOff(); + chunk = TOOHOT_MUS; +// if ((chunk == -1) || (MusicMode != smm_AdLib)) +//DEBUG control panel return; + + MM_BombOnError (false); + CA_CacheAudioChunk(STARTMUSIC + chunk); + MM_BombOnError (true); + if (mmerror) + mmerror = false; + else + { + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]); + } +} + +//========================================================================== + + +/* +=================== += += PlayLoop += +=================== +*/ + +void PlayLoop (void) +{ + int give; + + void (*think)(); + + ingame = true; + playstate = TimeCount = 0; + gamestate.shotpower = handheight = 0; + pointcount = pointsleft = 0; + + DrawLevelNumber (gamestate.mapon); + DrawBars (); + +#ifndef PROFILE + fizzlein = true; // fizzle fade in the first refresh +#endif + TimeCount = lasttimecount = lastnuke = 0; + + PollControls (); // center mouse + StartMusic (); + do + { +#ifndef PROFILE + PollControls(); +#else + c.xaxis = 1; + if (++TimeCount == 300) + return; +#endif + + for (obj = player;obj;obj = obj->next) + if (obj->active) + { + if (obj->ticcount) + { + obj->ticcount-=tics; + while ( obj->ticcount <= 0) + { + think = obj->state->think; + if (think) + { + think (obj); + if (!obj->state) + { + RemoveObj (obj); + goto nextactor; + } + } + + obj->state = obj->state->next; + if (!obj->state) + { + RemoveObj (obj); + goto nextactor; + } + if (!obj->state->tictime) + { + obj->ticcount = 0; + goto nextactor; + } + if (obj->state->tictime>0) + obj->ticcount += obj->state->tictime; + } + } + think = obj->state->think; + if (think) + { + think (obj); + if (!obj->state) + RemoveObj (obj); + } +nextactor:; + } + + + if (bordertime) + { + bordertime -= tics; + if (bordertime<=0) + { + bordertime = 0; + VW_ColorBorder (3); + } + } + + if (pointcount) + { + pointcount -= tics; + if (pointcount <= 0) + { + pointcount += POINTTICS; + give = (pointsleft > 1000)? 1000 : + ( + (pointsleft > 100)? 100 : + ((pointsleft < 20)? pointsleft : 20) + ); + SD_PlaySound (GETPOINTSSND); + AddPoints (give); + pointsleft -= give; + if (!pointsleft) + pointcount = 0; + } + } + + ThreeDRefresh (); + + CheckKeys(); + if (singlestep) + { + VW_WaitVBL(14); + lasttimecount = TimeCount; + } + if (extravbls) + VW_WaitVBL(extravbls); + + }while (!playstate); + StopMusic (); + + ingame = false; + if (bordertime) + { + bordertime = 0; + VW_ColorBorder (3); + } + + if (!abortgame) + AddPoints (pointsleft); + else + abortgame = false; +} + diff --git a/C3_SCALE.C b/C3_SCALE.C new file mode 100644 index 0000000..018083d --- /dev/null +++ b/C3_SCALE.C @@ -0,0 +1,690 @@ +/* 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. + */ + +// C3_SCALE.C + +#include "C3_DEF.H" +#pragma hdrstop + +//const unsigned viewheight = 144; +const unsigned screenbwide = 40; +const byte BACKGROUNDPIX = 5; + +unsigned shapesize[MAXSCALE+1]; +t_compscale _seg *scaledirectory[MAXSCALE+1]; +t_compshape _seg *shapedirectory[NUMSCALEPICS]; +memptr walldirectory[NUMSCALEWALLS]; + +/* +=========================== += += DeplanePic += += Takes a raw bit map of width bytes by height and creates a scaleable shape += += Returns the length of the shape in bytes += += Fills in spotvis (a convenient 64*64 array) with the color values += +=========================== +*/ + +void DeplanePic (int picnum) +{ + byte far *plane0,far *plane1,far *plane2,far *plane3; + byte by0,by1,by2,by3; + unsigned x,y,b,color,shift,width,height; + byte *dest; + +// +// convert ega pixels to byte color values in a temp buffer +// + width = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + + if (width>64 || height!=64) + Quit ("DePlanePic: Bad size shape"); + + memset (spotvis,BACKGROUNDPIX,sizeof(spotvis)); + + plane0 = (byte _seg *)grsegs[picnum]; + plane1 = plane0 + width*height; + plane2 = plane1 + width*height; + plane3 = plane2 + width*height; + + for (y=0;ycode[0]; + toppix = (viewheight-height)/2; + fix = 0; + + for (src=0;src<=64;src++) + { + startpix = fix>>16; + fix += step; + endpix = fix>>16; + + work->start[src] = startpix; + if (endpix>startpix) + work->width[src] = endpix-startpix; + else + work->width[src] = 0; + +// +// mark the start of the code +// + work->codeofs[src] = FP_OFF(code); + +// +// compile some code if the source pixel generates any screen pixels +// + startpix+=toppix; + endpix+=toppix; + + if (startpix == endpix || endpix < 0 || startpix >= VIEWHEIGHT || src == 64) + continue; + + // + // mov al,[si+src] + // + *code++ = 0x8a; + *code++ = 0x44; + *code++ = src; + + for (;startpix= VIEWHEIGHT) + break; // off the bottom of the view area + if (startpix < 0) + continue; // not into the view area + + // + // and [es:di+heightofs],al + // + *code++ = 0x26; + *code++ = 0x20; + *code++ = 0x85; + *((unsigned far *)code)++ = startpix*screenbwide; + } + + } + +// +// retf +// + *code++ = 0xcb; + + totalsize = FP_OFF(code); + MM_GetPtr (finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); + MM_FreePtr (&(memptr)work); + + return totalsize; +} + + + + +/* +======================== += += BuildCompShape += += typedef struct += { += unsigned width; += unsigned codeofs[64]; += } t_compshape; += += Width is the number of compiled line draws in the shape. The shape += drawing code will assume that the midpoint of the shape is in the += middle of the width. += += The non background pixel data will start at codeofs[width], so codeofs += greater than width will be invalid. += += Each code offset will draw one vertical line of the shape, consisting += of 0 or more segments of scaled pixels. += += The scaled shapes use ss:0-4 as a scratch variable for the far call to += the compiled scaler, so zero it back out after the shape is scaled, or += a "null pointer assignment" will result upon termination. += += Setup for a call to a compiled shape += ----------------------------------- += ax toast += bx toast += cx toast += dx segment of compiled shape += si toast += di byte at top of view area to draw the line in += bp 0 += ss:2 and ds the segment of the compiled scaler to use += es screenseg += += Upon return, ds IS NOT SET to the data segment. Do: += mov ax,ss += mov ds,ax += += += GC_BITMASK set to the pixels to be drawn in the row of bytes under DI += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += += += Code generated for each segment += ------------------------------- += mov bx,[(segend+1)*2] += mov cx,[bx] += mov [BYTE PTR bx],0xc8 // far return += mov ax,[segstart*2] += mov [ss:0],ax // entry point into the compiled scaler += mov ds,dx // (mov ds,cs) the data is after the compiled code += mov si,ofs data += call [bp] // scale some pixels += mov ds,[bp+2] += mov [bx],cx // un patch return += += Code generated after all segments on a line += ------------------------------------------- += retf += +======================== +*/ + +unsigned BuildCompShape (t_compshape _seg **finalspot) +{ + t_compshape _seg *work; + byte far *code; + int firstline,lastline,x,y; + unsigned firstpix,lastpix,width; + unsigned totalsize,pixelofs; + unsigned buff; + + +// MM_GetPtr (&(memptr)work,20000); + EGAWRITEMODE(0); + EGAREADMAP(0); // use ega screen memory for temp buffer + EGAMAPMASK(1); + buff = screenloc[1]; + work = (t_compshape _seg *)(0xa000+(buff+15)/16); + +// +// find the width of the shape +// + firstline = -1; + x=0; + do + { + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + { + firstline = x; + break; + } + if (++x == 64) + Quit ("BuildCompShape: No shape data!"); + } while (firstline == -1); + + lastline = -1; + x=63; + do + { + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + { + lastline = x; + break; + } + x--; + } while (lastline == -1); + + width = lastline-firstline+1; + + work->width = width; + code = (byte far *)&work->codeofs[width]; + +// +// copy all non background pixels to the work space +// + pixelofs = FP_OFF(code); + + for (x=firstline;x<=lastline;x++) + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + *code++ = spotvis[y][x]; + +// +// start compiling the vertical lines +// + for (x=firstline;x<=lastline;x++) + { + work->codeofs[x-firstline] = FP_OFF(code); + + y=0; + do + { + // + // scan past black background pixels + // + while (spotvis[y][x] == BACKGROUNDPIX && y<64) + y++; + + if (y>63) // no more segments + break; + + firstpix = y+1; // +1 because width is before codeofs + + // + // scan past scalable pixels + // + while (spotvis[y][x] != BACKGROUNDPIX && y<64) + y++; + + if (y>63) + lastpix = 65; + else + lastpix = y+1; // actually one pixel past the last displayed + + // + // compile the scale call + // + *code++ = 0x8b; // mov bx,[lastpix*2] + *code++ = 0x1e; + *((unsigned far *)code)++ = lastpix*2; + + *code++ = 0x8b; // mov cx,[bx] + *code++ = 0x0f; + + *code++ = 0xc6; // move [BYTE bx],0xcb + *code++ = 0x07; + *code++ = 0xcb; + + *code++ = 0xa1; // mov ax,[firstpix*2] /************* + *((unsigned far *)code)++ = firstpix*2; + + *code++ = 0x36; // mov [ss:0],ax + *code++ = 0xa3; + *code++ = 0x00; + *code++ = 0x00; + + *code++ = 0x8e; // mov ds,dx (mov ds,cs) + *code++ = 0xda; + + *code++ = 0xbe; // mov si,OFFSET pixelofs-firstpixel + *((unsigned far *)code)++ = pixelofs-firstpix; + + *code++ = 0xff; // call [DWORD bp] + *code++ = 0x5e; + *code++ = 0x00; + + *code++ = 0x8e; // mov ds,[bp+2] + *code++ = 0x5e; + *code++ = 0x02; + + *code++ = 0x89; // mov [bx],cx + *code++ = 0x0f; + + pixelofs += (lastpix-firstpix); + } while (y<63); + + // + // retf + // + *code++ = 0xcb; + } + + +// +// copy the final shape to a properly sized buffer +// + totalsize = FP_OFF(code); + MM_GetPtr ((memptr *)finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); +// MM_FreePtr (&(memptr)work); + + return totalsize; +} + + + +/* +======================= += += ScaleShape += += Draws a compiled shape at [scale] pixels high += += Setup for call += -------------- += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += GC_INDEX pointing at GC_BITMASK += +======================= +*/ + +static long longtemp; + +void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale) +{ + t_compscale _seg *comptable; + unsigned width,scalewidth; + int x,pixel,lastpixel,pixwidth,min; + unsigned far *codehandle, far *widthptr; + unsigned badcodeptr; + int rightclip; + + if (!compshape) + Quit ("ScaleShape: NULL compshape ptr!"); + + scale = (scale+1)/2; + if (!scale) + return; // too far away + if (scale>MAXSCALE) + scale = MAXSCALE; + comptable = scaledirectory[scale]; + + width = compshape->width; + scalewidth = comptable->start[width]; + + pixel = xcenter - scalewidth/2; + lastpixel = pixel+scalewidth-1; + if (pixel >= VIEWWIDTH || lastpixel < 0) + return; // totally off screen + +// +// scan backwards from the right edge until pixels are visable +// rightclip is the first NON VISABLE pixel +// + if (lastpixel>=VIEWWIDTH-1) + rightclip = VIEWWIDTH-1; + else + rightclip = lastpixel; + + if (zbuffer[rightclip]>scale) + { + if (pixel>0) + min = pixel; + else + min = 0; + do + { + if (--rightclip < min) + return; // totally covered or before 0 + if (zbuffer[rightclip]<=scale) + break; + } while (1); + } + rightclip++; + +// +// scan from the left until it is on screen, leaving +// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly +// + *(((unsigned *)&longtemp)+1) = (unsigned)compshape; // seg of shape + codehandle = &compshape->codeofs[0]; + badcodeptr = compshape->codeofs[width]; + widthptr = &comptable->width[0]; + asm mov ax,[comptable] + asm mov WORD PTR [2],ax // ds:0-4 is used as a far call pointer + // by the compiled shapes + pixwidth = *widthptr; // scaled width of this pixel + while (!pixwidth) + { + pixwidth = *++widthptr; // find the first visable pixel + codehandle++; + } + + if (pixel<0) + { + do + { + if (pixel+pixwidth>0) + { + pixwidth += pixel; + pixel = 0; + break; + } + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + pixel+=pixwidth; + } while (1); + } + +// +// scan until it is visable, leaving +// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly +// + do + { + if (zbuffer[pixel] <= scale) + break; // start drawing here + pixel++; + if (!--pixwidth) + { + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + } + } while (1); + + if (pixel+pixwidth>rightclip) + pixwidth = rightclip-pixel; +// +// draw lines +// + do // while (1) + { + // + // scale a vertical segment [pixwidth] pixels wide at [pixel] + // + (unsigned)longtemp = *codehandle; // offset of compiled code + if ((unsigned)longtemp == badcodeptr) + Quit ("ScaleShape: codehandle past end!"); + + asm mov bx,[pixel] + asm mov di,bx + asm shr di,1 + asm shr di,1 + asm shr di,1 // X in bytes + asm add di,[bufferofs] + asm and bx,7 + asm shl bx,1 + asm shl bx,1 + asm shl bx,1 + asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1 + asm dec bx + asm push bx + asm mov al,BYTE PTR [bitmasks1+bx] + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + + asm mov es,[screenseg] + asm push si + asm push di + asm push bp + asm xor bp,bp + asm mov dx,[WORD PTR longtemp+2] + asm mov ds,[2] + asm call ss:[DWORD PTR longtemp] // scale the line of pixels + asm mov ax,ss + asm mov ds,ax + asm pop bp + asm pop di + asm pop si + + asm pop bx + asm mov al,BYTE PTR [bitmasks2+bx] + asm or al,al + asm jz nosecond + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + asm push si + asm push di + asm push bp + asm xor bp,bp + asm mov dx,[WORD PTR longtemp+2] + asm mov ds,[2] + asm call ss:[DWORD PTR longtemp] // scale the line of pixels + asm mov ax,ss + asm mov ds,ax + asm pop bp + asm pop di + asm pop si + + + // + // advance to the next drawn line + // +nosecond:; + if ( (pixel+=pixwidth) == rightclip ) + { + asm mov WORD PTR [0],0 + asm mov WORD PTR [2],0 + return; // all done! + } + + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + + if (pixel+pixwidth > rightclip) + pixwidth = rightclip-pixel; + + } while (1); + +} + +// +// bit mask tables for drawing scaled strips up to eight pixels wide +// + +byte bitmasks1[8][8] = { +{0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}, +{0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f,0x7f}, +{0x20,0x30,0x38,0x3c,0x3e,0x3f,0x3f,0x3f}, +{0x10,0x18,0x1c,0x1e,0x1f,0x1f,0x1f,0x1f}, +{0x8,0xc,0xe,0xf,0xf,0xf,0xf,0xf}, +{0x4,0x6,0x7,0x7,0x7,0x7,0x7,0x7}, +{0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3}, +{0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1} }; + +byte bitmasks2[8][8] = { +{0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0x80}, +{0,0,0,0,0,0,0x80,0xc0}, +{0,0,0,0,0,0x80,0xc0,0xe0}, +{0,0,0,0,0x80,0xc0,0xe0,0xf0}, +{0,0,0,0x80,0xc0,0xe0,0xf0,0xf8}, +{0,0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc}, +{0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe} }; + + + + + + diff --git a/C3_SCA_A.ASM b/C3_SCA_A.ASM new file mode 100644 index 0000000..58a8737 --- /dev/null +++ b/C3_SCA_A.ASM @@ -0,0 +1,153 @@ +; 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. + +IDEAL +MODEL MEDIUM,C + +include "ID_ASM.EQU" + +;=========================================================================== +; +; SCALING GRAPHICS +; +;=========================================================================== + + + +MACRO MAKELAB NUM + +lab&NUM: + +ENDM + +MACRO MAKEREF NUM + +dw OFFSET lab&NUM + +ENDM + + +;========================================================================= + +MAXSCALES equ 256 + + DATASEG + +EXTRN screenseg:WORD +EXTRN linewidth:WORD + +LABEL endtable WORD +labcount = 0 +REPT MAXSCALES +MAKEREF %labcount +labcount = labcount + 1 +ENDM + + + CODESEG + +;================================================== +; +; void scaleline (int scale, unsigned picseg, unsigned maskseg, +; unsigned screen, unsigned width) +; +;================================================== + +PROC ScaleLine pixels:word, scaleptr:dword, picptr:dword, screen:word +USES si,di +PUBLIC ScaleLine + +; +; modify doline procedure for proper width +; + mov bx,[pixels] + cmp bx,MAXSCALES + jbe @@scaleok + mov bx,MAXSCALES +@@scaleok: + shl bx,1 + mov bx,[endtable+bx] + push [cs:bx] ;save the code that will be modified over + mov [WORD cs:bx],0d18eh ;mov ss,cx + push [cs:bx+2] ;save the code that will be modified over + mov [WORD cs:bx+2],90c3h ;ret / nop + push bx + + mov dx,[linewidth] + + mov di,[WORD screen] + mov es,[screenseg] + + mov si,[WORD scaleptr] + mov ds,[WORD scaleptr+2] + + mov bx,[WORD picptr] + mov ax,[WORD picptr+2] ;will be moved into ss after call + + mov bp,bx + + cli + call doline + sti +; +; restore doline to regular state +; + pop bx ;address of modified code + pop [cs:bx+2] + pop [cs:bx] + + mov ax,ss + mov ds,ax + ret + +;================ +; +; doline +; +; Big unwound scaling routine +; +; ds:si = scale table +; ss:bx = pic data +; es:di = screen location +; +;================ + +doline: + + mov cx,ss + mov ss,ax ;can't call a routine with ss used... + +labcount = 0 + +REPT MAXSCALES + +MAKELAB %labcount +labcount = labcount + 1 + + lodsb ; get scaled pixel number + xlat [ss:bx] ; look it up in the picture + xchg [es:di],al ; load latches and write pixel to screen + add di,dx ; down to next line + +ENDM + + mov ss,cx + ret + +ENDP + +END \ No newline at end of file diff --git a/C3_STATE.C b/C3_STATE.C new file mode 100644 index 0000000..8c31eb0 --- /dev/null +++ b/C3_STATE.C @@ -0,0 +1,546 @@ +/* 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. + */ + +// C3_STATE.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +dirtype opposite[9] = + {south,west,north,east,southwest,northwest,northeast,southeast,nodir}; + + + +//=========================================================================== + + +/* +=================== += += SpawnNewObj += +=================== +*/ + +void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size) +{ + GetNewObj (false); + new->size = size; + new->state = state; + new->ticcount = random (state->tictime)+1; + + new->tilex = x; + new->tiley = y; + new->x = ((long)x<y = ((long)y<dir = nodir; + + actorat[new->tilex][new->tiley] = new; +} + +void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size) +{ + GetNewObj (false); + new->size = size; + new->state = state; + new->ticcount = random (state->tictime)+1; + new->active = true; + + new->x = x; + new->y = y; + new->tilex = x>>TILESHIFT; + new->tiley = y>>TILESHIFT; + CalcBounds(new); + new->distance = 100; + new->dir = nodir; +} + + + +/* +=================== += += CheckHandAttack += += If the object can move next to the player, it will return true += +=================== +*/ + +boolean CheckHandAttack (objtype *ob) +{ + long deltax,deltay,size; + + size = (long)ob->size + player->size + ob->speed*tics; + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax > size || deltax < -size || deltay > size || deltay < -size) + return false; + + return true; +} + + +/* +=================== += += T_DoDamage += += Attacks the player if still nearby, then immediately changes to next state += +=================== +*/ + +void T_DoDamage (objtype *ob) +{ + int points; + + + if (!CheckHandAttack (ob)) + { + SD_PlaySound (MONSTERMISSSND); + } + else + { + points = 0; + + switch (ob->obclass) + { + case orcobj: + points = 4; + break; + case trollobj: + points = 8; + break; + case demonobj: + points = 15; + break; + } + TakeDamage (points); + } + + ob->state = ob->state->next; +} + + +//========================================================================== + +/* +================================== += += Walk += +================================== +*/ + +boolean Walk (objtype *ob) +{ + switch (ob->dir) + { + case north: + if (actorat[ob->tilex][ob->tiley-1]) + return false; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case northeast: + if (actorat[ob->tilex+1][ob->tiley-1]) + return false; + ob->tilex++; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case east: + if (actorat[ob->tilex+1][ob->tiley]) + return false; + ob->tilex++; + ob->distance = TILEGLOBAL; + return true; + + case southeast: + if (actorat[ob->tilex+1][ob->tiley+1]) + return false; + ob->tilex++; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case south: + if (actorat[ob->tilex][ob->tiley+1]) + return false; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case southwest: + if (actorat[ob->tilex-1][ob->tiley+1]) + return false; + ob->tilex--; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case west: + if (actorat[ob->tilex-1][ob->tiley]) + return false; + ob->tilex--; + ob->distance = TILEGLOBAL; + return true; + + case northwest: + if (actorat[ob->tilex-1][ob->tiley-1]) + return false; + ob->tilex--; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case nodir: + return false; + } + + Quit ("Walk: Bad dir"); + return false; +} + + + +/* +================================== += += ChaseThink += have the current monster go after the player, += either diagonally or straight on += +================================== +*/ + +void ChaseThink (objtype *obj, boolean diagonal) +{ + int deltax,deltay,i; + dirtype d[3]; + dirtype tdir, olddir, turnaround; + + + olddir=obj->dir; + turnaround=opposite[olddir]; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + d[1]=nodir; + d[2]=nodir; + + if (deltax>0) + d[1]= east; + if (deltax<0) + d[1]= west; + if (deltay>0) + d[2]=south; + if (deltay<0) + d[2]=north; + + if (abs(deltay)>abs(deltax)) + { + tdir=d[1]; + d[1]=d[2]; + d[2]=tdir; + } + + if (d[1]==turnaround) + d[1]=nodir; + if (d[2]==turnaround) + d[2]=nodir; + + + if (diagonal) + { /*ramdiagonals try the best dir first*/ + if (d[1]!=nodir) + { + obj->dir=d[1]; + if (Walk(obj)) + return; /*either moved forward or attacked*/ + } + + if (d[2]!=nodir) + { + obj->dir=d[2]; + if (Walk(obj)) + return; + } + } + else + { /*ramstraights try the second best dir first*/ + + if (d[2]!=nodir) + { + obj->dir=d[2]; + if (Walk(obj)) + return; + } + + if (d[1]!=nodir) + { + obj->dir=d[1]; + if (Walk(obj)) + return; + } + } + +/* there is no direct path to the player, so pick another direction */ + + obj->dir=olddir; + if (Walk(obj)) + return; + + if (US_RndT()>128) /*randomly determine direction of search*/ + { + for (tdir=north;tdir<=west;tdir++) + { + if (tdir!=turnaround) + { + obj->dir=tdir; + if (Walk(obj)) + return; + } + } + } + else + { + for (tdir=west;tdir>=north;tdir--) + { + if (tdir!=turnaround) + { + obj->dir=tdir; + if (Walk(obj)) + return; + } + } + } + + obj->dir=turnaround; + Walk(obj); /*last chance, don't worry about returned value*/ +} + + +/* +================= += += MoveObj += +================= +*/ + +void MoveObj (objtype *ob, long move) +{ + ob->distance -=move; + + switch (ob->dir) + { + case north: + ob->y -= move; + return; + case northeast: + ob->x += move; + ob->y -= move; + return; + case east: + ob->x += move; + return; + case southeast: + ob->x += move; + ob->y += move; + return; + case south: + ob->y += move; + return; + case southwest: + ob->x -= move; + ob->y += move; + return; + case west: + ob->x -= move; + return; + case northwest: + ob->x -= move; + ob->y -= move; + return; + + case nodir: + return; + } +} + + +/* +================= += += Chase += += returns true if hand attack range += +================= +*/ + +boolean Chase (objtype *ob, boolean diagonal) +{ + long move; + long deltax,deltay,size; + + move = ob->speed*tics; + size = (long)ob->size + player->size + move; + + while (move) + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size) + { + CalcBounds (ob); + return true; + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + if (ob->dir == nodir) + ob->dir = north; + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + ChaseThink (ob,diagonal); + if (!ob->distance) + break; // no possible move + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } + CalcBounds (ob); + return false; +} + +//=========================================================================== + + +/* +=================== += += ShootActor += +=================== +*/ + +void ShootActor (objtype *ob, unsigned damage) +{ + ob->hitpoints -= damage; + if (ob->hitpoints<=0) + { + switch (ob->obclass) + { + case orcobj: + ob->state = &s_orcdie1; + GivePoints (100); + break; + case trollobj: + ob->state = &s_trolldie1; + GivePoints (400); + break; + case demonobj: + ob->state = &s_demondie1; + GivePoints (1000); + break; + case mageobj: + ob->state = &s_magedie1; + GivePoints (600); + break; + case batobj: + ob->state = &s_batdie1; + GivePoints (100); + break; + case grelmobj: + ob->state = &s_greldie1; + GivePoints (10000); + break; + + } + ob->obclass = inertobj; + ob->shootable = false; + actorat[ob->tilex][ob->tiley] = NULL; + } + else + { + switch (ob->obclass) + { + case orcobj: + ob->state = &s_orcouch; + break; + case trollobj: + ob->state = &s_trollouch; + break; + case demonobj: + ob->state = &s_demonouch; + break; + case mageobj: + ob->state = &s_mageouch; + break; + case grelmobj: + ob->state = &s_grelouch; + break; + + } + } + ob->ticcount = ob->state->tictime; +} + diff --git a/C3_TRACE.C b/C3_TRACE.C new file mode 100644 index 0000000..45cb1bc --- /dev/null +++ b/C3_TRACE.C @@ -0,0 +1,872 @@ +/* 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. + */ + +// C3_TRACE.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +// +// TESTWALLVISABLE will set the global variable wallvisable to 1 or 0 +// depending on if tile.x,tile.y,wallon is visable from focal point +// +#define TESTWALLVISABLE { \ + if (tile.yfocal.x) \ + voffset += 2; \ + wallvisable = visable[voffset][wallon]; } + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean aborttrace; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +unsigned wallvisable,voffset; + + +fixed edgex,edgey; + +int wallon; +int basecolor; + +walltype *oldwall; + +// +// offsets from upper left corner of a tile to the left and right edges of +// a given wall (NORTH-WEST) +// +fixed point1x[4] = {GLOBAL1,GLOBAL1,0 ,0 }; +fixed point1y[4] = {0 ,GLOBAL1,GLOBAL1,0 }; + +fixed point2x[4] = {0 ,GLOBAL1,GLOBAL1,0 }; +fixed point2y[4] = {0 ,0 ,GLOBAL1 ,GLOBAL1}; + + +// +// offset from tile.x,tile.y of the tile that shares wallon side +// (side is not visable if it is shared) +// +int sharex[4] = { 0, 1, 0,-1}; +int sharey[4] = {-1, 0, 1, 0}; + +// +// amount to move tile.x,tile.y to follow wallon to another tile +// +int followx[4] = {-1, 0, 1, 0}; +int followy[4] = { 0,-1, 0, 1}; + +// +// cornerwall gives the wall on the same tile to start following when the +// wall ends at an empty tile (go around an edge on same tile) +// turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following +// when the wall hits another tile (right angle corner) +// +int cornerwall[4] = {WEST,NORTH,EAST,SOUTH}; +int turnwall[4] = {EAST,SOUTH,WEST,NORTH}; + +// +// wall visabilities in reletive locations +// -,- 0,- +,- +// -,0 0,0 +,0 +// -,+ 0,+ +,+ +// +int visable[9][4] = +{ + {0,1,1,0}, {0,0,1,0}, {0,0,1,1}, + {0,1,0,0}, {0,0,0,0}, {0,0,0,1}, + {1,1,0,0}, {1,0,0,0}, {1,0,0,1} +}; + +int startwall[9] = {2,2,3, 1,0,3, 1,0,0}; +int backupwall[9] = {3,3,0, 2,0,0, 2,1,1}; + + +int walllength; + +/* +============================================================================= + + FUNCTIONS + +============================================================================= +*/ + +/* +======================== += += FollowTrace += +======================== +*/ + +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max) +{ + int tx,ty,otx,oty; + long absdx,absdy,xstep,ystep; + + tx = tracex>>TILESHIFT; + ty = tracey>>TILESHIFT; + + spotvis[tx][ty] = true; + + absdx=LABS(deltax); + absdy=LABS(deltay); + + if (absdx>absdy) + { + ystep = (deltay<<8)/(absdx>>8); + + if (!ystep) + ystep = deltay>0 ? 1 : -1; + + oty = (tracey+ystep)>>TILESHIFT; + if (deltax>0) + { +//############### +// +// step x by +1 +// +//############### + do + { + tx++; + spotvis[tx][ty] = true; + tracey+=ystep; + ty = tracey>>TILESHIFT; + + if (ty!=oty) + { + if (tilemap[tx-1][ty]) + { + tile.x = tx-1; + tile.y = ty; + return 1; + } + oty = ty; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + else + { +//############### +// +// step x by -1 +// +//############### + do + { + tx--; + spotvis[tx][ty] = true; + tracey+=ystep; + ty = tracey>>TILESHIFT; + + if (ty!=oty) + { + if (tilemap[tx][oty]) + { + tile.x = tx; + tile.y = oty; + return 1; + } + oty = ty; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + + } + } + else + { + xstep = (deltax<<8)/(absdy>>8); + if (!xstep) + xstep = deltax>0 ? 1 : -1; + + + otx = (tracex+xstep)>>TILESHIFT; + if (deltay>0) + { +//############### +// +// step y by +1 +// +//############### + do + { + ty++; + spotvis[tx][ty] = true; + tracex+=xstep; + tx = tracex>>TILESHIFT; + + if (tx!=otx) + { + if (tilemap[tx][ty-1]) + { + tile.x = tx; + tile.y = ty-1; + return 1; + } + otx = tx; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + else + { +//############### +// +// step y by -1 +// +//############### + do + { + ty--; + spotvis[tx][ty] = true; + tracex+=xstep; + tx = tracex>>TILESHIFT; + + if (tx!=otx) + { + if (tilemap[otx][ty]) + { + tile.x = otx; + tile.y = ty; + return 1; + } + otx = tx; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + + } + +} + + +//=========================================================================== + + +/* +================= += += BackTrace += += Traces backwards from edgex,edgey to viewx,viewy to see if a closer += tile obscures the given point. If it does, it finishes the wall and += starts a new one. += Returns true if a tile is hit. += Call with a 1 to have it automatically finish the current wall += +================= +*/ + +int BackTrace (int finish) +{ + fixed tracex,tracey; + long deltax,deltay,absdx,absdy; + int steps,otx,oty,testx,testheight,offset,wall; + + deltax = viewx-edgex; + deltay = viewy-edgey; + + absdx = LABS(deltax); + absdy = LABS(deltay); + + if (absdx>absdy) + steps = ABS(focal.x-(edgex>>TILESHIFT))-1; + else + steps = ABS(focal.y-(edgey>>TILESHIFT))-1; + + if (steps<=0) + return 0; + + otx = tile.x; + oty = tile.y; + if (!FollowTrace(edgex,edgey,deltax,deltay,steps)) + return 0; + +// +// if the start wall is behind the focal point, the trace went too far back +// + if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2) // too close + { + if (tile.x == focal.x && tile.y == focal.y) + { + tile.x = otx; + tile.y = oty; + return 0; + } + + if (tile.xx1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + return 0; + } + + +// +// back up along the intersecting face to find the rightmost wall +// + + if (tile.yfocal.x) + offset += 2; + + wallon = backupwall[offset]; + + while (tilemap[tile.x][tile.y]) + { + tile.x += followx[wallon]; + tile.y += followy[wallon]; + }; + + tile.x -= followx[wallon]; + tile.y -= followy[wallon]; + + wallon = cornerwall[wallon]; // turn to first visable face + + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; + + return 1; +} + +//=========================================================================== + + +/* +================= += += ForwardTrace += += Traces forwards from edgex,edgey along the line from viewx,viewy until += a solid tile is hit. Sets tile.x,tile.y += +================= +*/ + +void ForwardTrace (void) +{ + int offset; + fixed tracex,tracey; + long deltax,deltay; + + deltax = edgex-viewx; + deltay = edgey-viewy; + + FollowTrace(edgex,edgey,deltax,deltay,0); + + if (tile.yfocal.x) + offset += 2; + + wallon = startwall[offset]; + +// +// start the new wall +// + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + +// +// if entire first wall is invisable, corner +// + TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon], + &rightwall->x2,&rightwall->height2); + + if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]] + || rightwall->x2 < (rightwall-1)->x2 ) + wallon = cornerwall [wallon]; + +// +// transform first point +// + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; +} + + +//=========================================================================== + + +/* +================= += += FinishWall += += Transforms edgex,edgey as the next point of the current wall += and sticks it in the wall list += +================= +*/ + +int FinishWall (void) +{ + char num[20]; + + oldwall = rightwall; + + rightwall->color = basecolor; + + TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2); + + if (rightwall->x2 <= (rightwall-1)->x2+2 + && rightwall->height2 < (rightwall-1)->height2 ) + return 0; + + rightwall->walllength = walllength; + + switch (wallon) + { + case north: + case south: + rightwall->side = 0; + rightwall->planecoord = edgey; + break; + + case west: + case east: + rightwall->side = 1; + rightwall->planecoord = edgex; + break; + } + + walllength = 1; + + rightwall++; + + return 1; +} + +//=========================================================================== + + +/* +================= += += InsideCorner += +================= +*/ + +void InsideCorner (void) +{ + int offset; + + // + // the wall turned -90 degrees, so draw what we have, move to the new tile, + // change wallon, change color, and continue following. + // + FinishWall (); + + tile.x += sharex[wallon]; + tile.y += sharey[wallon]; + + wallon = turnwall[wallon]; + + // + // if the new wall is visable, continue following it. Otherwise + // follow it backwards until it turns + // + TESTWALLVISABLE; + + if (wallvisable) + { + // + // just turn to the next wall and continue + // + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + basecolor = tilemap[tile.x][tile.y]; + return; // continue from here + } + + // + // back follow the invisable wall until it turns, then follow that + // + do + { + tile.x += followx[wallon]; + tile.y += followy[wallon]; + } while (tilemap[tile.x][tile.y]); + + tile.x -= followx[wallon]; + tile.y -= followy[wallon]; + + wallon = cornerwall[wallon]; // turn to first visable face + + edgex = ((long)tile.x<<16)+point1x[wallon]; + edgey = ((long)tile.y<<16)+point1y[wallon]; + + if (!BackTrace(0)) // backtrace without finishing a wall + { + TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1); + basecolor = tilemap[tile.x][tile.y]; + } +} + +//=========================================================================== + + +/* +================= += += OutsideCorner += +================= +*/ + +void OutsideCorner (void) +{ + int offset; + + // + // edge is the outside edge of a corner, so draw the current wall and + // turn the corner (+90 degrees) + // + FinishWall (); + + tile.x -= followx[wallon]; // backup to the real tile + tile.y -= followy[wallon]; + wallon = cornerwall[wallon]; + + // + // if the new wall is visable, continue following it. Otherwise + // trace a ray from the corner to find a wall in the distance to + // follow + // + TESTWALLVISABLE; + + if (wallvisable) + { + // + // the new wall is visable, so just continue on + // + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + return; // still on same tile, so color is ok + } + +// +// start from a new tile further away +// + ForwardTrace(); // find the next wall further back + +} + + +//=========================================================================== + + +/* +================= += += FollowWalls += += Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it += until something else is seen or the entire view area is covered += +================= +*/ + +void FollowWalls (void) +{ + int height,newcolor,offset,wall; + +//#################### +// +// figure leftmost wall of new tile +// +//#################### + +restart: + + walllength = 1; + + if (tile.yfocal.x) + offset += 2; + + wallon = startwall[offset]; + +// +// if the start wall is inside a block, skip it by cornering to the second wall +// + if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) + wallon = cornerwall [wallon]; + +// +// transform first edge to screen coordinates +// + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; + +//################## +// +// follow the wall as long as possible +// +//################## + +advance: + + do // while ( tile.x != right.x || tile.y != right.y) + { +// +// check for conditions that shouldn't happed... +// + if (rightwall->x1 > VIEWXH) // somehow missed right tile... + return; + + if (rightwall == &walls[DANGERHIGH]) + { + // + // somethiing got messed up! Correct by thrusting ahead... + // + VW_ColorBorder(6); + bordertime = 60; + Thrust(player->angle,TILEGLOBAL/4); + player->angle+=5; + if (player->angle>ANGLES) + player->angle-=ANGLES; + aborttrace = true; + return; + +#if 0 + strcpy (str,"Wall list overflow at LE:"); + itoa(mapon+1,str2,10); + strcat (str,str2); + strcat (str," X:"); + ltoa(objlist[0].x,str2,10); + strcat (str,str2); + strcat (str," Y:"); + ltoa(objlist[0].y,str2,10); + strcat (str,str2); + strcat (str," AN:"); + itoa(objlist[0].angle,str2,10); + strcat (str,str2); + + Quit (str); +#endif + } + +// +// proceed along wall +// + + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + + if (BackTrace(1)) // went behind a closer wall + continue; + + // + // advance to next tile along wall + // + tile.x += followx[wallon]; + tile.y += followy[wallon]; + + if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) + { + InsideCorner (); // turn at a corner + continue; + } + + newcolor = tilemap[tile.x][tile.y]; + + if (!newcolor) // turn around an edge + { + OutsideCorner (); + continue; + } + + if (newcolor != basecolor) + { + // + // wall changed color, so draw what we have and continue following + // + FinishWall (); + rightwall->x1 = oldwall->x2; // new wall shares this edge + rightwall->height1 = oldwall->height2; + basecolor = newcolor; + + continue; + } + walllength++; + } while (tile.x != right.x || tile.y != right.y); + + + +//###################### +// +// draw the last tile +// +//###################### + + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + FinishWall(); + + wallon = cornerwall[wallon]; + + // + // if the corner wall is visable, draw it + // + TESTWALLVISABLE; + + if (wallvisable) + { + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + FinishWall(); + } + +} + +//=========================================================================== diff --git a/C3_WIZ.C b/C3_WIZ.C new file mode 100644 index 0000000..1d68bc7 --- /dev/null +++ b/C3_WIZ.C @@ -0,0 +1,2046 @@ +/* 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. + */ + +// C3_WIZ.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define NUMSCROLLS 8 + +#define SHOWITEMS 9 + +#define NUKETIME 40 +#define NUMBOLTS 10 +#define BOLTTICS 6 + +#define STATUSCOLOR 4 +#define TEXTCOLOR 14 + +#define SIDEBARWIDTH 5 + +#define BODYLINE 8 +#define POWERLINE 80 + +#define SPECTILESTART 18 + + +#define SHOTDAMAGE 1 +#define BIGSHOTDAMAGE 3 + + +#define PLAYERSPEED 5120 +#define RUNSPEED 8192 + +#define SHOTSPEED 10000 + +#define LASTWALLTILE 17 +#define LASTSPECIALTILE 37 + +#define FIRETIME 4 // DEBUG 60 + +#define HANDPAUSE 60 + +#define COMPASSX 33 +#define COMPASSY 0 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +long lastnuke,lasthand; +int handheight; +int boltsleft; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +int lasttext,lastcompass; +int bolttimer; +unsigned lastfiretime; + +int strafeangle[9] = {0,90,180,270,45,135,225,315,0}; + + +//=========================================================================== + +void DrawChar (unsigned x, unsigned y, unsigned tile); +void RedrawStatusWindow (void); +void GiveBolt (void); +void TakeBolt (void); +void GiveNuke (void); +void TakeNuke (void); +void GivePotion (void); +void TakePotion (void); +void GiveKey (int keytype); +void TakeKey (int keytype); +void GiveScroll (int scrolltype,boolean show); +void ReadScroll (int scroll); +void GivePoints (int points); +void DrawLevelNumber (int number); +void DrawText (void); +void DrawBars (void); + +//---------- + +void Shoot (void); +void BigShoot (void); +void CastBolt (void); +void CastNuke (void); +void DrinkPotion (void); + +//---------- + +void SpawnPlayer (int tilex, int tiley, int dir); +void Thrust (int angle, unsigned speed); +void T_Player (objtype *ob); + +void AddPoints (int points); + +void ClipMove (objtype *ob, long xmove, long ymove); +boolean ShotClipMove (objtype *ob, long xmove, long ymove); + +//=========================================================================== + + +/* +=============== += += DrawChar += +=============== +*/ + +void DrawChar (unsigned x, unsigned y, unsigned tile) +{ + unsigned junk = latchpics[0]; + + EGAWRITEMODE(1); +asm mov bx,[y] +asm shl bx,1 +asm mov di,[WORD PTR ylookup+bx] +asm add di,[x] +asm mov si,[tile] +asm shl si,1 +asm shl si,1 +asm shl si,1 +asm add si,[junk] // the damn inline assembler won't reference latchpics +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax +asm mov dx,SCREENWIDTH-1 +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb +asm add di,dx +asm movsb + +asm mov ax,ss +asm mov ds,ax + EGAWRITEMODE(0); +} + + +//=========================================================================== + +/* +=============== += += RedrawStatusWindow += +=============== +*/ + +void RedrawStatusWindow (void) +{ + int i,j,x; + + DrawLevelNumber (gamestate.mapon); + lasttext = -1; + lastcompass = -1; + + j = gamestate.bolts < SHOWITEMS ? gamestate.bolts : SHOWITEMS; + for (i=0;itiley]+player->tilex)-NAMESTART; + + if ( number>26 ) + number = 0; + + if (number == lasttext) + return; + + bufferofs = 0; + lasttext = number; + + PrintY = 4; + WindowX = 26; + WindowW = 232; + + text = (char _seg *)grsegs[LEVEL1TEXT+mapon]+textstarts[number]; + + _fmemcpy (str,text,80); + + VW_Bar (26,4,232,9,STATUSCOLOR); + temp = fontcolor; + fontcolor = TEXTCOLOR^STATUSCOLOR; + US_CPrintLine (str); + fontcolor = temp; +} + +//=========================================================================== + +/* +=============== += += DrawCompass += +=============== +*/ + +void DrawCompass (void) +{ + int angle,number; + + // + // draw the compass if needed + // + angle = player->angle-ANGLES/4; + angle -= ANGLES/32; + if (angle<0) + angle+=ANGLES; + number = angle/(ANGLES/16); + if (number>15) // because 360 angles doesn't divide by 16 + number = 15; + + if (number == lastcompass) + return; + + lastcompass = number; + + bufferofs = 0; + LatchDrawPic (COMPASSX,COMPASSY,COMPAS1PIC+15-number); +} + +//=========================================================================== + + +/* +=============== += += DrawBars += +=============== +*/ + +void DrawBars (void) +{ + int i; + unsigned source,dest,topline; + + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_Bar (34*8,POWERLINE,40,MAXSHOTPOWER,1); + } + EGAWRITEMODE(1); + asm mov es,[screenseg] + +// +// shot power +// + if (gamestate.shotpower) + { + topline = MAXSHOTPOWER - gamestate.shotpower; + + source = latchpics[SHOTPOWERPIC-FIRSTLATCHPIC]+topline*SIDEBARWIDTH; + dest = (POWERLINE+topline)*SCREENWIDTH+34; + + asm mov si,[source] + asm mov di,[dest] + + asm mov cx,[WORD PTR gamestate.shotpower] +newline: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline + } + +// +// body +// + if (gamestate.body) + { + source = latchpics[BODYPIC-FIRSTLATCHPIC]; + dest = BODYLINE*SCREENWIDTH+34; + + asm mov si,[source] + asm mov di,[dest] + + asm mov cx,[WORD PTR gamestate.body] +newline2: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline2 + } + + if (gamestate.body != MAXBODY) + { + source = latchpics[NOBODYPIC-FIRSTLATCHPIC]+gamestate.body*SIDEBARWIDTH; + dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34; + topline = MAXBODY-gamestate.body; + + asm mov si,[source] + asm mov di,[dest] + + asm mov cx,[WORD PTR topline] +newline3: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline3 + } + + EGAWRITEMODE(0); +} + + +/* +============================================================================= + + SHOTS + +============================================================================= +*/ + +void T_Pshot (objtype *ob); + + +extern statetype s_pshot1; +extern statetype s_pshot2; + +extern statetype s_bigpshot1; +extern statetype s_bigpshot2; + + +statetype s_pshot1 = {PSHOT1PIC,8,&T_Pshot,&s_pshot2}; +statetype s_pshot2 = {PSHOT2PIC,8,&T_Pshot,&s_pshot1}; + +statetype s_shotexplode = {PSHOT2PIC,8,NULL,NULL}; + +statetype s_bigpshot1 = {BIGPSHOT1PIC,8,&T_Pshot,&s_bigpshot2}; +statetype s_bigpshot2 = {BIGPSHOT2PIC,8,&T_Pshot,&s_bigpshot1}; + + +/* +=================== += += SpawnPShot += +=================== +*/ + +void SpawnPShot (void) +{ + SpawnNewObjFrac (player->x,player->y,&s_pshot1,PIXRADIUS*14); + new->obclass = pshotobj; + new->speed = SHOTSPEED; + new->angle = player->angle; +} + +void SpawnBigPShot (void) +{ + SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS); + new->obclass = bigpshotobj; + new->speed = SHOTSPEED; + new->angle = player->angle; +} + + +/* +=============== += += T_Pshot += +=============== +*/ + +void T_Pshot (objtype *ob) +{ + objtype *check; + long xmove,ymove,speed; + +// +// check current position for monsters having moved into it +// + for (check = player->next; check; check=check->next) + if (check->shootable + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + ob->state = &s_shotexplode; + ob->ticcount = ob->state->tictime; + return; + } + + +// +// move ahead, possibly hitting a wall +// + speed = ob->speed*tics; + + xmove = FixedByFrac(speed,costable[ob->angle]); + ymove = -FixedByFrac(speed,sintable[ob->angle]); + + if (ShotClipMove(ob,xmove,ymove)) + { + ob->state = &s_shotexplode; + ob->ticcount = ob->state->tictime; + return; + } + + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + +// +// check final position for monsters hit +// + for (check = player->next; check; check=check->next) + if (ob->shootable + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + ShootActor (check,SHOTDAMAGE); + ob->state = &s_shotexplode; + ob->ticcount = ob->state->tictime; + return; + } + +} + + + +/* +============================================================================= + + PLAYER ACTIONS + +============================================================================= +*/ + + +/* +=============== += += BuildShotPower += +=============== +*/ + +void BuildShotPower (void) +{ + int newlines,topline; + long i; + unsigned source,dest; + + if (gamestate.shotpower == MAXSHOTPOWER) + return; + + newlines = 0; + for (i=lasttimecount-tics;i MAXSHOTPOWER) + { + newlines -= (gamestate.shotpower - MAXSHOTPOWER); + gamestate.shotpower = MAXSHOTPOWER; + } + + topline = MAXSHOTPOWER - gamestate.shotpower; + + source = latchpics[L_SHOTBAR]+topline*SIDEBARWIDTH; + dest = (POWERLINE+topline)*SCREENWIDTH+34; + + asm mov es,[screenseg] + asm mov si,[source] + asm mov di,[dest] + + EGAWRITEMODE(1); + + if (newlines) + { + asm mov cx,[newlines] +newline: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline + } + + EGAWRITEMODE(0); +} + + +//=========================================================================== + +/* +=============== += += ClearShotPower += +=============== +*/ + +void ClearShotPower (void) +{ + unsigned source,dest,topline; + + topline = MAXSHOTPOWER - gamestate.shotpower; + + source = latchpics[L_NOSHOT]+topline*SIDEBARWIDTH; + dest = (POWERLINE+topline)*SCREENWIDTH+34; + + asm mov es,[screenseg] + asm mov si,[source] + asm mov di,[dest] + + if (!gamestate.shotpower) + return; + + EGAWRITEMODE(1); + + asm mov cx,[WORD PTR gamestate.shotpower] +newline: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline + + EGAWRITEMODE(0); + + gamestate.shotpower = 0; +} + +//=========================================================================== + +/* +=============== += += Shoot += +=============== +*/ + +void Shoot (void) +{ + ClearShotPower (); + SD_PlaySound (SHOOTSND); + SpawnPShot (); +} + +//=========================================================================== + +/* +=============== += += BigShoot += +=============== +*/ + +void BigShoot (void) +{ + ClearShotPower (); + SD_PlaySound (BIGSHOOTSND); + SpawnBigPShot (); +} + +//=========================================================================== + +/* +=============== += += CastBolt += +=============== +*/ + +void CastBolt (void) +{ + if (!gamestate.bolts) + { + SD_PlaySound (NOITEMSND); + return; + } + + TakeBolt (); + boltsleft = NUMBOLTS; + bolttimer = BOLTTICS; + BigShoot (); +} + + +/* +=============== += += ContinueBolt += +=============== +*/ + +void ContinueBolt (void) +{ + bolttimer-=tics; + if (bolttimer<0) + { + boltsleft--; + bolttimer = BOLTTICS; + BigShoot (); + } +} + + +//=========================================================================== + +/* +=============== += += CastNuke += +=============== +*/ + +void CastNuke (void) +{ + int angle; + + if (!gamestate.nukes) + { + SD_PlaySound (NOITEMSND); + return; + } + + TakeNuke (); + lastnuke = TimeCount; + + for (angle = 0; angle < ANGLES; angle+= ANGLES/16) + { + SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS); + new->obclass = bigpshotobj; + new->speed = SHOTSPEED; + new->angle = angle; + } +} + +//=========================================================================== + +/* +=============== += += DrinkPotion += +=============== +*/ + +void DrinkPotion (void) +{ + unsigned source,dest,topline; + + if (!gamestate.potions) + { + SD_PlaySound (NOITEMSND); + return; + } + + TakePotion (); + gamestate.body = MAXBODY; + +// +// draw a full up bar +// + source = latchpics[L_BODYBAR]; + dest = BODYLINE*SCREENWIDTH+34; + + asm mov es,[screenseg] + asm mov si,[source] + asm mov di,[dest] + + EGAWRITEMODE(1); + + asm mov cx,MAXBODY +newline: + asm mov al,[es:si] + asm mov [es:di+PAGE1START],al + asm mov [es:di+PAGE2START],al + asm mov [es:di+PAGE3START],al + asm mov al,[es:si+1] + asm mov [es:di+1+PAGE1START],al + asm mov [es:di+1+PAGE2START],al + asm mov [es:di+1+PAGE3START],al + asm mov al,[es:si+2] + asm mov [es:di+2+PAGE1START],al + asm mov [es:di+2+PAGE2START],al + asm mov [es:di+2+PAGE3START],al + asm mov al,[es:si+3] + asm mov [es:di+3+PAGE1START],al + asm mov [es:di+3+PAGE2START],al + asm mov [es:di+3+PAGE3START],al + asm mov al,[es:si+4] + asm mov [es:di+4+PAGE1START],al + asm mov [es:di+4+PAGE2START],al + asm mov [es:di+4+PAGE3START],al + asm add di,SCREENWIDTH + asm add si,5 + + asm loop newline + + EGAWRITEMODE(0); +} + + + +//=========================================================================== + +/* +=============== += += ReadScroll += +=============== +*/ + +extern boolean tileneeded[NUMFLOORS]; + +void ReadScroll (int scroll) +{ + int i; + +// +// make wall pictures purgable +// + for (i=0;i= gamestate.body) + { + points = gamestate.body; + playstate = ex_died; + } + + bordertime = points*FLASHTICS; + VW_ColorBorder (FLASHCOLOR); + + if (gamestate.bodyxh < check->xl || ob->xl > check->xh || + ob->yh < check->yl || ob->yl > check->yh) + return false; // not quite touching + + switch (check->obclass) + { + case bonusobj: + if (check->temp1 == B_BOLT) + GiveBolt (); + else if (check->temp1 == B_NUKE) + GiveNuke (); + else if (check->temp1 == B_POTION) + GivePotion (); + else if (check->temp1 >= B_RKEY && check->temp1 <= B_BKEY) + GiveKey (check->temp1-B_RKEY); + else if (check->temp1 >= B_SCROLL1 && check->temp1 <= B_SCROLL8) + GiveScroll (check->temp1-B_SCROLL1,true); + else if (check->temp1 == B_CHEST) + GiveChest (); + else if (check->temp1 == B_GOAL) + GiveGoal (); + (unsigned)actorat[check->tilex][check->tiley] = 0; + RemoveObj (check); + + return false; + + } + return true; +} + + +/* +================== += += CalcBounds += +================== +*/ + +void CalcBounds (objtype *ob) +{ +// +// calculate hit rect +// + ob->xl = ob->x - ob->size; + ob->xh = ob->x + ob->size; + ob->yl = ob->y - ob->size; + ob->yh = ob->y + ob->size; +} + + +/* +=================== += += LocationInActor += +=================== +*/ + +boolean LocationInActor (objtype *ob) +{ + int x,y,xmin,ymin,xmax,ymax; + objtype *check; + + CalcBounds (ob); + + xmin = (ob->x >> TILESHIFT)-2; + ymin = (ob->y >> TILESHIFT)-2; + xmax = xmin+5; + ymax = ymin+5; + + for (x=xmin;x(objtype *)LASTSPECIALTILE + && check->shootable + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + return true; + } + + return false; +} + + +/* +=================== += += ClipMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ + +void ClipMove (objtype *ob, long xmove, long ymove) +{ + int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y; + long intersect,basex,basey,pointx,pointy; + unsigned inside,total,tile; + objtype *check; + boolean moveok; + +// +// move player and check to see if any corners are in solid tiles +// + basex = ob->x; + basey = ob->y; + + ob->x += xmove; + ob->y += ymove; + + CalcBounds (ob); + + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + check = actorat[x][y]; + if (!check) + continue; // blank floor, walk ok + + if ((unsigned)check<=LASTWALLTILE) + goto blockmove; // solid wall + + if ((unsigned)check<=LASTSPECIALTILE) + { + if ( HitSpecialTile (x,y,(unsigned)check-SPECTILESTART) ) + goto blockmove; // whatever it was, it blocked the move + else + continue; + } + TouchActor(ob,check); // pick up items + } + +// +// check nearby actors +// + if (LocationInActor(ob)) + { + ob->x -= xmove; + if (LocationInActor(ob)) + { + ob->x += xmove; + ob->y -= ymove; + if (LocationInActor(ob)) + ob->x -= xmove; + } + } + return; // move is OK! + + +blockmove: + + if (!SD_SoundPlaying()) + SD_PlaySound (HITWALLSND); + + moveok = false; + + do + { + xmove /= 2; + ymove /= 2; + if (moveok) + { + ob->x += xmove; + ob->y += ymove; + } + else + { + ob->x -= xmove; + ob->y -= ymove; + } + CalcBounds (ob); + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + if (tilemap[xl][yl] || tilemap[xh][yl] + || tilemap[xh][yh] || tilemap[xl][yh] ) + { + moveok = false; + if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + { + if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048) + return; + moveok = true; + } + } while (1); +} + + +//========================================================================== + + +/* +=================== += += ShotClipMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ + +boolean ShotClipMove (objtype *ob, long xmove, long ymove) +{ + int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y; + long intersect,basex,basey,pointx,pointy; + unsigned inside,total,tile; + objtype *check; + boolean moveok; + +// +// move shot and check to see if any corners are in solid tiles +// + basex = ob->x; + basey = ob->y; + + ob->x += xmove; + ob->y += ymove; + + CalcBounds (ob); + + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + tile = tilemap[x][y]; + if (tile) + { + if ((unsigned)(tile-EXPWALLSTART)x += xmove; + ob->y += ymove; + } + else + { + ob->x -= xmove; + ob->y -= ymove; + } + CalcBounds (ob); + xl = ob->xl>>TILESHIFT; + yl = ob->yl>>TILESHIFT; + xh = ob->xh>>TILESHIFT; + yh = ob->yh>>TILESHIFT; + if (tilemap[xl][yl] || tilemap[xh][yl] + || tilemap[xh][yh] || tilemap[xl][yh] ) + { + moveok = false; + if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048) + { + ob->x = basex; + ob->y = basey; + return true; + } + } + else + { + if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048) + return true; + moveok = true; + } + } while (1); +} + + + +/* +============================================================================= + + PLAYER CONTROL + +============================================================================= +*/ + + + +void T_Player (objtype *ob); + +statetype s_player = {0,0,&T_Player,&s_player}; + +/* +=============== += += SpawnPlayer += +=============== +*/ + +void SpawnPlayer (int tilex, int tiley, int dir) +{ + player->obclass = playerobj; + player->active = true; + player->tilex = tilex; + player->tiley = tiley; + player->x = ((long)tilex<y = ((long)tiley<state = &s_player; + player->angle = (1-dir)*90; + player->size = MINDIST; + CalcBounds (player); + if (player->angle<0) + player->angle += ANGLES; +} + + +/* +=================== += += Thrust += +=================== +*/ + +void Thrust (int angle, unsigned speed) +{ + long xmove,ymove; + + if (lasttimecount>>5 != ((lasttimecount-tics)>>5) ) + { + // + // walk sound + // + if (lasttimecount&32) + SD_PlaySound (WALK1SND); + else + SD_PlaySound (WALK2SND); + } + + xmove = FixedByFrac(speed,costable[angle]); + ymove = -FixedByFrac(speed,sintable[angle]); + + ClipMove(player,xmove,ymove); + player->tilex = player->x >> TILESHIFT; + player->tiley = player->y >> TILESHIFT; +} + + + +/* +======================= += += ControlMovement += +======================= +*/ + +void ControlMovement (objtype *ob) +{ + int angle; + long speed; + + + if (c.button1) + { + // + // strafing + // + // + // side to side move + // + if (!mousexmove) + speed = 0; + else if (mousexmove<0) + speed = -(long)mousexmove*300; + else + speed = -(long)mousexmove*300; + + if (c.xaxis == -1) + { + if (running) + speed += RUNSPEED*tics; + else + speed += PLAYERSPEED*tics; + } + else if (c.xaxis == 1) + { + if (running) + speed -= RUNSPEED*tics; + else + speed -= PLAYERSPEED*tics; + } + + if (speed > 0) + { + if (speed >= TILEGLOBAL) + speed = TILEGLOBAL-1; + angle = ob->angle + ANGLES/4; + if (angle >= ANGLES) + angle -= ANGLES; + Thrust (angle,speed); // move to left + } + else if (speed < 0) + { + if (speed <= -TILEGLOBAL) + speed = -TILEGLOBAL+1; + angle = ob->angle - ANGLES/4; + if (angle < 0) + angle += ANGLES; + Thrust (angle,-speed); // move to right + } + } + else + { + // + // not strafing + // + + // + // turning + // + if (c.xaxis == 1) + { + ob->angle -= tics; + if (running) // fast turn + ob->angle -= tics; + } + else if (c.xaxis == -1) + { + ob->angle+= tics; + if (running) // fast turn + ob->angle += tics; + } + + ob->angle -= (mousexmove/10); + + if (ob->angle >= ANGLES) + ob->angle -= ANGLES; + if (ob->angle < 0) + ob->angle += ANGLES; + + } + + // + // forward/backwards move + // + if (!mouseymove) + speed = 0; + else if (mouseymove<0) + speed = -(long)mouseymove*500; + else + speed = -(long)mouseymove*200; + + if (c.yaxis == -1) + { + if (running) + speed += RUNSPEED*tics; + else + speed += PLAYERSPEED*tics; + } + else if (c.yaxis == 1) + { + if (running) + speed -= RUNSPEED*tics; + else + speed -= PLAYERSPEED*tics; + } + + if (speed > 0) + { + if (speed >= TILEGLOBAL) + speed = TILEGLOBAL-1; + Thrust (ob->angle,speed); // move forwards + } + else if (speed < 0) + { + if (speed <= -TILEGLOBAL) + speed = -TILEGLOBAL+1; + angle = ob->angle + ANGLES/2; + if (angle >= ANGLES) + angle -= ANGLES; + Thrust (angle,-speed); // move backwards + } +} + + +/* +=============== += += T_Player += +=============== +*/ + +void T_Player (objtype *ob) +{ + int angle,speed,scroll; + unsigned text,tilex,tiley; + long lspeed; + + + ControlMovement (ob); + + + // + // firing + // + if (boltsleft) + { + handheight+=(tics<<2); + if (handheight>MAXHANDHEIGHT) + handheight = MAXHANDHEIGHT; + + ContinueBolt (); + lasthand = lasttimecount; + } + else + { + if (c.button0) + { + handheight+=(tics<<2); + if (handheight>MAXHANDHEIGHT) + handheight = MAXHANDHEIGHT; + + if ((unsigned)TimeCount/FIRETIME != lastfiretime) + BuildShotPower (); + lasthand = lasttimecount; + } + else + { + if (lasttimecount > lasthand+HANDPAUSE) + { + handheight-=(tics<<1); + if (handheight<0) + handheight = 0; + } + + if (gamestate.shotpower == MAXSHOTPOWER) + { + lastfiretime = (unsigned)TimeCount/FIRETIME; + BigShoot (); + } + else if (gamestate.shotpower) + { + lastfiretime = (unsigned)TimeCount/FIRETIME; + Shoot (); + } + } + } + + // + // special actions + // + + if ( (Keyboard[sc_Space] || Keyboard[sc_H]) && gamestate.body != MAXBODY) + DrinkPotion (); + + if (Keyboard[sc_B] && !boltsleft) + CastBolt (); + + if ( (Keyboard[sc_Enter] || Keyboard[sc_N]) && TimeCount-lastnuke > NUKETIME) + CastNuke (); + + scroll = LastScan-2; + if ( scroll>=0 && scrollc4z6@IPNZLijctaEK|NU+Pl(vcjYSziVs;B9}=?VIDyzQ zIdQ-Qr%aJh1?8gnK|z5-xDpH$S1OKel9+^G69R!MC<2LbVh|ya;d{L^tCcKiEbS(i zC|as-Ue9#*_qylx>({*#?&;pz=};Yk?#>UjZ`<#v-?_WpQ9!~vEmz=@R!+5PXDEpT zJp&{$re`o0* zcuc*0EdG<>ZH9mGO~5@Iv73(pq(u3X4u2oy$qe4?PB;aXS-S2$I^a=9)<&a?{=0BvUCT-oeUpg_$Yvm zG2F%3Lkx!*dKr!|e2n340NumVy$ttp_I`#3IQwyiqYMu+e1hSV44-0nh~d)=#~B`G zSS;Ka?)Z~S^pd))wN5Q>^){)#7Pn`)iy}h&jbCRACH*@1ymjCY1X>|nXYIrkD;3F9 zQBG7QQmDKVZI(kQD=j6e5Qh^(G4@uLh*`8?#Ojr%C8|tIRGXHlF)gvww8S#g63b0X ztS~LH(zL`X(-NyqOVq+}Z*+2tbQo@RRW%gxK&~lU3Nt3Ezwu_PVhXub=%Q(uMwE)x zsrHT&g_-nXIvr{VdIGGcN$Tis?=mSes>h4>E_68k9{;?mCG#E5rsmKFCE#!dg3bOw z*x^)>@rN4bv2x`Q<{#+Xx!tkAwaqaqlJ}wT)0#LGmSKysZ(sYKt-IU1hhq&ZA^&jC z#&W;JztK^$+3P52b~qE=_E_{8Yn?M1MMm(DyM1dsm1O98PR~Y9lj8Rb z{dD5oeI`1D2%UEzY97`IX@3_=B(P`yD?@lz6Tw66`#O8Nx3xRe&OKejk7AI0X#$fo z(G4b0x6w3p4!+R4r>nDjzr(k4YxkD!om=(}Q{U?kBs8*gCc434^nR~zpVxSe{(p*V z1S)CAB<#)U)q^JrF`hDUgK9+^mS7hz!%-m~qB1I_63Ryt$@FJ%oz5gGwh;XRJ8*FM z6>k&5uBZ?X6?$U3#3Yw4#KpCoR?teyMH>#f46b#l zM7JPU9qCuVr)-j3x)2vv3c}Ge%0U}NbOl^LO6QXpB7cO-?Wt>N09l(M#6zY~9jP<} zO*rW)xE=wQ1G%XYu25JB^B}evLOfhk$VOR|jV9(&AGmy3L@y(^0rmmg2nQ8aav4HA zT()?IFiMX8T0}QV z8z=axsp!|A3UC02_vOZW^Sawq1-)uDgwZ!YphhnIQ$DPoH<{Vs`iqt5DcD5=#t;1NC_g6$W9>ni4Q1?=!@gsUag#&+m#(}mF!#MGEe#qbzd zv}idMgUen-bRIkzm|u8JpeQ%EScoglhrd_|KSfws7J_Sl%}XvtF3cc}2yw8L!~T}Y z`m3n~T#sQ|&wy=QO_BXt&)$&jPzrJIaMeY$7#;(Yi4KDp(+= zda4=3wMMCHRMfR{42^3XROBWPSYQZxihW;dge|hHC>gk{5A+kp^=$ zdK9`?2kFlOMNz$#%llWm{{TXS3K|$vU0&NLF{2KdU%PWL>nLu|8q_ zs`U%jaIz605>5rz?r~NkN z!PHeL7g9=6jwKhRyly#??6dsN^5vwJmeiz2lU@@SMU6Noen`dm1uR9pMPH=j_|vvM zv@17uLU4fIKzvuz0GImidmHY6WD&yTt&`7GiEjECLMV@3+SXy`Y97q)2QV9C%U-vB zfdgq%JaWouT&W?b1R60pT%L6;Mp3!Zh|R}1f;_5~G--6PHaj0aWQsor9uf$j48$q` z#9{-)N(aOu2Snrmgi`||Iszgd17fKFX3GxDkS*s(%%wZghde3gODvFBC=naZ*ZPXJ zr}`Gp>FDnmT%?hSammTFO0N?$jB=?UtodU;s;$ESia?lopr zC>#vA*mvep**MU*T%4)6s54mp0Z=B|R&aiFGMy11DVH7(~r-)Bz7{A#YT zn%EFVH)(c6XKV`-Ky_9#W-n_;v^=pi4@GzUcA7uIbdR2l*s&#=Si|0bfMmzUaboG- zf6!*9ITK9x{sT!nj#wtvxJM6e?Ks!Cdt$hDRuleFKi}3k(sj97qyI4LX7?l&Nq4{lO=3Mn^Q`l$Lj3*duGS%AMY^t#u-W2edktsf3v&w~dM~S!2+y1Dr zVEVW{_575l1akT>RML`=HGUl9?XpF>v;o^?S)!U&i}i?FSBaHkg;*|@iKWhJxf2J@6&1e y9z97<(0A!O^lkbUeUl!iZ_s0OioQ-?!{6X!^105?kB7KB#I3 + Copyright (C) + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/GFXE_C3D.EQU b/GFXE_C3D.EQU new file mode 100644 index 0000000..43f2189 --- /dev/null +++ b/GFXE_C3D.EQU @@ -0,0 +1,327 @@ +;===================================== +; +; Graphics .EQU file for .C3D +; IGRAB-ed on Tue Dec 21 15:06:11 1993 +; +;===================================== + +CP_MAINMENUPIC = 5 +CP_NEWGAMEMENUPIC = 6 +CP_LOADMENUPIC = 7 +CP_SAVEMENUPIC = 8 +CP_CONFIGMENUPIC = 9 +CP_SOUNDMENUPIC = 10 +CP_MUSICMENUPIC = 11 +CP_KEYBOARDMENUPIC = 12 +CP_KEYMOVEMENTPIC = 13 +CP_KEYBUTTONPIC = 14 +CP_JOYSTICKMENUPIC = 15 +CP_OPTIONSMENUPIC = 16 +CP_PADDLEWARPIC = 17 +CP_QUITPIC = 18 +CP_JOYSTICKPIC = 19 +CP_MENUSCREENPIC = 20 +TITLEPIC = 21 +CREDITSPIC = 22 +HIGHSCORESPIC = 23 +FINALEPIC = 24 +STATUSPIC = 25 +SIDEBARSPIC = 26 +SCROLLTOPPIC = 27 +SCROLL1PIC = 28 +SCROLL2PIC = 29 +SCROLL3PIC = 30 +SCROLL4PIC = 31 +SCROLL5PIC = 32 +SCROLL6PIC = 33 +SCROLL7PIC = 34 +SCROLL8PIC = 35 +FIRSTLATCHPIC = 36 +NOSHOTPOWERPIC = 37 +SHOTPOWERPIC = 38 +NOBODYPIC = 39 +BODYPIC = 40 +COMPAS1PIC = 41 +COMPAS2PIC = 42 +COMPAS3PIC = 43 +COMPAS4PIC = 44 +COMPAS5PIC = 45 +COMPAS6PIC = 46 +COMPAS7PIC = 47 +COMPAS8PIC = 48 +COMPAS9PIC = 49 +COMPAS10PIC = 50 +COMPAS11PIC = 51 +COMPAS12PIC = 52 +COMPAS13PIC = 53 +COMPAS14PIC = 54 +COMPAS15PIC = 55 +COMPAS16PIC = 56 +DEADPIC = 57 +FIRSTSCALEPIC = 58 +ORC1PIC = 59 +ORC2PIC = 60 +ORC3PIC = 61 +ORC4PIC = 62 +ORCATTACK1PIC = 63 +ORCATTACK2PIC = 64 +ORCOUCHPIC = 65 +ORCDIE1PIC = 66 +ORCDIE2PIC = 67 +ORCDIE3PIC = 68 +TROLL1PIC = 69 +TROLL2PIC = 70 +TROLL3PIC = 71 +TROLL4PIC = 72 +TROLLOUCHPIC = 73 +TROLLATTACK1PIC = 74 +TROLLATTACK2PIC = 75 +TROLLATTACK3PIC = 76 +TROLLDIE1PIC = 77 +TROLLDIE2PIC = 78 +TROLLDIE3PIC = 79 +WARP1PIC = 80 +WARP2PIC = 81 +WARP3PIC = 82 +WARP4PIC = 83 +BOLTOBJPIC = 84 +BOLTOBJ2PIC = 85 +NUKEOBJPIC = 86 +NUKEOBJ2PIC = 87 +POTIONOBJPIC = 88 +RKEYOBJPIC = 89 +YKEYOBJPIC = 90 +GKEYOBJPIC = 91 +BKEYOBJPIC = 92 +SCROLLOBJPIC = 93 +CHESTOBJPIC = 94 +PSHOT1PIC = 95 +PSHOT2PIC = 96 +BIGPSHOT1PIC = 97 +BIGPSHOT2PIC = 98 +DEMON1PIC = 99 +DEMON2PIC = 100 +DEMON3PIC = 101 +DEMON4PIC = 102 +DEMONATTACK1PIC = 103 +DEMONATTACK2PIC = 104 +DEMONATTACK3PIC = 105 +DEMONOUCHPIC = 106 +DEMONDIE1PIC = 107 +DEMONDIE2PIC = 108 +DEMONDIE3PIC = 109 +MAGE1PIC = 110 +MAGE2PIC = 111 +MAGEOUCHPIC = 112 +MAGEATTACKPIC = 113 +MAGEDIE1PIC = 114 +MAGEDIE2PIC = 115 +BAT1PIC = 116 +BAT2PIC = 117 +BAT3PIC = 118 +BAT4PIC = 119 +BATDIE1PIC = 120 +BATDIE2PIC = 121 +GREL1PIC = 122 +GREL2PIC = 123 +GRELATTACKPIC = 124 +GRELHITPIC = 125 +GRELDIE1PIC = 126 +GRELDIE2PIC = 127 +GRELDIE3PIC = 128 +GRELDIE4PIC = 129 +GRELDIE5PIC = 130 +GRELDIE6PIC = 131 +NEMESISPIC = 132 +FIRSTWALLPIC = 133 +EXPWALL1PIC = 134 +EXPWALL2PIC = 135 +EXPWALL3PIC = 136 +WALL1LPIC = 137 +WALL1DPIC = 138 +WALL2DPIC = 139 +WALL2LPIC = 140 +WALL3DPIC = 141 +WALL3LPIC = 142 +WALL4DPIC = 143 +WALL4LPIC = 144 +WALL5DPIC = 145 +WALL5LPIC = 146 +WALL6DPIC = 147 +WALL6LPIC = 148 +WALL7DPIC = 149 +WALL7LPIC = 150 +RDOOR1PIC = 151 +RDOOR2PIC = 152 +YDOOR1PIC = 153 +YDOOR2PIC = 154 +GDOOR1PIC = 155 +GDOOR2PIC = 156 +BDOOR1PIC = 157 +BDOOR2PIC = 158 +ENTERPLAQUEPIC = 159 + +CP_MENUMASKPICM = 160 +HAND1PICM = 161 +HAND2PICM = 162 + +PADDLESPR = 163 +BALLSPR = 164 +BALL1PIXELTOTHERIGHTSPR = 165 + +LEVEL1TEXT = 456 +LEVEL2TEXT = 457 +LEVEL3TEXT = 458 +LEVEL4TEXT = 459 +LEVEL5TEXT = 460 +LEVEL6TEXT = 461 +LEVEL7TEXT = 462 +LEVEL8TEXT = 463 +LEVEL9TEXT = 464 +LEVEL10TEXT = 465 +LEVEL11TEXT = 466 +LEVEL12TEXT = 467 +LEVEL13TEXT = 468 +LEVEL14TEXT = 469 +LEVEL15TEXT = 470 +LEVEL16TEXT = 471 +LEVEL17TEXT = 472 +LEVEL18TEXT = 473 +LEVEL19TEXT = 474 +LEVEL20TEXT = 475 +OUTOFMEM = 476 +PIRACY = 477 + +CONTROLS_LUMP_START = 5 +CONTROLS_LUMP_END = 20 + +PADDLE_LUMP_START = 163 +PADDLE_LUMP_END = 165 + +ORC_LUMP_START = 59 +ORC_LUMP_END = 68 + +TROLL_LUMP_START = 69 +TROLL_LUMP_END = 79 + +WARP_LUMP_START = 80 +WARP_LUMP_END = 83 + +BOLT_LUMP_START = 84 +BOLT_LUMP_END = 85 + +NUKE_LUMP_START = 86 +NUKE_LUMP_END = 87 + +POTION_LUMP_START = 88 +POTION_LUMP_END = 88 + +RKEY_LUMP_START = 89 +RKEY_LUMP_END = 89 + +YKEY_LUMP_START = 90 +YKEY_LUMP_END = 90 + +GKEY_LUMP_START = 91 +GKEY_LUMP_END = 91 + +BKEY_LUMP_START = 92 +BKEY_LUMP_END = 92 + +SCROLL_LUMP_START = 93 +SCROLL_LUMP_END = 93 + +CHEST_LUMP_START = 94 +CHEST_LUMP_END = 94 + +PLAYER_LUMP_START = 95 +PLAYER_LUMP_END = 98 + +DEMON_LUMP_START = 99 +DEMON_LUMP_END = 109 + +MAGE_LUMP_START = 110 +MAGE_LUMP_END = 115 + +BAT_LUMP_START = 116 +BAT_LUMP_END = 121 + +GREL_LUMP_START = 122 +GREL_LUMP_END = 132 + +EXPWALL_LUMP_START = 134 +EXPWALL_LUMP_END = 136 + +WALL1_LUMP_START = 137 +WALL1_LUMP_END = 138 + +WALL2_LUMP_START = 139 +WALL2_LUMP_END = 140 + +WALL3_LUMP_START = 141 +WALL3_LUMP_END = 142 + +WALL4_LUMP_START = 143 +WALL4_LUMP_END = 144 + +WALL5_LUMP_START = 145 +WALL5_LUMP_END = 146 + +WALL6_LUMP_START = 147 +WALL6_LUMP_END = 148 + +WALL7_LUMP_START = 149 +WALL7_LUMP_END = 150 + +RDOOR_LUMP_START = 151 +RDOOR_LUMP_END = 152 + +YDOOR_LUMP_START = 153 +YDOOR_LUMP_END = 154 + +GDOOR_LUMP_START = 155 +GDOOR_LUMP_END = 156 + +BDOOR_LUMP_START = 157 +BDOOR_LUMP_END = 158 + + +; +; Amount of each data item +; +NUMCHUNKS = 478 +NUMFONT = 2 +NUMFONTM = 0 +NUMPICS = 155 +NUMPICM = 3 +NUMSPRITES = 3 +NUMTILE8 = 108 +NUMTILE8M = 36 +NUMTILE16 = 216 +NUMTILE16M = 72 +NUMTILE32 = 0 +NUMTILE32M = 0 +NUMEXTERN = 22 +; +; File offsets for data items +; +STRUCTPIC = 0 +STRUCTPICM = 1 +STRUCTSPRITE = 2 + +STARTFONT = 3 +STARTFONTM = 5 +STARTPICS = 5 +STARTPICM = 160 +STARTSPRITES = 163 +STARTTILE8 = 166 +STARTTILE8M = 167 +STARTTILE16 = 168 +STARTTILE16M = 384 +STARTTILE32 = 456 +STARTTILE32M = 456 +STARTEXTERN = 456 + +; +; Thank you for using IGRAB! +; diff --git a/GFXE_C3D.H b/GFXE_C3D.H new file mode 100644 index 0000000..7c443e8 --- /dev/null +++ b/GFXE_C3D.H @@ -0,0 +1,353 @@ +/* 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. + */ + +////////////////////////////////////// +// +// Graphics .H file for .C3D +// IGRAB-ed on Tue Dec 21 15:06:10 1993 +// +////////////////////////////////////// + +typedef enum { + // Lump Start + CP_MAINMENUPIC=5, + CP_NEWGAMEMENUPIC, // 6 + CP_LOADMENUPIC, // 7 + CP_SAVEMENUPIC, // 8 + CP_CONFIGMENUPIC, // 9 + CP_SOUNDMENUPIC, // 10 + CP_MUSICMENUPIC, // 11 + CP_KEYBOARDMENUPIC, // 12 + CP_KEYMOVEMENTPIC, // 13 + CP_KEYBUTTONPIC, // 14 + CP_JOYSTICKMENUPIC, // 15 + CP_OPTIONSMENUPIC, // 16 + CP_PADDLEWARPIC, // 17 + CP_QUITPIC, // 18 + CP_JOYSTICKPIC, // 19 + CP_MENUSCREENPIC, // 20 + TITLEPIC, // 21 + CREDITSPIC, // 22 + HIGHSCORESPIC, // 23 + FINALEPIC, // 24 + STATUSPIC, // 25 + SIDEBARSPIC, // 26 + SCROLLTOPPIC, // 27 + SCROLL1PIC, // 28 + SCROLL2PIC, // 29 + SCROLL3PIC, // 30 + SCROLL4PIC, // 31 + SCROLL5PIC, // 32 + SCROLL6PIC, // 33 + SCROLL7PIC, // 34 + SCROLL8PIC, // 35 + FIRSTLATCHPIC, // 36 + NOSHOTPOWERPIC, // 37 + SHOTPOWERPIC, // 38 + NOBODYPIC, // 39 + BODYPIC, // 40 + COMPAS1PIC, // 41 + COMPAS2PIC, // 42 + COMPAS3PIC, // 43 + COMPAS4PIC, // 44 + COMPAS5PIC, // 45 + COMPAS6PIC, // 46 + COMPAS7PIC, // 47 + COMPAS8PIC, // 48 + COMPAS9PIC, // 49 + COMPAS10PIC, // 50 + COMPAS11PIC, // 51 + COMPAS12PIC, // 52 + COMPAS13PIC, // 53 + COMPAS14PIC, // 54 + COMPAS15PIC, // 55 + COMPAS16PIC, // 56 + DEADPIC, // 57 + FIRSTSCALEPIC, // 58 + ORC1PIC, // 59 + ORC2PIC, // 60 + ORC3PIC, // 61 + ORC4PIC, // 62 + ORCATTACK1PIC, // 63 + ORCATTACK2PIC, // 64 + ORCOUCHPIC, // 65 + ORCDIE1PIC, // 66 + ORCDIE2PIC, // 67 + ORCDIE3PIC, // 68 + TROLL1PIC, // 69 + TROLL2PIC, // 70 + TROLL3PIC, // 71 + TROLL4PIC, // 72 + TROLLOUCHPIC, // 73 + TROLLATTACK1PIC, // 74 + TROLLATTACK2PIC, // 75 + TROLLATTACK3PIC, // 76 + TROLLDIE1PIC, // 77 + TROLLDIE2PIC, // 78 + TROLLDIE3PIC, // 79 + WARP1PIC, // 80 + WARP2PIC, // 81 + WARP3PIC, // 82 + WARP4PIC, // 83 + BOLTOBJPIC, // 84 + BOLTOBJ2PIC, // 85 + NUKEOBJPIC, // 86 + NUKEOBJ2PIC, // 87 + POTIONOBJPIC, // 88 + RKEYOBJPIC, // 89 + YKEYOBJPIC, // 90 + GKEYOBJPIC, // 91 + BKEYOBJPIC, // 92 + SCROLLOBJPIC, // 93 + CHESTOBJPIC, // 94 + PSHOT1PIC, // 95 + PSHOT2PIC, // 96 + BIGPSHOT1PIC, // 97 + BIGPSHOT2PIC, // 98 + DEMON1PIC, // 99 + DEMON2PIC, // 100 + DEMON3PIC, // 101 + DEMON4PIC, // 102 + DEMONATTACK1PIC, // 103 + DEMONATTACK2PIC, // 104 + DEMONATTACK3PIC, // 105 + DEMONOUCHPIC, // 106 + DEMONDIE1PIC, // 107 + DEMONDIE2PIC, // 108 + DEMONDIE3PIC, // 109 + MAGE1PIC, // 110 + MAGE2PIC, // 111 + MAGEOUCHPIC, // 112 + MAGEATTACKPIC, // 113 + MAGEDIE1PIC, // 114 + MAGEDIE2PIC, // 115 + BAT1PIC, // 116 + BAT2PIC, // 117 + BAT3PIC, // 118 + BAT4PIC, // 119 + BATDIE1PIC, // 120 + BATDIE2PIC, // 121 + GREL1PIC, // 122 + GREL2PIC, // 123 + GRELATTACKPIC, // 124 + GRELHITPIC, // 125 + GRELDIE1PIC, // 126 + GRELDIE2PIC, // 127 + GRELDIE3PIC, // 128 + GRELDIE4PIC, // 129 + GRELDIE5PIC, // 130 + GRELDIE6PIC, // 131 + NEMESISPIC, // 132 + FIRSTWALLPIC, // 133 + EXPWALL1PIC, // 134 + EXPWALL2PIC, // 135 + EXPWALL3PIC, // 136 + WALL1LPIC, // 137 + WALL1DPIC, // 138 + WALL2DPIC, // 139 + WALL2LPIC, // 140 + WALL3DPIC, // 141 + WALL3LPIC, // 142 + WALL4DPIC, // 143 + WALL4LPIC, // 144 + WALL5DPIC, // 145 + WALL5LPIC, // 146 + WALL6DPIC, // 147 + WALL6LPIC, // 148 + WALL7DPIC, // 149 + WALL7LPIC, // 150 + RDOOR1PIC, // 151 + RDOOR2PIC, // 152 + YDOOR1PIC, // 153 + YDOOR2PIC, // 154 + GDOOR1PIC, // 155 + GDOOR2PIC, // 156 + BDOOR1PIC, // 157 + BDOOR2PIC, // 158 + ENTERPLAQUEPIC, // 159 + + CP_MENUMASKPICM=160, + HAND1PICM, // 161 + HAND2PICM, // 162 + + // Lump Start + PADDLESPR=163, + BALLSPR, // 164 + BALL1PIXELTOTHERIGHTSPR, // 165 + + LEVEL1TEXT=456, + LEVEL2TEXT, // 457 + LEVEL3TEXT, // 458 + LEVEL4TEXT, // 459 + LEVEL5TEXT, // 460 + LEVEL6TEXT, // 461 + LEVEL7TEXT, // 462 + LEVEL8TEXT, // 463 + LEVEL9TEXT, // 464 + LEVEL10TEXT, // 465 + LEVEL11TEXT, // 466 + LEVEL12TEXT, // 467 + LEVEL13TEXT, // 468 + LEVEL14TEXT, // 469 + LEVEL15TEXT, // 470 + LEVEL16TEXT, // 471 + LEVEL17TEXT, // 472 + LEVEL18TEXT, // 473 + LEVEL19TEXT, // 474 + LEVEL20TEXT, // 475 + OUTOFMEM, // 476 + PIRACY, // 477 + ENUMEND + } graphicnums; + +// +// Data LUMPs +// +#define CONTROLS_LUMP_START 5 +#define CONTROLS_LUMP_END 20 + +#define PADDLE_LUMP_START 163 +#define PADDLE_LUMP_END 165 + +#define ORC_LUMP_START 59 +#define ORC_LUMP_END 68 + +#define TROLL_LUMP_START 69 +#define TROLL_LUMP_END 79 + +#define WARP_LUMP_START 80 +#define WARP_LUMP_END 83 + +#define BOLT_LUMP_START 84 +#define BOLT_LUMP_END 85 + +#define NUKE_LUMP_START 86 +#define NUKE_LUMP_END 87 + +#define POTION_LUMP_START 88 +#define POTION_LUMP_END 88 + +#define RKEY_LUMP_START 89 +#define RKEY_LUMP_END 89 + +#define YKEY_LUMP_START 90 +#define YKEY_LUMP_END 90 + +#define GKEY_LUMP_START 91 +#define GKEY_LUMP_END 91 + +#define BKEY_LUMP_START 92 +#define BKEY_LUMP_END 92 + +#define SCROLL_LUMP_START 93 +#define SCROLL_LUMP_END 93 + +#define CHEST_LUMP_START 94 +#define CHEST_LUMP_END 94 + +#define PLAYER_LUMP_START 95 +#define PLAYER_LUMP_END 98 + +#define DEMON_LUMP_START 99 +#define DEMON_LUMP_END 109 + +#define MAGE_LUMP_START 110 +#define MAGE_LUMP_END 115 + +#define BAT_LUMP_START 116 +#define BAT_LUMP_END 121 + +#define GREL_LUMP_START 122 +#define GREL_LUMP_END 132 + +#define EXPWALL_LUMP_START 134 +#define EXPWALL_LUMP_END 136 + +#define WALL1_LUMP_START 137 +#define WALL1_LUMP_END 138 + +#define WALL2_LUMP_START 139 +#define WALL2_LUMP_END 140 + +#define WALL3_LUMP_START 141 +#define WALL3_LUMP_END 142 + +#define WALL4_LUMP_START 143 +#define WALL4_LUMP_END 144 + +#define WALL5_LUMP_START 145 +#define WALL5_LUMP_END 146 + +#define WALL6_LUMP_START 147 +#define WALL6_LUMP_END 148 + +#define WALL7_LUMP_START 149 +#define WALL7_LUMP_END 150 + +#define RDOOR_LUMP_START 151 +#define RDOOR_LUMP_END 152 + +#define YDOOR_LUMP_START 153 +#define YDOOR_LUMP_END 154 + +#define GDOOR_LUMP_START 155 +#define GDOOR_LUMP_END 156 + +#define BDOOR_LUMP_START 157 +#define BDOOR_LUMP_END 158 + + +// +// Amount of each data item +// +#define NUMCHUNKS 478 +#define NUMFONT 2 +#define NUMFONTM 0 +#define NUMPICS 155 +#define NUMPICM 3 +#define NUMSPRITES 3 +#define NUMTILE8 108 +#define NUMTILE8M 36 +#define NUMTILE16 216 +#define NUMTILE16M 72 +#define NUMTILE32 0 +#define NUMTILE32M 0 +#define NUMEXTERNS 22 +// +// File offsets for data items +// +#define STRUCTPIC 0 +#define STRUCTPICM 1 +#define STRUCTSPRITE 2 + +#define STARTFONT 3 +#define STARTFONTM 5 +#define STARTPICS 5 +#define STARTPICM 160 +#define STARTSPRITES 163 +#define STARTTILE8 166 +#define STARTTILE8M 167 +#define STARTTILE16 168 +#define STARTTILE16M 384 +#define STARTTILE32 456 +#define STARTTILE32M 456 +#define STARTEXTERNS 456 + +// +// Thank you for using IGRAB! +// diff --git a/ID_ASM.EQU b/ID_ASM.EQU new file mode 100644 index 0000000..e5110cd --- /dev/null +++ b/ID_ASM.EQU @@ -0,0 +1,114 @@ +; +; Equates for all .ASM files +; + +;---------------------------------------------------------------------------- + +INCLUDE "GFXE_C3D.EQU" + +;---------------------------------------------------------------------------- + +CGAGR = 1 +EGAGR = 2 +VGAGR = 3 + +GRMODE = EGAGR +PROFILE = 0 ; 1=keep stats on tile drawing + +SC_INDEX = 03C4h +SC_RESET = 0 +SC_CLOCK = 1 +SC_MAPMASK = 2 +SC_CHARMAP = 3 +SC_MEMMODE = 4 + +CRTC_INDEX = 03D4h +CRTC_H_TOTAL = 0 +CRTC_H_DISPEND = 1 +CRTC_H_BLANK = 2 +CRTC_H_ENDBLANK = 3 +CRTC_H_RETRACE = 4 +CRTC_H_ENDRETRACE = 5 +CRTC_V_TOTAL = 6 +CRTC_OVERFLOW = 7 +CRTC_ROWSCAN = 8 +CRTC_MAXSCANLINE = 9 +CRTC_CURSORSTART = 10 +CRTC_CURSOREND = 11 +CRTC_STARTHIGH = 12 +CRTC_STARTLOW = 13 +CRTC_CURSORHIGH = 14 +CRTC_CURSORLOW = 15 +CRTC_V_RETRACE = 16 +CRTC_V_ENDRETRACE = 17 +CRTC_V_DISPEND = 18 +CRTC_OFFSET = 19 +CRTC_UNDERLINE = 20 +CRTC_V_BLANK = 21 +CRTC_V_ENDBLANK = 22 +CRTC_MODE = 23 +CRTC_LINECOMPARE = 24 + + +GC_INDEX = 03CEh +GC_SETRESET = 0 +GC_ENABLESETRESET = 1 +GC_COLORCOMPARE = 2 +GC_DATAROTATE = 3 +GC_READMAP = 4 +GC_MODE = 5 +GC_MISCELLANEOUS = 6 +GC_COLORDONTCARE = 7 +GC_BITMASK = 8 + +ATR_INDEX = 03c0h +ATR_MODE = 16 +ATR_OVERSCAN = 17 +ATR_COLORPLANEENABLE = 18 +ATR_PELPAN = 19 +ATR_COLORSELECT = 20 + +STATUS_REGISTER_1 = 03dah + + +MACRO WORDOUT + out dx,ax +ENDM + +if 0 + +MACRO WORDOUT + out dx,al + inc dx + xchg al,ah + out dx,al + dec dx + xchg al,ah +ENDM + +endif + +UPDATEWIDE = 22 +UPDATEHIGH = 13 ; hack for catacombs + +; +; tile info offsets from segment tinf +; + +SPEED = 402 +ANIM = (SPEED+NUMTILE16) + +NORTHWALL = (ANIM+NUMTILE16) +EASTWALL = (NORTHWALL+NUMTILE16M) +SOUTHWALL = (EASTWALL+NUMTILE16M) +WESTWALL = (SOUTHWALL+NUMTILE16M) +MANIM = (WESTWALL+NUMTILE16M) +INTILE = (MANIM+NUMTILE16M) +MSPEED = (INTILE+NUMTILE16M) + +IFE GRMODE-EGAGR +SCREENWIDTH = 40 +ENDIF +IFE GRMODE-CGAGR +SCREENWIDTH = 128 +ENDIF diff --git a/ID_CA.C b/ID_CA.C new file mode 100644 index 0000000..b44bbb5 --- /dev/null +++ b/ID_CA.C @@ -0,0 +1,2133 @@ +/* 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_CA.C + +/* +============================================================================= + +Id Software Caching Manager +--------------------------- + +Must be started BEFORE the memory manager, because it needs to get the headers +loaded into the data segment + +============================================================================= +*/ + +#include "ID_HEADS.H" +#pragma hdrstop +#include "ID_STRS.H" + +#pragma warn -pro +#pragma warn -use + +#define THREEBYTEGRSTARTS + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +typedef struct +{ + unsigned bit0,bit1; // 0-255 is a character, > is a pointer to a node +} huffnode; + + +typedef struct +{ + unsigned RLEWtag; + long headeroffsets[100]; + byte tileinfo[]; +} mapfiletype; + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +byte _seg *tinf; +int mapon; + +unsigned _seg *mapsegs[3]; +maptype _seg *mapheaderseg[NUMMAPS]; +byte _seg *audiosegs[NUMSNDCHUNKS]; +void _seg *grsegs[NUMCHUNKS]; + +byte far grneeded[NUMCHUNKS]; +byte ca_levelbit,ca_levelnum; + +int profilehandle,debughandle; + +void (*drawcachebox) (char *title, unsigned numcache); +void (*updatecachebox) (void); +void (*finishcachebox) (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +extern long far CGAhead; +extern long far EGAhead; +extern byte CGAdict; +extern byte EGAdict; +extern byte far maphead; +extern byte mapdict; +extern byte far audiohead; +extern byte audiodict; + + +long _seg *grstarts; // array of offsets in egagraph, -1 for sparse +long _seg *audiostarts; // array of offsets in audio / audiot + +#ifdef GRHEADERLINKED +huffnode *grhuffman; +#else +huffnode grhuffman[255]; +#endif + +#ifdef AUDIOHEADERLINKED +huffnode *audiohuffman; +#else +huffnode audiohuffman[255]; +#endif + + +int grhandle; // handle to EGAGRAPH +int maphandle; // handle to MAPTEMP / GAMEMAPS +int audiohandle; // handle to AUDIOT / AUDIO + +long chunkcomplen,chunkexplen; + +SDMode oldsoundmode; + + + +void CAL_DialogDraw (char *title,unsigned numcache); +void CAL_DialogUpdate (void); +void CAL_DialogFinish (void); +void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, + unsigned length); + + +#ifdef THREEBYTEGRSTARTS +#define FILEPOSSIZE 3 +//#define GRFILEPOS(c) (*(long far *)(((byte far *)grstarts)+(c)*3)&0xffffff) +long GRFILEPOS(int c) +{ + long value; + int offset; + + offset = c*3; + + value = *(long far *)(((byte far *)grstarts)+offset); + + value &= 0x00ffffffl; + + if (value == 0xffffffl) + value = -1; + + return value; +}; +#else +#define FILEPOSSIZE 4 +#define GRFILEPOS(c) (grstarts[c]) +#endif + +/* +============================================================================= + + LOW LEVEL ROUTINES + +============================================================================= +*/ + +/* +============================ += += CA_OpenDebug / CA_CloseDebug += += Opens a binary file with the handle "debughandle" += +============================ +*/ + +void CA_OpenDebug (void) +{ + unlink ("DEBUG.TXT"); + debughandle = open("DEBUG.TXT", O_CREAT | O_WRONLY | O_TEXT); +} + +void CA_CloseDebug (void) +{ + close (debughandle); +} + + + +/* +============================ += += CAL_GetGrChunkLength += += Gets the length of an explicit length chunk (not tiles) += The file pointer is positioned so the compressed data can be read in next. += +============================ +*/ + +void CAL_GetGrChunkLength (int chunk) +{ + lseek(grhandle,GRFILEPOS(chunk),SEEK_SET); + read(grhandle,&chunkexplen,sizeof(chunkexplen)); + chunkcomplen = GRFILEPOS(chunk+1)-GRFILEPOS(chunk)-4; +} + + +/* +========================== += += CA_FarRead += += Read from a file to a far pointer += +========================== +*/ + +boolean CA_FarRead (int handle, byte far *dest, long length) +{ + if (length>0xffffl) + Quit ("CA_FarRead doesn't support 64K reads yet!"); + +asm push ds +asm mov bx,[handle] +asm mov cx,[WORD PTR length] +asm mov dx,[WORD PTR dest] +asm mov ds,[WORD PTR dest+2] +asm mov ah,0x3f // READ w/handle +asm int 21h +asm pop ds +asm jnc good + errno = _AX; + return false; +good: +asm cmp ax,[WORD PTR length] +asm je done + errno = EINVFMT; // user manager knows this is bad read + return false; +done: + return true; +} + + +/* +========================== += += CA_SegWrite += += Write from a file to a far pointer += +========================== +*/ + +boolean CA_FarWrite (int handle, byte far *source, long length) +{ + if (length>0xffffl) + Quit ("CA_FarWrite doesn't support 64K reads yet!"); + +asm push ds +asm mov bx,[handle] +asm mov cx,[WORD PTR length] +asm mov dx,[WORD PTR source] +asm mov ds,[WORD PTR source+2] +asm mov ah,0x40 // WRITE w/handle +asm int 21h +asm pop ds +asm jnc good + errno = _AX; + return false; +good: +asm cmp ax,[WORD PTR length] +asm je done + errno = ENOMEM; // user manager knows this is bad write + return false; + +done: + return true; +} + + +/* +========================== += += CA_ReadFile += += Reads a file into an allready allocated buffer += +========================== +*/ + +boolean CA_ReadFile (char *filename, memptr *ptr) +{ + int handle; + long size; + + if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1) + return false; + + size = filelength (handle); + if (!CA_FarRead (handle,*ptr,size)) + { + close (handle); + return false; + } + close (handle); + return true; +} + + + +/* +========================== += += CA_LoadFile += += Allocate space for and load a file += +========================== +*/ + +boolean CA_LoadFile (char *filename, memptr *ptr) +{ + int handle; + long size; + + if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1) + return false; + + size = filelength (handle); + MM_GetPtr (ptr,size); + if (!CA_FarRead (handle,*ptr,size)) + { + close (handle); + return false; + } + close (handle); + return true; +} + +/* +============================================================================ + + COMPRESSION routines, see JHUFF.C for more + +============================================================================ +*/ + + + +/* +=============== += += CAL_OptimizeNodes += += Goes through a huffman table and changes the 256-511 node numbers to the += actular address of the node. Must be called before CAL_HuffExpand += +=============== +*/ + +void CAL_OptimizeNodes (huffnode *table) +{ + huffnode *node; + int i; + + node = table; + + for (i=0;i<255;i++) + { + if (node->bit0 >= 256) + node->bit0 = (unsigned)(table+(node->bit0-256)); + if (node->bit1 >= 256) + node->bit1 = (unsigned)(table+(node->bit1-256)); + node++; + } +} + + + +/* +====================== += += CAL_HuffExpand += += Length is the length of the EXPANDED data += +====================== +*/ + +void CAL_HuffExpand (byte huge *source, byte huge *dest, + long length,huffnode *hufftable) +{ +// unsigned bit,byte,node,code; + unsigned sourceseg,sourceoff,destseg,destoff,endoff; + huffnode *headptr; +// huffnode *nodeon; + + headptr = hufftable+254; // head node is allways node 254 + + source++; // normalize + source--; + dest++; + dest--; + + sourceseg = FP_SEG(source); + sourceoff = FP_OFF(source); + destseg = FP_SEG(dest); + destoff = FP_OFF(dest); + endoff = destoff+length; + +// +// ds:si source +// es:di dest +// ss:bx node pointer +// + + if (length <0xfff0) + { + +//-------------------------- +// expand less than 64k of data +//-------------------------- + +asm mov bx,[headptr] + +asm mov si,[sourceoff] +asm mov di,[destoff] +asm mov es,[destseg] +asm mov ds,[sourceseg] +asm mov ax,[endoff] + +asm mov ch,[si] // load first byte +asm inc si +asm mov cl,1 + +expandshort: +asm test ch,cl // bit set? +asm jnz bit1short +asm mov dx,[ss:bx] // take bit0 path from node +asm shl cl,1 // advance to next bit position +asm jc newbyteshort +asm jnc sourceupshort + +bit1short: +asm mov dx,[ss:bx+2] // take bit1 path +asm shl cl,1 // advance to next bit position +asm jnc sourceupshort + +newbyteshort: +asm mov ch,[si] // load next byte +asm inc si +asm mov cl,1 // back to first bit + +sourceupshort: +asm or dh,dh // if dx<256 its a byte, else move node +asm jz storebyteshort +asm mov bx,dx // next node = (huffnode *)code +asm jmp expandshort + +storebyteshort: +asm mov [es:di],dl +asm inc di // write a decopmpressed byte out +asm mov bx,[headptr] // back to the head node for next bit + +asm cmp di,ax // done? +asm jne expandshort + } + else + { + +//-------------------------- +// expand more than 64k of data +//-------------------------- + + length--; + +asm mov bx,[headptr] +asm mov cl,1 + +asm mov si,[sourceoff] +asm mov di,[destoff] +asm mov es,[destseg] +asm mov ds,[sourceseg] + +asm lodsb // load first byte + +expand: +asm test al,cl // bit set? +asm jnz bit1 +asm mov dx,[ss:bx] // take bit0 path from node +asm jmp gotcode +bit1: +asm mov dx,[ss:bx+2] // take bit1 path + +gotcode: +asm shl cl,1 // advance to next bit position +asm jnc sourceup +asm lodsb +asm cmp si,0x10 // normalize ds:si +asm jb sinorm +asm mov cx,ds +asm inc cx +asm mov ds,cx +asm xor si,si +sinorm: +asm mov cl,1 // back to first bit + +sourceup: +asm or dh,dh // if dx<256 its a byte, else move node +asm jz storebyte +asm mov bx,dx // next node = (huffnode *)code +asm jmp expand + +storebyte: +asm mov [es:di],dl +asm inc di // write a decopmpressed byte out +asm mov bx,[headptr] // back to the head node for next bit + +asm cmp di,0x10 // normalize es:di +asm jb dinorm +asm mov dx,es +asm inc dx +asm mov es,dx +asm xor di,di +dinorm: + +asm sub [WORD PTR ss:length],1 +asm jnc expand +asm dec [WORD PTR ss:length+2] +asm jns expand // when length = ffff ffff, done + + } + +asm mov ax,ss +asm mov ds,ax + +} + + +/* +====================== += += CAL_CarmackExpand += += Length is the length of the EXPANDED data += +====================== +*/ + +#define NEARTAG 0xa7 +#define FARTAG 0xa8 + +void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, unsigned length) +{ + unsigned ch,chhigh,count,offset; + unsigned far *copyptr, far *inptr, far *outptr; + + length/=2; + + inptr = source; + outptr = dest; + + while (length) + { + ch = *inptr++; + chhigh = ch>>8; + if (chhigh == NEARTAG) + { + count = ch&0xff; + if (!count) + { // have to insert a word containing the tag byte + ch |= *((unsigned char far *)inptr)++; + *outptr++ = ch; + length--; + } + else + { + offset = *((unsigned char far *)inptr)++; + copyptr = outptr - offset; + length -= count; + while (count--) + *outptr++ = *copyptr++; + } + } + else if (chhigh == FARTAG) + { + count = ch&0xff; + if (!count) + { // have to insert a word containing the tag byte + ch |= *((unsigned char far *)inptr)++; + *outptr++ = ch; + length --; + } + else + { + offset = *inptr++; + copyptr = dest + offset; + length -= count; + while (count--) + *outptr++ = *copyptr++; + } + } + else + { + *outptr++ = ch; + length --; + } + } +} + + + +/* +====================== += += CA_RLEWcompress += +====================== +*/ + +long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest, + unsigned rlewtag) +{ + long complength; + unsigned value,count,i; + unsigned huge *start,huge *end; + + start = dest; + + end = source + (length+1)/2; + +// +// compress it +// + do + { + count = 1; + value = *source++; + while (*source == value && source3 || value == rlewtag) + { + // + // send a tag / count / value string + // + *dest++ = rlewtag; + *dest++ = count; + *dest++ = value; + } + else + { + // + // send word without compressing + // + for (i=1;i<=count;i++) + *dest++ = value; + } + + } while (source0 + MM_GetPtr(&(memptr)pictable,NUMPICS*sizeof(pictabletype)); + CAL_GetGrChunkLength(STRUCTPIC); // position file pointer + MM_GetPtr(&compseg,chunkcomplen); + CA_FarRead (grhandle,compseg,chunkcomplen); + CAL_HuffExpand (compseg, (byte huge *)pictable,NUMPICS*sizeof(pictabletype),grhuffman); + MM_FreePtr(&compseg); +#endif + +#if NUMPICM>0 + MM_GetPtr(&(memptr)picmtable,NUMPICM*sizeof(pictabletype)); + CAL_GetGrChunkLength(STRUCTPICM); // position file pointer + MM_GetPtr(&compseg,chunkcomplen); + CA_FarRead (grhandle,compseg,chunkcomplen); + CAL_HuffExpand (compseg, (byte huge *)picmtable,NUMPICS*sizeof(pictabletype),grhuffman); + MM_FreePtr(&compseg); +#endif + +#if NUMSPRITES>0 + MM_GetPtr(&(memptr)spritetable,NUMSPRITES*sizeof(spritetabletype)); + CAL_GetGrChunkLength(STRUCTSPRITE); // position file pointer + MM_GetPtr(&compseg,chunkcomplen); + CA_FarRead (grhandle,compseg,chunkcomplen); + CAL_HuffExpand (compseg, (byte huge *)spritetable,NUMSPRITES*sizeof(spritetabletype),grhuffman); + MM_FreePtr(&compseg); +#endif + +} + +//========================================================================== + + +/* +====================== += += CAL_SetupMapFile += +====================== +*/ + +void CAL_SetupMapFile (void) +{ + int handle; + long length; + +// +// load maphead.ext (offsets and tileinfo for map file) +// +#ifndef MAPHEADERLINKED + if ((handle = open("MAPHEAD."EXTENSION, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open MAPHEAD."EXTENSION"!"); + length = filelength(handle); + MM_GetPtr (&(memptr)tinf,length); + CA_FarRead(handle, tinf, length); + close(handle); +#else + + tinf = (byte _seg *)FP_SEG(&maphead); + +#endif + +// +// open the data file +// +#ifdef MAPHEADERLINKED + if ((maphandle = open("GAMEMAPS."EXTENSION, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open GAMEMAPS."EXTENSION"!"); +#else + if ((maphandle = open("MAPTEMP."EXTENSION, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open MAPTEMP."EXTENSION"!"); +#endif +} + +//========================================================================== + + +/* +====================== += += CAL_SetupAudioFile += +====================== +*/ + +void CAL_SetupAudioFile (void) +{ + int handle; + long length; + +// +// load maphead.ext (offsets and tileinfo for map file) +// +#ifndef AUDIOHEADERLINKED + if ((handle = open("AUDIOHED."EXTENSION, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open AUDIOHED."EXTENSION"!"); + length = filelength(handle); + MM_GetPtr (&(memptr)audiostarts,length); + CA_FarRead(handle, (byte far *)audiostarts, length); + close(handle); +#else + audiohuffman = (huffnode *)&audiodict; + CAL_OptimizeNodes (audiohuffman); + audiostarts = (long _seg *)FP_SEG(&audiohead); +#endif + +// +// open the data file +// +#ifndef AUDIOHEADERLINKED + if ((audiohandle = open("AUDIOT."EXTENSION, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open AUDIOT."EXTENSION"!"); +#else + if ((audiohandle = open("AUDIO."EXTENSION, + O_RDONLY | O_BINARY, S_IREAD)) == -1) + Quit ("Can't open AUDIO."EXTENSION"!"); +#endif +} + +//========================================================================== + + +/* +====================== += += CA_Startup += += Open all files and load in headers += +====================== +*/ + +void CA_Startup (void) +{ +#ifdef PROFILE + unlink ("PROFILE.TXT"); + profilehandle = open("PROFILE.TXT", O_CREAT | O_WRONLY | O_TEXT); +#endif + +#ifndef NOMAPS + CAL_SetupMapFile (); +#endif +#ifndef NOGRAPHICS + CAL_SetupGrFile (); +#endif +#ifndef NOAUDIO + CAL_SetupAudioFile (); +#endif + + mapon = -1; + ca_levelbit = 1; + ca_levelnum = 0; + + drawcachebox = CAL_DialogDraw; + updatecachebox = CAL_DialogUpdate; + finishcachebox = CAL_DialogFinish; +} + +//========================================================================== + + +/* +====================== += += CA_Shutdown += += Closes all files += +====================== +*/ + +void CA_Shutdown (void) +{ +#ifdef PROFILE + close (profilehandle); +#endif + + close (maphandle); + close (grhandle); + close (audiohandle); +} + +//=========================================================================== + +/* +====================== += += CA_CacheAudioChunk += +====================== +*/ + +void CA_CacheAudioChunk (int chunk) +{ + long pos,compressed; +#ifdef AUDIOHEADERLINKED + long expanded; + memptr bigbufferseg; + byte far *source; +#endif + + if (audiosegs[chunk]) + { + MM_SetPurge (&(memptr)audiosegs[chunk],0); + return; // allready in memory + } + +// +// load the chunk into a buffer, either the miscbuffer if it fits, or allocate +// a larger buffer +// + pos = audiostarts[chunk]; + compressed = audiostarts[chunk+1]-pos; + + lseek(audiohandle,pos,SEEK_SET); + +#ifndef AUDIOHEADERLINKED + + MM_GetPtr (&(memptr)audiosegs[chunk],compressed); + if (mmerror) + return; + + CA_FarRead(audiohandle,audiosegs[chunk],compressed); + +#else + + if (compressed<=BUFFERSIZE) + { + CA_FarRead(audiohandle,bufferseg,compressed); + source = bufferseg; + } + else + { + MM_GetPtr(&bigbufferseg,compressed); + if (mmerror) + return; + MM_SetLock (&bigbufferseg,true); + CA_FarRead(audiohandle,bigbufferseg,compressed); + source = bigbufferseg; + } + + expanded = *(long far *)source; + source += 4; // skip over length + MM_GetPtr (&(memptr)audiosegs[chunk],expanded); + if (mmerror) + goto done; + CAL_HuffExpand (source,audiosegs[chunk],expanded,audiohuffman); + +done: + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); +#endif +} + +//=========================================================================== + +/* +====================== += += CA_LoadAllSounds += += Purges all sounds, then loads all new ones (mode switch) += +====================== +*/ + +void CA_LoadAllSounds (void) +{ + unsigned start,i; + + switch (oldsoundmode) + { + case sdm_Off: + goto cachein; + case sdm_PC: + start = STARTPCSOUNDS; + break; + case sdm_AdLib: + start = STARTADLIBSOUNDS; + break; + } + + for (i=0;iwidth*spr->height; + MM_GetPtr (&grsegs[chunk],smallplane*2+MAXSHIFTS*6); + if (mmerror) + return; + dest = (spritetype _seg *)grsegs[chunk]; + dest->sourceoffset[0] = MAXSHIFTS*6; // start data after 3 unsigned tables + dest->planesize[0] = smallplane; + dest->width[0] = spr->width; + +// +// expand the unshifted shape +// + CAL_HuffExpand (compressed, &dest->data[0],smallplane*2,grhuffman); + +#endif + + +#if GRMODE == EGAGR + +// +// calculate sizes +// + spr = &spritetable[chunk-STARTSPRITES]; + smallplane = spr->width*spr->height; + bigplane = (spr->width+1)*spr->height; + + shiftstarts[0] = MAXSHIFTS*6; // start data after 3 unsigned tables + shiftstarts[1] = shiftstarts[0] + smallplane*5; // 5 planes in a sprite + shiftstarts[2] = shiftstarts[1] + bigplane*5; + shiftstarts[3] = shiftstarts[2] + bigplane*5; + shiftstarts[4] = shiftstarts[3] + bigplane*5; // nothing ever put here + + expanded = shiftstarts[spr->shifts]; + MM_GetPtr (&grsegs[chunk],expanded); + if (mmerror) + return; + dest = (spritetype _seg *)grsegs[chunk]; + +// +// expand the unshifted shape +// + CAL_HuffExpand (compressed, &dest->data[0],smallplane*5,grhuffman); + +// +// make the shifts! +// + switch (spr->shifts) + { + case 1: + for (i=0;i<4;i++) + { + dest->sourceoffset[i] = shiftstarts[0]; + dest->planesize[i] = smallplane; + dest->width[i] = spr->width; + } + break; + + case 2: + for (i=0;i<2;i++) + { + dest->sourceoffset[i] = shiftstarts[0]; + dest->planesize[i] = smallplane; + dest->width[i] = spr->width; + } + for (i=2;i<4;i++) + { + dest->sourceoffset[i] = shiftstarts[1]; + dest->planesize[i] = bigplane; + dest->width[i] = spr->width+1; + } + CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0], + dest->sourceoffset[2],spr->width,spr->height,4); + break; + + case 4: + dest->sourceoffset[0] = shiftstarts[0]; + dest->planesize[0] = smallplane; + dest->width[0] = spr->width; + + dest->sourceoffset[1] = shiftstarts[1]; + dest->planesize[1] = bigplane; + dest->width[1] = spr->width+1; + CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0], + dest->sourceoffset[1],spr->width,spr->height,2); + + dest->sourceoffset[2] = shiftstarts[2]; + dest->planesize[2] = bigplane; + dest->width[2] = spr->width+1; + CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0], + dest->sourceoffset[2],spr->width,spr->height,4); + + dest->sourceoffset[3] = shiftstarts[3]; + dest->planesize[3] = bigplane; + dest->width[3] = spr->width+1; + CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0], + dest->sourceoffset[3],spr->width,spr->height,6); + + break; + + default: + Quit ("CAL_CacheSprite: Bad shifts number!"); + } + +#endif +} + +//=========================================================================== + + +/* +====================== += += CAL_ExpandGrChunk += += Does whatever is needed with a pointer to a compressed chunk += +====================== +*/ + +void CAL_ExpandGrChunk (int chunk, byte far *source) +{ + long expanded; + + + if (chunk >= STARTTILE8 && chunk < STARTEXTERNS) + { + // + // expanded sizes of tile8/16/32 are implicit + // + +#if GRMODE == EGAGR +#define BLOCK 32 +#define MASKBLOCK 40 +#endif + +#if GRMODE == CGAGR +#define BLOCK 16 +#define MASKBLOCK 32 +#endif + + if (chunk=STARTSPRITES && chunk< STARTTILE8) + CAL_CacheSprite(chunk,source); + else + { + MM_GetPtr (&grsegs[chunk],expanded); + if (mmerror) + return; + CAL_HuffExpand (source,grsegs[chunk],expanded,grhuffman); + } +} + + +/* +====================== += += CAL_ReadGrChunk += += Gets a chunk off disk, optimizing reads to general buffer += +====================== +*/ + +void CAL_ReadGrChunk (int chunk) +{ + long pos,compressed; + memptr bigbufferseg; + byte far *source; + int next; + +// +// load the chunk into a buffer, either the miscbuffer if it fits, or allocate +// a larger buffer +// + pos = GRFILEPOS(chunk); + if (pos<0) // $FFFFFFFF start is a sparse tile + return; + + next = chunk +1; + while (GRFILEPOS(next) == -1) // skip past any sparse tiles + next++; + + compressed = GRFILEPOS(next)-pos; + + lseek(grhandle,pos,SEEK_SET); + + if (compressed<=BUFFERSIZE) + { + CA_FarRead(grhandle,bufferseg,compressed); + source = bufferseg; + } + else + { + MM_GetPtr(&bigbufferseg,compressed); + if (mmerror) + return; + MM_SetLock (&bigbufferseg,true); + CA_FarRead(grhandle,bigbufferseg,compressed); + source = bigbufferseg; + } + + CAL_ExpandGrChunk (chunk,source); + + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); +} + + +/* +====================== += += CA_CacheGrChunk += += Makes sure a given chunk is in memory, loadiing it if needed += +====================== +*/ + +void CA_CacheGrChunk (int chunk) +{ + long pos,compressed; + memptr bigbufferseg; + byte far *source; + int next; + + grneeded[chunk] |= ca_levelbit; // make sure it doesn't get removed + if (grsegs[chunk]) + { + MM_SetPurge (&grsegs[chunk],0); + return; // allready in memory + } + +// +// load the chunk into a buffer, either the miscbuffer if it fits, or allocate +// a larger buffer +// + pos = GRFILEPOS(chunk); + if (pos<0) // $FFFFFFFF start is a sparse tile + return; + + next = chunk +1; + while (GRFILEPOS(next) == -1) // skip past any sparse tiles + next++; + + compressed = GRFILEPOS(next)-pos; + + lseek(grhandle,pos,SEEK_SET); + + if (compressed<=BUFFERSIZE) + { + CA_FarRead(grhandle,bufferseg,compressed); + source = bufferseg; + } + else + { + MM_GetPtr(&bigbufferseg,compressed); + MM_SetLock (&bigbufferseg,true); + CA_FarRead(grhandle,bigbufferseg,compressed); + source = bigbufferseg; + } + + CAL_ExpandGrChunk (chunk,source); + + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); +} + + + +//========================================================================== + +/* +====================== += += CA_CacheMap += +====================== +*/ + +void CA_CacheMap (int mapnum) +{ + long pos,compressed; + int plane; + memptr *dest,bigbufferseg; + unsigned size; + unsigned far *source; +#ifdef MAPHEADERLINKED + memptr buffer2seg; + long expanded; +#endif + + +// +// free up memory from last map +// + if (mapon>-1 && mapheaderseg[mapon]) + MM_SetPurge (&(memptr)mapheaderseg[mapon],3); + for (plane=0;planeheaderoffsets[mapnum]; + if (pos<0) // $FFFFFFFF start is a sparse map + Quit ("CA_CacheMap: Tried to load a non existent map!"); + + MM_GetPtr(&(memptr)mapheaderseg[mapnum],sizeof(maptype)); + lseek(maphandle,pos,SEEK_SET); + CA_FarRead (maphandle,(memptr)mapheaderseg[mapnum],sizeof(maptype)); + } + else + MM_SetPurge (&(memptr)mapheaderseg[mapnum],0); + +// +// load the planes in +// If a plane's pointer still exists it will be overwritten (levels are +// allways reloaded, never cached) +// + + size = mapheaderseg[mapnum]->width * mapheaderseg[mapnum]->height * 2; + + for (plane = 0; planeplanestart[plane]; + compressed = mapheaderseg[mapnum]->planelength[plane]; + + if (!compressed) + continue; // the plane is not used in this game + + dest = &(memptr)mapsegs[plane]; + MM_GetPtr(dest,size); + + lseek(maphandle,pos,SEEK_SET); + if (compressed<=BUFFERSIZE) + source = bufferseg; + else + { + MM_GetPtr(&bigbufferseg,compressed); + MM_SetLock (&bigbufferseg,true); + source = bigbufferseg; + } + + CA_FarRead(maphandle,(byte far *)source,compressed); +#ifdef MAPHEADERLINKED + // + // unhuffman, then unRLEW + // The huffman'd chunk has a two byte expanded length first + // The resulting RLEW chunk also does, even though it's not really + // needed + // + expanded = *source; + source++; + MM_GetPtr (&buffer2seg,expanded); + CAL_CarmackExpand (source, (unsigned far *)buffer2seg,expanded); + CA_RLEWexpand (((unsigned far *)buffer2seg)+1,*dest,size, + ((mapfiletype _seg *)tinf)->RLEWtag); + MM_FreePtr (&buffer2seg); + +#else + // + // unRLEW, skipping expanded length + // + CA_RLEWexpand (source+1, *dest,size, + ((mapfiletype _seg *)tinf)->RLEWtag); +#endif + + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); + } +} + +//=========================================================================== + +/* +====================== += += CA_UpLevel += += Goes up a bit level in the needed lists and clears it out. += Everything is made purgable += +====================== +*/ + +void CA_UpLevel (void) +{ + if (ca_levelnum==7) + Quit ("CA_UpLevel: Up past level 7!"); + + ca_levelbit<<=1; + ca_levelnum++; +} + +//=========================================================================== + +/* +====================== += += CA_DownLevel += += Goes down a bit level in the needed lists and recaches += everything from the lower level += +====================== +*/ + +void CA_DownLevel (void) +{ + if (!ca_levelnum) + Quit ("CA_DownLevel: Down past level 0!"); + ca_levelbit>>=1; + ca_levelnum--; + CA_CacheMarks(NULL); +} + +//=========================================================================== + +/* +====================== += += CA_ClearMarks += += Clears out all the marks at the current level += +====================== +*/ + +void CA_ClearMarks (void) +{ + int i; + + for (i=0;i>16; + if (xh - lastx > BARSTEP) + { + for (x=lastx;x<=xh;x++) +#if GRMODE == EGAGR + VWB_Vlin (thy,thy+13,x,14); +#endif +#if GRMODE == CGAGR + VWB_Vlin (thy,thy+13,x,SECONDCOLOR); +#endif + lastx = xh; + VW_UpdateScreen(); + } +} + +/* +====================== += += CAL_DialogFinish += +====================== +*/ + +void CAL_DialogFinish (void) +{ + unsigned x,xh; + + xh = thx + NUMBARS; + for (x=lastx;x<=xh;x++) +#if GRMODE == EGAGR + VWB_Vlin (thy,thy+13,x,14); +#endif +#if GRMODE == CGAGR + VWB_Vlin (thy,thy+13,x,SECONDCOLOR); +#endif + VW_UpdateScreen(); + +} + +//=========================================================================== + +/* +====================== += += CA_CacheMarks += +====================== +*/ +#define MAXEMPTYREAD 1024 + +void CA_CacheMarks (char *title) +{ + boolean dialog; + int i,next,numcache; + long pos,endpos,nextpos,nextendpos,compressed; + long bufferstart,bufferend; // file position of general buffer + byte far *source; + memptr bigbufferseg; + + dialog = (title!=NULL); + + numcache = 0; +// +// go through and make everything not needed purgable +// + for (i=0;i= endpos) + { + // data is allready in buffer + source = (byte _seg *)bufferseg+(pos-bufferstart); + } + else + { + // load buffer with a new block from disk + // try to get as many of the needed blocks in as possible + while ( next < NUMCHUNKS ) + { + while (next < NUMCHUNKS && + !(grneeded[next]&ca_levelbit && !grsegs[next])) + next++; + if (next == NUMCHUNKS) + continue; + + nextpos = GRFILEPOS(next); + while (GRFILEPOS(++next) == -1) // skip past any sparse tiles + ; + nextendpos = GRFILEPOS(next); + if (nextpos - endpos <= MAXEMPTYREAD + && nextendpos-pos <= BUFFERSIZE) + endpos = nextendpos; + else + next = NUMCHUNKS; // read pos to posend + } + + lseek(grhandle,pos,SEEK_SET); + CA_FarRead(grhandle,bufferseg,endpos-pos); + bufferstart = pos; + bufferend = endpos; + source = bufferseg; + } + } + else + { + // big chunk, allocate temporary buffer + MM_GetPtr(&bigbufferseg,compressed); + if (mmerror) + return; + MM_SetLock (&bigbufferseg,true); + lseek(grhandle,pos,SEEK_SET); + CA_FarRead(grhandle,bigbufferseg,compressed); + source = bigbufferseg; + } + + CAL_ExpandGrChunk (i,source); + if (mmerror) + return; + + if (compressed>BUFFERSIZE) + MM_FreePtr(&bigbufferseg); + + } + +// +// finish up any thermometer remnants +// + if (dialog && finishcachebox) + finishcachebox(); +} + diff --git a/ID_CA.H b/ID_CA.H new file mode 100644 index 0000000..23007aa --- /dev/null +++ b/ID_CA.H @@ -0,0 +1,124 @@ +/* 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_CA.H + +#ifndef __TYPES__ +#include "ID_TYPES.H" +#endif + +#ifndef __ID_MM__ +#include "ID_MM.H" +#endif + +#ifndef __ID_GLOB__ +#include "ID_GLOB.H" +#endif + +#define __ID_CA__ + +//=========================================================================== + +//#define NOMAPS +//#define NOGRAPHICS +//#define NOAUDIO + +#define MAPHEADERLINKED +#define GRHEADERLINKED +#define AUDIOHEADERLINKED + +#define NUMMAPS 30 +#define MAPPLANES 3 + +//=========================================================================== + +typedef struct +{ + long planestart[3]; + unsigned planelength[3]; + unsigned width,height; + char name[16]; +} maptype; + +//=========================================================================== + +extern byte _seg *tinf; +extern int mapon; + +extern unsigned _seg *mapsegs[3]; +extern maptype _seg *mapheaderseg[NUMMAPS]; +extern byte _seg *audiosegs[NUMSNDCHUNKS]; +extern void _seg *grsegs[NUMCHUNKS]; + +extern byte far grneeded[NUMCHUNKS]; +extern byte ca_levelbit,ca_levelnum; + +extern char *titleptr[8]; + +extern int profilehandle,debughandle; + +// +// hooks for custom cache dialogs +// +extern void (*drawcachebox) (char *title, unsigned numcache); +extern void (*updatecachebox) (void); +extern void (*finishcachebox) (void); + +//=========================================================================== + +// just for the score box reshifting + +void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest, + unsigned width, unsigned height, unsigned pixshift); + +//=========================================================================== + +void CA_OpenDebug (void); +void CA_CloseDebug (void); +boolean CA_FarRead (int handle, byte far *dest, long length); +boolean CA_FarWrite (int handle, byte far *source, long length); +boolean CA_ReadFile (char *filename, memptr *ptr); +boolean CA_LoadFile (char *filename, memptr *ptr); + +long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest, + unsigned rlewtag); + +void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length, + unsigned rlewtag); + +void CA_Startup (void); +void CA_Shutdown (void); + +void CA_CacheAudioChunk (int chunk); +void CA_LoadAllSounds (void); + +void CA_UpLevel (void); +void CA_DownLevel (void); + +void CA_SetAllPurge (void); + +void CA_ClearMarks (void); +void CA_ClearAllMarks (void); + +#define CA_MarkGrChunk(chunk) grneeded[chunk]|=ca_levelbit + +void CA_CacheGrChunk (int chunk); +void CA_CacheMap (int mapnum); + +void CA_CacheMarks (char *title); + diff --git a/ID_HEADS.H b/ID_HEADS.H new file mode 100644 index 0000000..622c069 --- /dev/null +++ b/ID_HEADS.H @@ -0,0 +1,124 @@ +/* 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_GLOB.H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __ID_GLOB__ + +//-------------------------------------------------------------------------- + +#define EXTENSION "C3D" + +extern char far introscn; + +#include "GFXE_C3D.H" +#include "AUDIOC3D.H" + +//-------------------------------------------------------------------------- + +#define CAT3D + +#define TEXTGR 0 +#define CGAGR 1 +#define EGAGR 2 +#define VGAGR 3 + +#define GRMODE EGAGR + +#if GRMODE == EGAGR +#define GREXT "EGA" +#endif +#if GRMODE == CGAGR +#define GREXT "CGA" +#endif + +//#define PROFILE + +// +// ID Engine +// Types.h - Generic types, #defines, etc. +// v1.0d1 +// + +#ifndef __TYPES__ +#define __TYPES__ + +typedef enum {false,true} boolean; +typedef unsigned char byte; +typedef unsigned int word; +typedef unsigned long longword; +typedef byte * Ptr; + +typedef struct + { + int x,y; + } Point; +typedef struct + { + Point ul,lr; + } Rect; + +#define nil ((void *)0) + +#endif + +#include "ID_MM.H" +#include "ID_CA.H" +#include "ID_VW.H" +#include "ID_IN.H" +#include "ID_SD.H" +#include "ID_US.H" + + +void Quit (char *error); // defined in user program + +// +// replacing refresh manager with custom routines +// + +#define PORTTILESWIDE 21 // all drawing takes place inside a +#define PORTTILESHIGH 14 // non displayed port of this size + +#define UPDATEWIDE (PORTTILESWIDE+1) +#define UPDATEHIGH PORTTILESHIGH + +#define MAXTICS 6 +#define DEMOTICS 3 + +#define UPDATETERMINATE 0x0301 + +extern unsigned mapwidth,mapheight,tics; +extern boolean compatability; + +extern byte *updateptr; +extern unsigned uwidthtable[UPDATEHIGH]; +extern unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; diff --git a/ID_IN.C b/ID_IN.C new file mode 100644 index 0000000..b78ca43 --- /dev/null +++ b/ID_IN.C @@ -0,0 +1,1112 @@ +/* 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 Engine +// ID_IN.c - Input Manager +// v1.0d1 +// By Jason Blochowiak +// + +// +// This module handles dealing with the various input devices +// +// Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff), +// User Mgr (for command line parms) +// +// Globals: +// LastScan - The keyboard scan code of the last key pressed +// LastASCII - The ASCII value of the last key pressed +// DEBUG - there are more globals +// + +#include "ID_HEADS.H" +#pragma hdrstop + +#define KeyInt 9 // The keyboard ISR number + +// Stuff for the joystick +#define JoyScaleMax 32768 +#define JoyScaleShift 8 +#define MaxJoyValue 5000 + +// Global variables + boolean Keyboard[NumCodes], + JoysPresent[MaxJoys], + MousePresent; + boolean Paused; + char LastASCII; + ScanCode LastScan; + KeyboardDef KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}}; + JoystickDef JoyDefs[MaxJoys]; + ControlType Controls[MaxPlayers]; + + Demo DemoMode = demo_Off; + byte _seg *DemoBuffer; + word DemoOffset,DemoSize; + +// Internal variables +static boolean IN_Started; +static boolean CapsLock; +static ScanCode CurCode,LastCode; +static byte far ASCIINames[] = // Unshifted ASCII for scan codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8 ,9 , // 0 + 'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0 ,'a','s', // 1 + 'd','f','g','h','j','k','l',';',39 ,'`',0 ,92 ,'z','x','c','v', // 2 + 'b','n','m',',','.','/',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4 + '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + far ShiftNames[] = // Shifted ASCII for scan codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8 ,9 , // 0 + 'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0 ,'A','S', // 1 + 'D','F','G','H','J','K','L',':',34 ,'~',0 ,'|','Z','X','C','V', // 2 + 'B','N','M','<','>','?',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4 + '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + far SpecialNames[] = // ASCII for 0xe0 prefixed codes + { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 0 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,13 ,0 ,0 ,0 , // 1 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 2 + 0 ,0 ,0 ,0 ,0 ,'/',0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 3 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 4 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6 + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7 + }, + + *ScanNames[] = // Scan code names with single chars + { + "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?", + "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S", + "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V", + "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?", + "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?" + }, // DEBUG - consolidate these + far ExtScanCodes[] = // Scan codes with >1 char names + { + 1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e, + 0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36, + 0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48, + 0x50,0x4b,0x4d,0x00 + }, + *ExtScanNames[] = // Names corresponding to ExtScanCodes + { + "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4", + "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft", + "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up", + "Down","Left","Right","" + }; +static Direction DirTable[] = // Quick lookup for total direction + { + dir_NorthWest, dir_North, dir_NorthEast, + dir_West, dir_None, dir_East, + dir_SouthWest, dir_South, dir_SouthEast + }; + +static void (*INL_KeyHook)(void); +static void interrupt (*OldKeyVect)(void); + +static char *ParmStrings[] = {"nojoys","nomouse",nil}; + +// Internal routines + +/////////////////////////////////////////////////////////////////////////// +// +// INL_KeyService() - Handles a keyboard interrupt (key up/down) +// +/////////////////////////////////////////////////////////////////////////// +static void interrupt +INL_KeyService(void) +{ +static boolean special; + byte k,c, + temp; + + k = inportb(0x60); // Get the scan code + + // Tell the XT keyboard controller to clear the key + outportb(0x61,(temp = inportb(0x61)) | 0x80); + outportb(0x61,temp); + + if (k == 0xe0) // Special key prefix + special = true; + else if (k == 0xe1) // Handle Pause key + Paused = true; + else + { + if (k & 0x80) // Break code + { + k &= 0x7f; + +// DEBUG - handle special keys: ctl-alt-delete, print scrn + + Keyboard[k] = false; + } + else // Make code + { + LastCode = CurCode; + CurCode = LastScan = k; + Keyboard[k] = true; + + if (special) + c = SpecialNames[k]; + else + { + if (k == sc_CapsLock) + { + CapsLock ^= true; + // DEBUG - make caps lock light work + } + + if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted + { + c = ShiftNames[k]; + if ((c >= 'A') && (c <= 'Z') && CapsLock) + c += 'a' - 'A'; + } + else + { + c = ASCIINames[k]; + if ((c >= 'a') && (c <= 'z') && CapsLock) + c -= 'a' - 'A'; + } + } + if (c) + LastASCII = c; + } + + special = false; + } + + if (INL_KeyHook && !special) + INL_KeyHook(); + outportb(0x20,0x20); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetMouseDelta() - Gets the amount that the mouse has moved from the +// mouse driver +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_GetMouseDelta(int *x,int *y) +{ + Mouse(MDelta); + *x = _CX; + *y = _DX; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetMouseButtons() - Gets the status of the mouse buttons from the +// mouse driver +// +/////////////////////////////////////////////////////////////////////////// +static word +INL_GetMouseButtons(void) +{ + word buttons; + + Mouse(MButtons); + buttons = _BX; + return(buttons); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetJoyAbs() - Reads the absolute position of the specified joystick +// +/////////////////////////////////////////////////////////////////////////// +void +IN_GetJoyAbs(word joy,word *xp,word *yp) +{ + byte xb,yb, + xs,ys; + word x,y; + + x = y = 0; + xs = joy? 2 : 0; // Find shift value for x axis + xb = 1 << xs; // Use shift value to get x bit mask + ys = joy? 3 : 1; // Do the same for y axis + yb = 1 << ys; + +// Read the absolute joystick values +asm pushf // Save some registers +asm push si +asm push di +asm cli // Make sure an interrupt doesn't screw the timings + + +asm mov dx,0x201 +asm in al,dx +asm out dx,al // Clear the resistors + +asm mov ah,[xb] // Get masks into registers +asm mov ch,[yb] + +asm xor si,si // Clear count registers +asm xor di,di +asm xor bh,bh // Clear high byte of bx for later + +asm push bp // Don't mess up stack frame +asm mov bp,MaxJoyValue + +loop: +asm in al,dx // Get bits indicating whether all are finished + +asm dec bp // Check bounding register +asm jz done // We have a silly value - abort + +asm mov bl,al // Duplicate the bits +asm and bl,ah // Mask off useless bits (in [xb]) +asm add si,bx // Possibly increment count register +asm mov cl,bl // Save for testing later + +asm mov bl,al +asm and bl,ch // [yb] +asm add di,bx + +asm add cl,bl +asm jnz loop // If both bits were 0, drop out + +done: +asm pop bp + +asm mov cl,[xs] // Get the number of bits to shift +asm shr si,cl // and shift the count that many times + +asm mov cl,[ys] +asm shr di,cl + +asm mov [x],si // Store the values into the variables +asm mov [y],di + +asm pop di +asm pop si +asm popf // Restore the registers + + *xp = x; + *yp = y; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetJoyDelta() - Returns the relative movement of the specified +// joystick (from +/-127, scaled adaptively) +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive) +{ + word x,y; + longword time; + JoystickDef *def; +static longword lasttime; + + IN_GetJoyAbs(joy,&x,&y); + def = JoyDefs + joy; + + if (x < def->threshMinX) + { + if (x < def->joyMinX) + x = def->joyMinX; + + x = -(x - def->threshMinX); + x *= def->joyMultXL; + x >>= JoyScaleShift; + *dx = (x > 127)? -127 : -x; + } + else if (x > def->threshMaxX) + { + if (x > def->joyMaxX) + x = def->joyMaxX; + + x = x - def->threshMaxX; + x *= def->joyMultXH; + x >>= JoyScaleShift; + *dx = (x > 127)? 127 : x; + } + else + *dx = 0; + + if (y < def->threshMinY) + { + if (y < def->joyMinY) + y = def->joyMinY; + + y = -(y - def->threshMinY); + y *= def->joyMultYL; + y >>= JoyScaleShift; + *dy = (y > 127)? -127 : -y; + } + else if (y > def->threshMaxY) + { + if (y > def->joyMaxY) + y = def->joyMaxY; + + y = y - def->threshMaxY; + y *= def->joyMultYH; + y >>= JoyScaleShift; + *dy = (y > 127)? 127 : y; + } + else + *dy = 0; + + if (adaptive) + { + time = (TimeCount - lasttime) / 2; + if (time) + { + if (time > 8) + time = 8; + *dx *= time; + *dy *= time; + } + } + lasttime = TimeCount; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_GetJoyButtons() - Returns the button status of the specified +// joystick +// +/////////////////////////////////////////////////////////////////////////// +static word +INL_GetJoyButtons(word joy) +{ +register word result; + + result = inportb(0x201); // Get all the joystick buttons + result >>= joy? 6 : 4; // Shift into bits 0-1 + result &= 3; // Mask off the useless bits + result ^= 3; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetJoyButtonsDB() - Returns the de-bounced button status of the +// specified joystick +// +/////////////////////////////////////////////////////////////////////////// +word +IN_GetJoyButtonsDB(word joy) +{ + longword lasttime; + word result1,result2; + + do + { + result1 = INL_GetJoyButtons(joy); + lasttime = TimeCount; + while (TimeCount == lasttime) + ; + result2 = INL_GetJoyButtons(joy); + } while (result1 != result2); + return(result1); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartKbd() - Sets up my keyboard stuff for use +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_StartKbd(void) +{ + INL_KeyHook = 0; // Clear key hook + + IN_ClearKeysDown(); + + OldKeyVect = getvect(KeyInt); + setvect(KeyInt,INL_KeyService); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutKbd() - Restores keyboard control to the BIOS +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutKbd(void) +{ + poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0); // Clear ctrl/alt/shift flags + + setvect(KeyInt,OldKeyVect); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartMouse() - Detects and sets up the mouse +// +/////////////////////////////////////////////////////////////////////////// +static boolean +INL_StartMouse(void) +{ + if (getvect(MouseInt)) + { + Mouse(MReset); + if (_AX == 0xffff) + return(true); + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutMouse() - Cleans up after the mouse +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutMouse(void) +{ +} + +// +// INL_SetJoyScale() - Sets up scaling values for the specified joystick +// +static void +INL_SetJoyScale(word joy) +{ + JoystickDef *def; + + def = &JoyDefs[joy]; + def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX); + def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX); + def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY); + def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale() +// to set up scaling values +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy) +{ + word d,r; + JoystickDef *def; + + def = &JoyDefs[joy]; + + def->joyMinX = minx; + def->joyMaxX = maxx; + r = maxx - minx; + d = r / 5; + def->threshMinX = ((r / 2) - d) + minx; + def->threshMaxX = ((r / 2) + d) + minx; + + def->joyMinY = miny; + def->joyMaxY = maxy; + r = maxy - miny; + d = r / 5; + def->threshMinY = ((r / 2) - d) + miny; + def->threshMaxY = ((r / 2) + d) + miny; + + INL_SetJoyScale(joy); +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_StartJoy() - Detects & auto-configures the specified joystick +// The auto-config assumes the joystick is centered +// +/////////////////////////////////////////////////////////////////////////// +static boolean +INL_StartJoy(word joy) +{ + word x,y; + + IN_GetJoyAbs(joy,&x,&y); + + if + ( + ((x == 0) || (x > MaxJoyValue - 10)) + || ((y == 0) || (y > MaxJoyValue - 10)) + ) + return(false); + else + { + IN_SetupJoy(joy,0,x * 2,0,y * 2); + return(true); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_ShutJoy() - Cleans up the joystick stuff +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_ShutJoy(word joy) +{ + JoysPresent[joy] = false; +} + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Startup() - Starts up the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Startup(void) +{ + boolean checkjoys,checkmouse; + word i; + + if (IN_Started) + return; + + checkjoys = true; + checkmouse = true; + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],ParmStrings)) + { + case 0: + checkjoys = false; + break; + case 1: + checkmouse = false; + break; + } + } + + INL_StartKbd(); + MousePresent = checkmouse? INL_StartMouse() : false; + + for (i = 0;i < MaxJoys;i++) + JoysPresent[i] = checkjoys? INL_StartJoy(i) : false; + + IN_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Default() - Sets up default conditions for the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Default(boolean gotit,ControlType in) +{ + if + ( + (!gotit) + || ((in == ctrl_Joystick1) && !JoysPresent[0]) + || ((in == ctrl_Joystick2) && !JoysPresent[1]) + || ((in == ctrl_Mouse) && !MousePresent) + ) + in = ctrl_Keyboard1; + IN_SetControlType(0,in); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Shutdown() - Shuts down the Input Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Shutdown(void) +{ + word i; + + if (!IN_Started) + return; + + INL_ShutMouse(); + for (i = 0;i < MaxJoys;i++) + INL_ShutJoy(i); + INL_ShutKbd(); + + IN_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService() +// everytime a real make/break code gets hit +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetKeyHook(void (*hook)()) +{ + INL_KeyHook = hook; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ClearKeyDown() - Clears the keyboard array +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ClearKeysDown(void) +{ + int i; + + LastScan = sc_None; + LastASCII = key_None; + for (i = 0;i < NumCodes;i++) + Keyboard[i] = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor() +// +/////////////////////////////////////////////////////////////////////////// +static void +INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy) +{ + if (buttons & (1 << 0)) + info->button0 = true; + if (buttons & (1 << 1)) + info->button1 = true; + + info->x += dx; + info->y += dy; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ReadCursor() - Reads the input devices and fills in the cursor info +// struct +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ReadCursor(CursorInfo *info) +{ + word i, + buttons; + int dx,dy; + + info->x = info->y = 0; + info->button0 = info->button1 = false; + + if (MousePresent) + { + buttons = INL_GetMouseButtons(); + INL_GetMouseDelta(&dx,&dy); + INL_AdjustCursor(info,buttons,dx,dy); + } + + for (i = 0;i < MaxJoys;i++) + { + if (!JoysPresent[i]) + continue; + + buttons = INL_GetJoyButtons(i); + INL_GetJoyDelta(i,&dx,&dy,true); + dx /= 64; + dy /= 64; + INL_AdjustCursor(info,buttons,dx,dy); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_ReadControl() - Reads the device associated with the specified +// player and fills in the control info struct +// +/////////////////////////////////////////////////////////////////////////// +void +IN_ReadControl(int player,ControlInfo *info) +{ + boolean realdelta; + byte dbyte; + word buttons; + int dx,dy; + Motion mx,my; + ControlType type; +register KeyboardDef *def; + + dx = dy = 0; + mx = my = motion_None; + buttons = 0; + + if (DemoMode == demo_Playback) + { + dbyte = DemoBuffer[DemoOffset + 1]; + my = (dbyte & 3) - 1; + mx = ((dbyte >> 2) & 3) - 1; + buttons = (dbyte >> 4) & 3; + + if (!(--DemoBuffer[DemoOffset])) + { + DemoOffset += 2; + if (DemoOffset >= DemoSize) + DemoMode = demo_PlayDone; + } + + realdelta = false; + } + else if (DemoMode == demo_PlayDone) + Quit("Demo playback exceeded"); + else + { + switch (type = Controls[player]) + { + case ctrl_Keyboard1: + case ctrl_Keyboard2: + def = &KbdDefs[type - ctrl_Keyboard]; + + if (Keyboard[def->upleft]) + mx = motion_Left,my = motion_Up; + else if (Keyboard[def->upright]) + mx = motion_Right,my = motion_Up; + else if (Keyboard[def->downleft]) + mx = motion_Left,my = motion_Down; + else if (Keyboard[def->downright]) + mx = motion_Right,my = motion_Down; + + if (Keyboard[def->up]) + my = motion_Up; + else if (Keyboard[def->down]) + my = motion_Down; + + if (Keyboard[def->left]) + mx = motion_Left; + else if (Keyboard[def->right]) + mx = motion_Right; + + if (Keyboard[def->button0]) + buttons += 1 << 0; + if (Keyboard[def->button1]) + buttons += 1 << 1; + realdelta = false; + break; + case ctrl_Joystick1: + case ctrl_Joystick2: + INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false); + buttons = INL_GetJoyButtons(type - ctrl_Joystick); + realdelta = true; + break; + case ctrl_Mouse: + INL_GetMouseDelta(&dx,&dy); + buttons = INL_GetMouseButtons(); + realdelta = true; + break; + } + } + + if (realdelta) + { + mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None); + my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None); + } + else + { + dx = mx * 127; + dy = my * 127; + } + + info->x = dx; + info->xaxis = mx; + info->y = dy; + info->yaxis = my; + info->button0 = buttons & (1 << 0); + info->button1 = buttons & (1 << 1); + info->dir = DirTable[((my + 1) * 3) + (mx + 1)]; + + if (DemoMode == demo_Record) + { + // Pack the control info into a byte + dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1); + + if + ( + (DemoBuffer[DemoOffset + 1] == dbyte) + && (DemoBuffer[DemoOffset] < 255) + ) + (DemoBuffer[DemoOffset])++; + else + { + if (DemoOffset || DemoBuffer[DemoOffset]) + DemoOffset += 2; + + if (DemoOffset >= DemoSize) + Quit("Demo buffer overflow"); + + DemoBuffer[DemoOffset] = 1; + DemoBuffer[DemoOffset + 1] = dbyte; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_SetControlType() - Sets the control type to be used by the specified +// player +// +/////////////////////////////////////////////////////////////////////////// +void +IN_SetControlType(int player,ControlType type) +{ + // DEBUG - check that requested type is present? + Controls[player] = type; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_StartDemoRecord() - Starts the demo recording, using a buffer the +// size passed. Returns if the buffer allocation was successful +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_StartDemoRecord(word bufsize) +{ + if (!bufsize) + return(false); + + MM_GetPtr((memptr *)&DemoBuffer,bufsize); + DemoMode = demo_Record; + DemoSize = bufsize & ~1; + DemoOffset = 0; + DemoBuffer[0] = DemoBuffer[1] = 0; + + return(true); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_StartDemoPlayback() - Plays back the demo pointed to of the given size +// +/////////////////////////////////////////////////////////////////////////// +void +IN_StartDemoPlayback(byte _seg *buffer,word bufsize) +{ + DemoBuffer = buffer; + DemoMode = demo_Playback; + DemoSize = bufsize & ~1; + DemoOffset = 0; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_StopDemo() - Turns off demo mode +// +/////////////////////////////////////////////////////////////////////////// +void +IN_StopDemo(void) +{ + if ((DemoMode == demo_Record) && DemoOffset) + DemoOffset += 2; + + DemoMode = demo_Off; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated +// +/////////////////////////////////////////////////////////////////////////// +void +IN_FreeDemoBuffer(void) +{ + if (DemoBuffer) + MM_FreePtr((memptr *)&DemoBuffer); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetScanName() - Returns a string containing the name of the +// specified scan code +// +/////////////////////////////////////////////////////////////////////////// +byte * +IN_GetScanName(ScanCode scan) +{ + byte **p; + ScanCode far *s; + + for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++) + if (*s == scan) + return(*p); + + return(ScanNames[scan]); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_WaitForKey() - Waits for a scan code, then clears LastScan and +// returns the scan code +// +/////////////////////////////////////////////////////////////////////////// +ScanCode +IN_WaitForKey(void) +{ + ScanCode result; + + while (!(result = LastScan)) + ; + LastScan = 0; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and +// returns the ASCII value +// +/////////////////////////////////////////////////////////////////////////// +char +IN_WaitForASCII(void) +{ + char result; + + while (!(result = LastASCII)) + ; + LastASCII = '\0'; + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_AckBack() - Waits for either an ASCII keypress or a button press +// +/////////////////////////////////////////////////////////////////////////// +void +IN_AckBack(void) +{ + word i; + + while (!LastScan) + { + if (MousePresent) + { + if (INL_GetMouseButtons()) + { + while (INL_GetMouseButtons()) + ; + return; + } + } + + for (i = 0;i < MaxJoys;i++) + { + if (JoysPresent[i]) + { + if (IN_GetJoyButtonsDB(i)) + { + while (IN_GetJoyButtonsDB(i)) + ; + return; + } + } + } + } + + IN_ClearKey(LastScan); + LastScan = sc_None; +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_Ack() - Clears user input & then calls IN_AckBack() +// +/////////////////////////////////////////////////////////////////////////// +void +IN_Ack(void) +{ + word i; + + IN_ClearKey(LastScan); + LastScan = sc_None; + + if (MousePresent) + while (INL_GetMouseButtons()) + ; + for (i = 0;i < MaxJoys;i++) + if (JoysPresent[i]) + while (IN_GetJoyButtonsDB(i)) + ; + + IN_AckBack(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_IsUserInput() - Returns true if a key has been pressed or a button +// is down +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_IsUserInput(void) +{ + boolean result; + word i; + + result = LastScan; + + if (MousePresent) + if (INL_GetMouseButtons()) + result = true; + + for (i = 0;i < MaxJoys;i++) + if (JoysPresent[i]) + if (INL_GetJoyButtons(i)) + result = true; + + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// IN_UserInput() - Waits for the specified delay time (in ticks) or the +// user pressing a key or a mouse button. If the clear flag is set, it +// then either clears the key or waits for the user to let the mouse +// button up. +// +/////////////////////////////////////////////////////////////////////////// +boolean +IN_UserInput(longword delay,boolean clear) +{ + longword lasttime; + + lasttime = TimeCount; + do + { + if (IN_IsUserInput()) + { + if (clear) + IN_AckBack(); + return(true); + } + } while (TimeCount - lasttime < delay); + return(false); +} diff --git a/ID_IN.H b/ID_IN.H new file mode 100644 index 0000000..45477ec --- /dev/null +++ b/ID_IN.H @@ -0,0 +1,208 @@ +/* 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 Engine +// ID_IN.h - Header file for Input Manager +// v1.0d1 +// By Jason Blochowiak +// + +#ifndef __TYPES__ +#include "ID_Types.h" +#endif + +#ifndef __ID_IN__ +#define __ID_IN__ + +#ifdef __DEBUG__ +#define __DEBUG_InputMgr__ +#endif + +#define MaxPlayers 4 +#define MaxKbds 2 +#define MaxJoys 2 +#define NumCodes 128 + +typedef byte ScanCode; +#define sc_None 0 +#define sc_Bad 0xff +#define sc_Return 0x1c +#define sc_Enter sc_Return +#define sc_Escape 0x01 +#define sc_Space 0x39 +#define sc_BackSpace 0x0e +#define sc_Tab 0x0f +#define sc_Alt 0x38 +#define sc_Control 0x1d +#define sc_CapsLock 0x3a +#define sc_LShift 0x2a +#define sc_RShift 0x36 +#define sc_UpArrow 0x48 +#define sc_DownArrow 0x50 +#define sc_LeftArrow 0x4b +#define sc_RightArrow 0x4d +#define sc_Insert 0x52 +#define sc_Delete 0x53 +#define sc_Home 0x47 +#define sc_End 0x4f +#define sc_PgUp 0x49 +#define sc_PgDn 0x51 +#define sc_F1 0x3b +#define sc_F2 0x3c +#define sc_F3 0x3d +#define sc_F4 0x3e +#define sc_F5 0x3f +#define sc_F6 0x40 +#define sc_F7 0x41 +#define sc_F8 0x42 +#define sc_F9 0x43 +#define sc_F10 0x44 +#define sc_F11 0x57 +#define sc_F12 0x59 + +#define sc_A 0x1e +#define sc_B 0x30 +#define sc_C 0x2e +#define sc_D 0x20 +#define sc_E 0x12 +#define sc_F 0x21 +#define sc_G 0x22 +#define sc_H 0x23 +#define sc_I 0x17 +#define sc_J 0x24 +#define sc_K 0x25 +#define sc_L 0x26 +#define sc_M 0x32 +#define sc_N 0x31 +#define sc_O 0x18 +#define sc_P 0x19 +#define sc_Q 0x10 +#define sc_R 0x13 +#define sc_S 0x1f +#define sc_T 0x14 +#define sc_U 0x16 +#define sc_V 0x2f +#define sc_W 0x11 +#define sc_X 0x2d +#define sc_Y 0x15 +#define sc_Z 0x2c + +#define key_None 0 +#define key_Return 0x0d +#define key_Enter key_Return +#define key_Escape 0x1b +#define key_Space 0x20 +#define key_BackSpace 0x08 +#define key_Tab 0x09 +#define key_Delete 0x7f + +// Stuff for the mouse +#define MReset 0 +#define MButtons 3 +#define MDelta 11 + +#define MouseInt 0x33 +#define Mouse(x) _AX = x,geninterrupt(MouseInt) + +typedef enum { + demo_Off,demo_Record,demo_Playback,demo_PlayDone + } Demo; +typedef enum { + ctrl_Keyboard, + ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2, + ctrl_Joystick, + ctrl_Joystick1 = ctrl_Joystick,ctrl_Joystick2, + ctrl_Mouse + } ControlType; +typedef enum { + motion_Left = -1,motion_Up = -1, + motion_None = 0, + motion_Right = 1,motion_Down = 1 + } Motion; +typedef enum { + dir_North,dir_NorthEast, + dir_East,dir_SouthEast, + dir_South,dir_SouthWest, + dir_West,dir_NorthWest, + dir_None + } Direction; +typedef struct { + boolean button0,button1; + int x,y; + Motion xaxis,yaxis; + Direction dir; + } CursorInfo; +typedef CursorInfo ControlInfo; +typedef struct { + ScanCode button0,button1, + upleft, up, upright, + left, right, + downleft, down, downright; + } KeyboardDef; +typedef struct { + word joyMinX,joyMinY, + threshMinX,threshMinY, + threshMaxX,threshMaxY, + joyMaxX,joyMaxY, + joyMultXL,joyMultYL, + joyMultXH,joyMultYH; + } JoystickDef; +// Global variables +extern boolean Keyboard[], + MousePresent, + JoysPresent[]; +extern boolean Paused; +extern char LastASCII; +extern ScanCode LastScan; +extern KeyboardDef KbdDefs[]; +extern JoystickDef JoyDefs[]; +extern ControlType Controls[MaxPlayers]; + +extern Demo DemoMode; +extern byte _seg *DemoBuffer; +extern word DemoOffset,DemoSize; + +// Function prototypes +#define IN_KeyDown(code) (Keyboard[(code)]) +#define IN_ClearKey(code) {Keyboard[code] = false;\ + if (code == LastScan) LastScan = sc_None;} + +// DEBUG - put names in prototypes +extern void IN_Startup(void),IN_Shutdown(void), + IN_Default(boolean gotit,ControlType in), + IN_SetKeyHook(void (*)()), + IN_ClearKeysDown(void), + IN_ReadCursor(CursorInfo *), + IN_ReadControl(int,ControlInfo *), + IN_SetControlType(int,ControlType), + IN_GetJoyAbs(word joy,word *xp,word *yp), + IN_SetupJoy(word joy,word minx,word maxx, + word miny,word maxy), + IN_StartDemoPlayback(byte _seg *buffer,word bufsize), + IN_StopDemo(void),IN_FreeDemoBuffer(void), + IN_Ack(void),IN_AckBack(void); +extern boolean IN_UserInput(longword delay,boolean clear), + IN_IsUserInput(void), + IN_StartDemoRecord(word bufsize); +extern byte *IN_GetScanName(ScanCode); +extern char IN_WaitForASCII(void); +extern ScanCode IN_WaitForKey(void); +extern word IN_GetJoyButtonsDB(word joy); + +#endif diff --git a/ID_MM.C b/ID_MM.C new file mode 100644 index 0000000..76a867d --- /dev/null +++ b/ID_MM.C @@ -0,0 +1,1130 @@ +/* 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. + */ + +// NEWMM.C + +/* +============================================================================= + + ID software memory manager + -------------------------- + +Primary coder: John Carmack + +RELIES ON +--------- +Quit (char *error) function + + +WORK TO DO +---------- +MM_SizePtr to change the size of a given pointer + +Multiple purge levels utilized + +EMS / XMS unmanaged routines + +============================================================================= +*/ + +#include "ID_HEADS.H" +#pragma hdrstop + +#pragma warn -pro +#pragma warn -use + +/* +============================================================================= + + LOCAL INFO + +============================================================================= +*/ + +#define LOCKBIT 0x80 // if set in attributes, block cannot be moved +#define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first +#define PURGEMASK 0xfffc +#define BASEATTRIBUTES 0 // unlocked, non purgable + +#define MAXUMBS 10 + +typedef struct mmblockstruct +{ + unsigned start,length; + unsigned attributes; + memptr *useptr; // pointer to the segment start + struct mmblockstruct far *next; +} mmblocktype; + + +//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!")\ +// ;mmfree=mmfree->next;} + +#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;} + +#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;} + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +mminfotype mminfo; +memptr bufferseg; +boolean mmerror; + +void (* beforesort) (void); +void (* aftersort) (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +boolean mmstarted; + +void far *farheap; +void *nearheap; + +mmblocktype far mmblocks[MAXBLOCKS] + ,far *mmhead,far *mmfree,far *mmrover,far *mmnew; + +boolean bombonerror; + +unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle; + +void (* XMSaddr) (void); // far pointer to XMS driver + +unsigned numUMBs,UMBbase[MAXUMBS]; + +//========================================================================== + +// +// local prototypes +// + +boolean MML_CheckForEMS (void); +void MML_ShutdownEMS (void); +void MM_MapEMS (void); +boolean MML_CheckForXMS (void); +void MML_ShutdownXMS (void); +void MML_UseSpace (unsigned segstart, unsigned seglength); +void MML_ClearBlock (void); + +//========================================================================== + +/* +====================== += += MML_CheckForEMS += += Routine from p36 of Extending DOS += +======================= +*/ + +char emmname[9] = "EMMXXXX0"; + +boolean MML_CheckForEMS (void) +{ +asm mov dx,OFFSET emmname[0] +asm mov ax,0x3d00 +asm int 0x21 // try to open EMMXXXX0 device +asm jc error + +asm mov bx,ax +asm mov ax,0x4400 + +asm int 0x21 // get device info +asm jc error + +asm and dx,0x80 +asm jz error + +asm mov ax,0x4407 + +asm int 0x21 // get status +asm jc error +asm or al,al +asm jz error + +asm mov ah,0x3e +asm int 0x21 // close handle +asm jc error + +// +// EMS is good +// + return true; + +error: +// +// EMS is bad +// + return false; +} + + +/* +====================== += += MML_SetupEMS += +======================= +*/ + +void MML_SetupEMS (void) +{ + char str[80],str2[10]; + unsigned error; + + totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0; + +asm { + mov ah,EMS_STATUS + int EMS_INT // make sure EMS hardware is present + or ah,ah + jnz error + + mov ah,EMS_VERSION + int EMS_INT + or ah,ah + jnz error + cmp al,0x32 // only work on ems 3.2 or greater + jb error + + mov ah,EMS_GETFRAME + int EMS_INT // find the page frame address + or ah,ah + jnz error + mov [EMSpageframe],bx + + mov ah,EMS_GETPAGES + int EMS_INT // find out how much EMS is there + or ah,ah + jnz error + mov [totalEMSpages],dx + mov [freeEMSpages],bx + or bx,bx + jz noEMS // no EMS at all to allocate + + cmp bx,4 + jle getpages // there is only 1,2,3,or 4 pages + mov bx,4 // we can't use more than 4 pages + } + +getpages: +asm { + mov [EMSpagesmapped],bx + mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS + int EMS_INT + or ah,ah + jnz error + mov [EMShandle],dx + } + return; + +error: + error = _AH; + strcpy (str,"MML_SetupEMS: EMS error 0x"); + itoa(error,str2,16); + strcpy (str,str2); + Quit (str); + +noEMS: +; +} + + +/* +====================== += += MML_ShutdownEMS += +======================= +*/ + +void MML_ShutdownEMS (void) +{ + if (!EMShandle) + return; + +asm { + mov ah,EMS_FREEPAGES + mov dx,[EMShandle] + int EMS_INT + or ah,ah + jz ok + } + + Quit ("MML_ShutdownEMS: Error freeing EMS!"); + +ok: +; +} + +/* +==================== += += MM_MapEMS += += Maps the 64k of EMS used by memory manager into the page frame += for general use. This only needs to be called if you are keeping += other things in EMS. += +==================== +*/ + +void MM_MapEMS (void) +{ + char str[80],str2[10]; + unsigned error; + int i; + + for (i=0;istart+scan->length < segstart) + { + last = scan; + scan = scan->next; + } + +// +// take the given range out of the block +// + oldend = scan->start + scan->length; + extra = oldend - (segstart+seglength); + if (extra < 0) + Quit ("MML_UseSpace: Segment spans two blocks!"); + + if (segstart == scan->start) + { + last->next = scan->next; // unlink block + FREEBLOCK(scan); + scan = last; + } + else + scan->length = segstart-scan->start; // shorten block + + if (extra > 0) + { + GETNEWBLOCK; + mmnew->next = scan->next; + scan->next = mmnew; + mmnew->start = segstart+seglength; + mmnew->length = extra; + mmnew->attributes = LOCKBIT; + } + +} + +//========================================================================== + +/* +==================== += += MML_ClearBlock += += We are out of blocks, so free a purgable block += +==================== +*/ + +void MML_ClearBlock (void) +{ + mmblocktype far *scan,far *last; + + scan = mmhead->next; + + while (scan) + { + if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) ) + { + MM_FreePtr(scan->useptr); + return; + } + scan = scan->next; + } + + Quit ("MM_ClearBlock: No purgable blocks!"); +} + + +//========================================================================== + +/* +=================== += += MM_Startup += += Grabs all space from turbo with malloc/farmalloc += Allocates bufferseg misc buffer += +=================== +*/ + +static char *ParmStrings[] = {"noems","noxms",""}; + +void MM_Startup (void) +{ + int i; + unsigned long length; + void far *start; + unsigned segstart,seglength,endfree; + + if (mmstarted) + MM_Shutdown (); + + + mmstarted = true; + bombonerror = true; +// +// set up the linked list (everything in the free list; +// + mmhead = NULL; + mmfree = &mmblocks[0]; + for (i=0;istart = 0; + mmnew->length = 0xffff; + mmnew->attributes = LOCKBIT; + mmnew->next = NULL; + mmrover = mmhead; + + +// +// get all available near conventional memory segments +// + length=coreleft(); + start = (void far *)(nearheap = malloc(length)); + + length -= 16-(FP_OFF(start)&15); + length -= SAVENEARHEAP; + seglength = length / 16; // now in paragraphs + segstart = FP_SEG(start)+(FP_OFF(start)+15)/16; + MML_UseSpace (segstart,seglength); + mminfo.nearheap = length; + +// +// get all available far conventional memory segments +// + length=farcoreleft(); + start = farheap = farmalloc(length); + length -= 16-(FP_OFF(start)&15); + length -= SAVEFARHEAP; + seglength = length / 16; // now in paragraphs + segstart = FP_SEG(start)+(FP_OFF(start)+15)/16; + MML_UseSpace (segstart,seglength); + mminfo.farheap = length; + mminfo.mainmem = mminfo.nearheap + mminfo.farheap; + + +// +// detect EMS and allocate up to 64K at page frame +// + mminfo.EMSmem = 0; + for (i = 1;i < _argc;i++) + { + if ( US_CheckParm(_argv[i],ParmStrings) == 0) + goto emsskip; // param NOEMS + } + + if (MML_CheckForEMS()) + { + MML_SetupEMS(); // allocate space + MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400); + MM_MapEMS(); // map in used pages + mminfo.EMSmem = EMSpagesmapped*0x4000l; + } + +// +// detect XMS and get upper memory blocks +// +emsskip: + mminfo.XMSmem = 0; + for (i = 1;i < _argc;i++) + { + if ( US_CheckParm(_argv[i],ParmStrings) == 0) + goto xmsskip; // param NOXMS + } + + if (MML_CheckForXMS()) + MML_SetupXMS(); // allocate as many UMBs as possible + +// +// allocate the misc buffer +// +xmsskip: + mmrover = mmhead; // start looking for space after low block + + MM_GetPtr (&bufferseg,BUFFERSIZE); +} + +//========================================================================== + +/* +==================== += += MM_Shutdown += += Frees all conventional, EMS, and XMS allocated += +==================== +*/ + +void MM_Shutdown (void) +{ + if (!mmstarted) + return; + + farfree (farheap); + free (nearheap); + MML_ShutdownEMS (); + MML_ShutdownXMS (); +} + +//========================================================================== + +/* +==================== += += MM_GetPtr += += Allocates an unlocked, unpurgable block += +==================== +*/ + +void MM_GetPtr (memptr *baseptr,unsigned long size) +{ + mmblocktype far *scan,far *lastscan,far *endscan + ,far *purge,far *next; + int search; + unsigned needed,startseg; + + needed = (size+15)/16; // convert size from bytes to paragraphs + + GETNEWBLOCK; // fill in start and next after a spot is found + mmnew->length = needed; + mmnew->useptr = baseptr; + mmnew->attributes = BASEATTRIBUTES; + + for (search = 0; search<3; search++) + { + // + // first search: try to allocate right after the rover, then on up + // second search: search from the head pointer up to the rover + // third search: compress memory, then scan from start + if (search == 1 && mmrover == mmhead) + search++; + + switch (search) + { + case 0: + lastscan = mmrover; + scan = mmrover->next; + endscan = NULL; + break; + case 1: + lastscan = mmhead; + scan = mmhead->next; + endscan = mmrover; + break; + case 2: + MM_SortMem (); + lastscan = mmhead; + scan = mmhead->next; + endscan = NULL; + break; + } + + startseg = lastscan->start + lastscan->length; + + while (scan != endscan) + { + if (scan->start - startseg >= needed) + { + // + // got enough space between the end of lastscan and + // the start of scan, so throw out anything in the middle + // and allocate the new block + // + purge = lastscan->next; + lastscan->next = mmnew; + mmnew->start = *(unsigned *)baseptr = startseg; + mmnew->next = scan; + while ( purge != scan) + { // free the purgable block + next = purge->next; + FREEBLOCK(purge); + purge = next; // purge another if not at scan + } + mmrover = mmnew; + return; // good allocation! + } + + // + // if this block is purge level zero or locked, skip past it + // + if ( (scan->attributes & LOCKBIT) + || !(scan->attributes & PURGEBITS) ) + { + lastscan = scan; + startseg = lastscan->start + lastscan->length; + } + + + scan=scan->next; // look at next line + } + } + + if (bombonerror) + Quit ("MM_GetPtr: Out of memory!"); + else + mmerror = true; +} + +//========================================================================== + +/* +==================== += += MM_FreePtr += += Allocates an unlocked, unpurgable block += +==================== +*/ + +void MM_FreePtr (memptr *baseptr) +{ + mmblocktype far *scan,far *last; + + last = mmhead; + scan = last->next; + + if (baseptr == mmrover->useptr) // removed the last allocated block + mmrover = mmhead; + + while (scan->useptr != baseptr && scan) + { + last = scan; + scan = scan->next; + } + + if (!scan) + Quit ("MM_FreePtr: Block not found!"); + + last->next = scan->next; + + FREEBLOCK(scan); +} +//========================================================================== + +/* +===================== += += MM_SetPurge += += Sets the purge level for a block (locked blocks cannot be made purgable) += +===================== +*/ + +void MM_SetPurge (memptr *baseptr, int purge) +{ + mmblocktype far *start; + + start = mmrover; + + do + { + if (mmrover->useptr == baseptr) + break; + + mmrover = mmrover->next; + + if (!mmrover) + mmrover = mmhead; + else if (mmrover == start) + Quit ("MM_SetPurge: Block not found!"); + + } while (1); + + mmrover->attributes &= ~PURGEBITS; + mmrover->attributes |= purge; +} + +//========================================================================== + +/* +===================== += += MM_SetLock += += Locks / unlocks the block += +===================== +*/ + +void MM_SetLock (memptr *baseptr, boolean locked) +{ + mmblocktype far *start; + + start = mmrover; + + do + { + if (mmrover->useptr == baseptr) + break; + + mmrover = mmrover->next; + + if (!mmrover) + mmrover = mmhead; + else if (mmrover == start) + Quit ("MM_SetLock: Block not found!"); + + } while (1); + + mmrover->attributes &= ~LOCKBIT; + mmrover->attributes |= locked*LOCKBIT; +} + +//========================================================================== + +/* +===================== += += MM_SortMem += += Throws out all purgable stuff and compresses movable blocks += +===================== +*/ + +void MM_SortMem (void) +{ + mmblocktype far *scan,far *last,far *next; + unsigned start,length,source,dest,oldborder; + int playing; + + // + // lock down a currently playing sound + // + playing = SD_SoundPlaying (); + if (playing) + { + switch (SoundMode) + { + case sdm_PC: + playing += STARTPCSOUNDS; + break; + case sdm_AdLib: + playing += STARTADLIBSOUNDS; + break; + } + MM_SetLock(&(memptr)audiosegs[playing],true); + } + + + SD_StopSound(); + oldborder = bordercolor; + VW_ColorBorder (15); + + if (beforesort) + beforesort(); + + scan = mmhead; + + last = NULL; // shut up compiler warning + + while (scan) + { + if (scan->attributes & LOCKBIT) + { + // + // block is locked, so try to pile later blocks right after it + // + start = scan->start + scan->length; + } + else + { + if (scan->attributes & PURGEBITS) + { + // + // throw out the purgable block + // + next = scan->next; + FREEBLOCK(scan); + last->next = next; + scan = next; + continue; + } + else + { + // + // push the non purgable block on top of the last moved block + // + if (scan->start != start) + { + length = scan->length; + source = scan->start; + dest = start; + while (length > 0xf00) + { + movedata(source,0,dest,0,0xf00*16); + length -= 0xf00; + source += 0xf00; + dest += 0xf00; + } + movedata(source,0,dest,0,length*16); + + scan->start = start; + *(unsigned *)scan->useptr = start; + } + start = scan->start + scan->length; + } + } + + last = scan; + scan = scan->next; // go to next block + } + + mmrover = mmhead; + + if (aftersort) + aftersort(); + + VW_ColorBorder (oldborder); + + if (playing) + MM_SetLock(&(memptr)audiosegs[playing],false); +} + + +//========================================================================== + +/* +===================== += += MM_ShowMemory += +===================== +*/ + +void MM_ShowMemory (void) +{ + mmblocktype far *scan; + unsigned color,temp; + long end,owner; + char scratch[80],str[10]; + + VW_SetDefaultColors(); + VW_SetLineWidth(40); + temp = bufferofs; + bufferofs = 0; + VW_SetScreen (0,0); + + scan = mmhead; + + end = -1; + +//CA_OpenDebug (); + + while (scan) + { + if (scan->attributes & PURGEBITS) + color = 5; // dark purple = purgable + else + color = 9; // medium blue = non purgable + if (scan->attributes & LOCKBIT) + color = 12; // red = locked + if (scan->start<=end) + Quit ("MM_ShowMemory: Memory block order currupted!"); + end = scan->start+scan->length-1; + VW_Hlin(scan->start,(unsigned)end,0,color); + VW_Plot(scan->start,0,15); + if (scan->next->start > end+1) + VW_Hlin(end+1,scan->next->start,0,0); // black = free + +#if 0 +strcpy (scratch,"Size:"); +ltoa ((long)scan->length*16,str,10); +strcat (scratch,str); +strcat (scratch,"\tOwner:0x"); +owner = (unsigned)scan->useptr; +ultoa (owner,str,16); +strcat (scratch,str); +strcat (scratch,"\n"); +write (debughandle,scratch,strlen(scratch)); +#endif + + scan = scan->next; + } + +//CA_CloseDebug (); + + IN_Ack(); + VW_SetLineWidth(64); + bufferofs = temp; +} + +//========================================================================== + + +/* +====================== += += MM_UnusedMemory += += Returns the total free space without purging += +====================== +*/ + +long MM_UnusedMemory (void) +{ + unsigned free; + mmblocktype far *scan; + + free = 0; + scan = mmhead; + + while (scan->next) + { + free += scan->next->start - (scan->start + scan->length); + scan = scan->next; + } + + return free*16l; +} + +//========================================================================== + + +/* +====================== += += MM_TotalFree += += Returns the total free space with purging += +====================== +*/ + +long MM_TotalFree (void) +{ + unsigned free; + mmblocktype far *scan; + + free = 0; + scan = mmhead; + + while (scan->next) + { + if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT)) + free += scan->length; + free += scan->next->start - (scan->start + scan->length); + scan = scan->next; + } + + return free*16l; +} + +//========================================================================== + +/* +===================== += += MM_BombOnError += +===================== +*/ + +void MM_BombOnError (boolean bomb) +{ + bombonerror = bomb; +} + + diff --git a/ID_MM.H b/ID_MM.H new file mode 100644 index 0000000..49fb7ed --- /dev/null +++ b/ID_MM.H @@ -0,0 +1,108 @@ +/* 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_MM.H + +#ifndef __ID_CA__ + +#define __ID_CA__ + +#define SAVENEARHEAP 0x400 // space to leave in data segment +#define SAVEFARHEAP 0 // space to leave in far heap + +#define BUFFERSIZE 0x1000 // miscelanious, allways available buffer + +#define MAXBLOCKS 600 + + +//-------- + +#define EMS_INT 0x67 + +#define EMS_STATUS 0x40 +#define EMS_GETFRAME 0x41 +#define EMS_GETPAGES 0x42 +#define EMS_ALLOCPAGES 0x43 +#define EMS_MAPPAGE 0x44 +#define EMS_FREEPAGES 0x45 +#define EMS_VERSION 0x46 + +//-------- + +#define XMS_VERSION 0x00 + +#define XMS_ALLOCHMA 0x01 +#define XMS_FREEHMA 0x02 + +#define XMS_GENABLEA20 0x03 +#define XMS_GDISABLEA20 0x04 +#define XMS_LENABLEA20 0x05 +#define XMS_LDISABLEA20 0x06 +#define XMS_QUERYA20 0x07 + +#define XMS_QUERYREE 0x08 +#define XMS_ALLOC 0x09 +#define XMS_FREE 0x0A +#define XMS_MOVE 0x0B +#define XMS_LOCK 0x0C +#define XMS_UNLOCK 0x0D +#define XMS_GETINFO 0x0E +#define XMS_RESIZE 0x0F + +#define XMS_ALLOCUMB 0x10 +#define XMS_FREEUMB 0x11 + +//========================================================================== + +typedef void _seg * memptr; + +typedef struct +{ + long nearheap,farheap,EMSmem,XMSmem,mainmem; +} mminfotype; + +//========================================================================== + +extern mminfotype mminfo; +extern memptr bufferseg; +extern boolean mmerror; + +extern void (* beforesort) (void); +extern void (* aftersort) (void); + +//========================================================================== + +void MM_Startup (void); +void MM_Shutdown (void); +void MM_MapEMS (void); + +void MM_GetPtr (memptr *baseptr,unsigned long size); +void MM_FreePtr (memptr *baseptr); + +void MM_SetPurge (memptr *baseptr, int purge); +void MM_SetLock (memptr *baseptr, boolean locked); +void MM_SortMem (void); + +void MM_ShowMemory (void); + +long MM_UnusedMemory (void); +long MM_TotalFree (void); + +void MM_BombOnError (boolean bomb); + +#endif \ No newline at end of file diff --git a/ID_RF.C b/ID_RF.C new file mode 100644 index 0000000..fa08d19 --- /dev/null +++ b/ID_RF.C @@ -0,0 +1,2845 @@ +/* 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; +} 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; + + if (grmode == EGAGR) + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],ParmStrings) == 0) + { + compatability = true; + break; + } + + 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;i=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]; + *info = (unsigned)&allanims[i]; + numanimchains++; + } + + anims = 0; + change = (signed char)(tinf[ANIM+tile]); + next = tile+change; + while (change && next != tile) + { + 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]; + + *info = (unsigned)&allanims[i]; + numanimchains++; + } + + anims = 0; + change = (signed char)(tinf[MANIM+tile]); + next = tile+change; + while (change && next != tile) + { + 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 (startnexttile; + 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); + } + +// +// 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); + } + +} + + +/* +==================== += += RFL_RemoveAnimsOnX += +==================== +*/ + +void RFL_RemoveAnimsOnX (unsigned x) +{ + animtiletype *current,*next; + + current = animhead; + while (current) + { + if (current->x == x) + { + *(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) + { + *(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) + { + *(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; + } + 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; + } + } +} + +/* +============================================================================= + + EGA specific routines + +============================================================================= +*/ + +#if GRMODE == EGAGR + +/* +===================== += += 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... +} + +//=========================================================================== + +/* +===================== += += 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; + 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: + 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: + 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 +// + RFL_CalcTics (); +} + +#endif // GRMODE == CGAGR diff --git a/ID_RF.H b/ID_RF.H new file mode 100644 index 0000000..f11fdf8 --- /dev/null +++ b/ID_RF.H @@ -0,0 +1,153 @@ +/* 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.H + +#define __ID_RF__ + +#ifndef __ID_MM__ +#include "ID_MM.H" +#endif + +/* +============================================================================= + + CONSTANTS + +============================================================================= +*/ + +#define MINTICS 2 +#define MAXTICS 6 +#define DEMOTICS 3 + +#define MAPBORDER 2 // map border must be at least 1 + +#define MAXSPRITES 50 // max tracked sprites +#define MAXANIMTILES 90 // max animating tiles on screen +#define MAXANIMTYPES 50 // max different unique anim tiles on map + +#define MAXMAPHEIGHT 200 + +#define PRIORITIES 4 +#define MASKEDTILEPRIORITY 3 // planes go: 0,1,2,MTILES,3 + +#define TILEGLOBAL 256 +#define PIXGLOBAL 16 + +#define G_T_SHIFT 8 // global >> ?? = tile +#define G_P_SHIFT 4 // global >> ?? = pixels +#define P_T_SHIFT 4 // pixels >> ?? = tile + +#define PORTTILESWIDE 21 // all drawing takes place inside a +#define PORTTILESHIGH 14 // non displayed port of this size + +//#define PORTGLOBALWIDE (21*TILEGLOBAL) +//#define PORTGLOBALHIGH (14*TILEGLOBAL) + +#define UPDATEWIDE (PORTTILESWIDE+1) +#define UPDATEHIGH PORTTILESHIGH + + +//=========================================================================== + +typedef enum {spritedraw,maskdraw} drawtype; + +/* +============================================================================= + + PUBLIC VARIABLES + +============================================================================= +*/ + + +extern boolean compatability; // crippled refresh for wierdo SVGAs + +extern unsigned tics; +extern long lasttimecount; + +extern unsigned originxglobal,originyglobal; +extern unsigned originxtile,originytile; +extern unsigned originxscreen,originyscreen; + +extern unsigned mapwidth,mapheight,mapbyteswide,mapwordswide + ,mapbytesextra,mapwordsextra; +extern unsigned mapbwidthtable[MAXMAPHEIGHT]; + +extern unsigned originxmin,originxmax,originymin,originymax; + +extern unsigned masterofs; + +// +// the floating update window is also used by the view manager for +// double buffer tracking +// + +extern byte *updateptr; // current start of update window + +#if GRMODE == CGAGR +extern byte *baseupdateptr; +#endif + +extern unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +extern unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH]; +extern unsigned uwidthtable[UPDATEHIGH]; // lookup instead of multiple + +#define UPDATETERMINATE 0x0301 + +/* +============================================================================= + + PUBLIC FUNCTIONS + +============================================================================= +*/ + +void RF_Startup (void); +void RF_Shutdown (void); + +void RF_FixOfs (void); +void RF_NewMap (void); +void RF_MarkTileGraphics (void); +void RF_SetScrollBlock (int x, int y, boolean horizontal); +void RF_NewPosition (unsigned x, unsigned y); +void RF_Scroll (int x, int y); + +void RF_MapToMap (unsigned srcx, unsigned srcy, + unsigned destx, unsigned desty, + unsigned width, unsigned height); +void RF_MemToMap (unsigned far *source, unsigned plane, + unsigned destx, unsigned desty, + unsigned width, unsigned height); + +void RF_ClearBlock (int x, int y, int width, int height); +void RF_RedrawBlock (int x, int y, int width, int height); + +void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly, + unsigned spritenumber, drawtype draw, int priority); +void RF_RemoveSprite (void **user); + +void RF_CalcTics (void); + +void RF_Refresh (void); +void RF_ForceRefresh (void); +void RF_SetRefreshHook (void (*func) (void) ); + +unsigned RF_FindFreeBuffer (void); + diff --git a/ID_RF_A.ASM b/ID_RF_A.ASM new file mode 100644 index 0000000..56db0c7 --- /dev/null +++ b/ID_RF_A.ASM @@ -0,0 +1,690 @@ +; 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_A.ASM + +IDEAL +MODEL MEDIUM,C + +INCLUDE "ID_ASM.EQU" + +;============================================================================ + +TILESWIDE = 21 +TILESHIGH = 14 + +UPDATESIZE = (TILESWIDE+1)*TILESHIGH+1 + +DATASEG + +EXTRN screenseg:WORD +EXTRN updateptr:WORD +EXTRN updatestart:WORD +EXTRN masterofs:WORD ;start of master tile port +EXTRN bufferofs:WORD ;start of current buffer port +EXTRN screenstart:WORD ;starts of three screens (0/1/master) in EGA mem +EXTRN grsegs:WORD +EXTRN mapsegs:WORD +EXTRN originmap:WORD +EXTRN updatemapofs:WORD +EXTRN tinf:WORD ;seg pointer to map header and tile info +EXTRN blockstarts:WORD ;offsets from bufferofs for each update block + +planemask db ? +planenum db ? + +CODESEG + +screenstartcs dw ? ;in code segment for accesability + + + + +IFE GRMODE-CGAGR +;============================================================================ +; +; CGA refresh routines +; +;============================================================================ + +TILEWIDTH = 4 + +;================= +; +; RFL_NewTile +; +; Draws a composit two plane tile to the master screen and sets the update +; spot to 1 in both update pages, forcing the tile to be copied to the +; view pages the next two refreshes +; +; Called to draw newlly scrolled on strips and animating tiles +; +;================= + +PROC RFL_NewTile updateoffset:WORD +PUBLIC RFL_NewTile +USES SI,DI + +; +; mark both update lists at this spot +; + mov di,[updateoffset] + + mov bx,[updateptr] ;start of update matrix + mov [BYTE bx+di],1 + + mov dx,SCREENWIDTH-TILEWIDTH ;add to get to start of next line + +; +; set di to the location in screenseg to draw the tile +; + shl di,1 + mov si,[updatemapofs+di] ;offset in map from origin + add si,[originmap] + mov di,[blockstarts+di] ;screen location for tile + add di,[masterofs] + +; +; set BX to the foreground tile number and SI to the background number +; If either BX or SI = 0xFFFF, the tile does not need to be masked together +; as one of the planes totally eclipses the other +; + mov es,[mapsegs+2] ;foreground plane + mov bx,[es:si] + mov es,[mapsegs] ;background plane + mov si,[es:si] + + mov es,[screenseg] + + or bx,bx + jz @@singletile + jmp @@maskeddraw ;draw both together + +;============= +; +; Draw single background tile from main memory +; +;============= + +@@singletile: + shl si,1 + mov ds,[grsegs+STARTTILE16*2+si] + + xor si,si ;block is segment aligned + +REPT 15 + movsw + movsw + add di,dx +ENDM + movsw + movsw + + mov ax,ss + mov ds,ax ;restore turbo's data segment + ret + + +;========= +; +; Draw a masked tile combo +; Interupts are disabled and the stack segment is reassigned +; +;========= +@@maskeddraw: + cli ; don't allow ints when SS is set + shl bx,1 + mov ss,[grsegs+STARTTILE16M*2+bx] + shl si,1 + mov ds,[grsegs+STARTTILE16*2+si] + + xor si,si ;first word of tile data + +REPT 16 + mov ax,[si] ;background tile + and ax,[ss:si] ;mask + or ax,[ss:si+64] ;masked data + stosw + mov ax,[si+2] ;background tile + and ax,[ss:si+2] ;mask + or ax,[ss:si+66] ;masked data + stosw + add si,4 + add di,dx +ENDM + + mov ax,@DATA + mov ss,ax + sti + mov ds,ax + ret +ENDP + +ENDIF + + + +IFE GRMODE-EGAGR +;=========================================================================== +; +; EGA refresh routines +; +;=========================================================================== + +TILEWIDTH = 2 + +;================= +; +; RFL_NewTile +; +; Draws a composit two plane tile to the master screen and sets the update +; spot to 1 in both update pages, forcing the tile to be copied to the +; view pages the next two refreshes +; +; Called to draw newlly scrolled on strips and animating tiles +; +; Assumes write mode 0 +; +;================= + +PROC RFL_NewTile updateoffset:WORD +PUBLIC RFL_NewTile +USES SI,DI + +; +; mark both update lists at this spot +; + mov di,[updateoffset] + + mov bx,[updatestart] ;page 0 pointer + mov [BYTE bx+di],1 + mov bx,[updatestart+2] ;page 1 pointer + mov [BYTE bx+di],1 + +; +; set screenstartcs to the location in screenseg to draw the tile +; + shl di,1 + mov si,[updatemapofs+di] ;offset in map from origin + add si,[originmap] + mov di,[blockstarts+di] ;screen location for tile + add di,[masterofs] + mov [cs:screenstartcs],di + +; +; set BX to the foreground tile number and SI to the background number +; If either BX or SI = 0xFFFF, the tile does not need to be masked together +; as one of the planes totally eclipses the other +; + mov es,[mapsegs+2] ;foreground plane + mov bx,[es:si] + mov es,[mapsegs] ;background plane + mov si,[es:si] + + mov es,[screenseg] + mov dx,SC_INDEX ;for stepping through map mask planes + + or bx,bx + jz @@singletile + jmp @@maskeddraw ;draw both together + +;========= +; +; No foreground tile, so draw a single background tile. +; +;========= +@@singletile: + + mov bx,SCREENWIDTH-2 ;add to get to start of next line + shl si,1 + + mov ax,[cs:screenstartcs] + mov ds,[grsegs+STARTTILE16*2+si] + + xor si,si ;block is segment aligned + + mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0 + + mov cx,4 ;draw four planes +@@planeloop: + mov dx,SC_INDEX + WORDOUT + + mov di,[cs:screenstartcs] ;start at same place in all planes + +REPT 15 + movsw + add di,bx +ENDM + movsw + + shl ah,1 ;shift plane mask over for next plane + loop @@planeloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + ret + + +;========= +; +; Draw a masked tile combo +; Interupts are disabled and the stack segment is reassigned +; +;========= +@@maskeddraw: + cli ; don't allow ints when SS is set + shl bx,1 + mov ss,[grsegs+STARTTILE16M*2+bx] + shl si,1 + mov ds,[grsegs+STARTTILE16*2+si] + + xor si,si ;first word of tile data + + mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0 + + mov di,[cs:screenstartcs] +@@planeloopm: + WORDOUT +tileofs = 0 +lineoffset = 0 +REPT 16 + mov bx,[si+tileofs] ;background tile + and bx,[ss:tileofs] ;mask + or bx,[ss:si+tileofs+32] ;masked data + mov [es:di+lineoffset],bx +tileofs = tileofs + 2 +lineoffset = lineoffset + SCREENWIDTH +ENDM + add si,32 + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b + je @@done ;drawn all four planes + jmp @@planeloopm + +@@done: + mov ax,@DATA + mov ss,ax + sti + mov ds,ax + ret +ENDP + +ENDIF + +IFE GRMODE-VGAGR +;============================================================================ +; +; VGA refresh routines +; +;============================================================================ + + +ENDIF + + +;============================================================================ +; +; reasonably common refresh routines +; +;============================================================================ + + +;================= +; +; RFL_UpdateTiles +; +; Scans through the update matrix pointed to by updateptr, looking for 1s. +; A 1 represents a tile that needs to be copied from the master screen to the +; current screen (a new row or an animated tiled). If more than one adjacent +; tile in a horizontal row needs to be copied, they will be copied as a group. +; +; Assumes write mode 1 +; +;================= + + +; AX 0/1 for scasb, temp for segment register transfers +; BX width for block copies +; CX REP counter +; DX line width deltas +; SI source for copies +; DI scas dest / movsb dest +; BP pointer to UPDATETERMINATE +; +; DS +; ES +; SS + +PROC RFL_UpdateTiles +PUBLIC RFL_UpdateTiles +USES SI,DI,BP + + jmp SHORT @@realstart +@@done: +; +; all tiles have been scanned +; + ret + +@@realstart: + mov di,[updateptr] + mov bp,(TILESWIDE+1)*TILESHIGH+1 + add bp,di ; when di = bx, all tiles have been scanned + push di + mov cx,-1 ; definately scan the entire thing + +; +; scan for a 1 in the update list, meaning a tile needs to be copied +; from the master screen to the current screen +; +@@findtile: + pop di ; place to continue scaning from + mov ax,ss + mov es,ax ; search in the data segment + mov ds,ax + mov al,1 + repne scasb + cmp di,bp + je @@done + + cmp [BYTE di],al + jne @@singletile + jmp @@tileblock + +;============ +; +; copy a single tile +; +;============ +EVEN +@@singletile: + inc di ; we know the next tile is nothing + push di ; save off the spot being scanned + sub di,[updateptr] + shl di,1 + mov di,[blockstarts-4+di] ; start of tile location on screen + mov si,di + add di,[bufferofs] ; dest in current screen + add si,[masterofs] ; source in master screen + + mov dx,SCREENWIDTH-TILEWIDTH + mov ax,[screenseg] + mov ds,ax + mov es,ax + +;-------------------------- + +IFE GRMODE-CGAGR + +REPT 15 + movsw + movsw + add si,dx + add di,dx +ENDM + movsw + movsw + +ENDIF + +;-------------------------- + +IFE GRMODE-EGAGR + +REPT 15 + movsb + movsb + add si,dx + add di,dx +ENDM + movsb + movsb + +ENDIF + +;-------------------------- + + jmp @@findtile + +;============ +; +; more than one tile in a row needs to be updated, so do it as a group +; +;============ +EVEN +@@tileblock: + mov dx,di ; hold starting position + 1 in dx + inc di ; we know the next tile also gets updated + repe scasb ; see how many more in a row + push di ; save off the spot being scanned + + mov bx,di + sub bx,dx ; number of tiles in a row + shl bx,1 ; number of bytes / row + + mov di,dx ; lookup position of start tile + sub di,[updateptr] + shl di,1 + mov di,[blockstarts-2+di] ; start of tile location + mov si,di + add di,[bufferofs] ; dest in current screen + add si,[masterofs] ; source in master screen + + mov dx,SCREENWIDTH + sub dx,bx ; offset to next line on screen +IFE GRMODE-CGAGR + sub dx,bx ; bx is words wide in CGA tiles +ENDIF + + mov ax,[screenseg] + mov ds,ax + mov es,ax + +REPT 15 + mov cx,bx +IFE GRMODE-CGAGR + rep movsw +ENDIF +IFE GRMODE-EGAGR + rep movsb +ENDIF + add si,dx + add di,dx +ENDM + mov cx,bx +IFE GRMODE-CGAGR + rep movsw +ENDIF +IFE GRMODE-EGAGR + rep movsb +ENDIF + + dec cx ; was 0 from last rep movsb, now $ffff for scasb + jmp @@findtile + +ENDP + + +;============================================================================ + + +;================= +; +; RFL_MaskForegroundTiles +; +; Scan through update looking for 3's. If the foreground tile there is a +; masked foreground tile, draw it to the screen +; +;================= + +PROC RFL_MaskForegroundTiles +PUBLIC RFL_MaskForegroundTiles +USES SI,DI,BP + jmp SHORT @@realstart +@@done: +; +; all tiles have been scanned +; + ret + +@@realstart: + mov di,[updateptr] + mov bp,(TILESWIDE+1)*TILESHIGH+2 + add bp,di ; when di = bx, all tiles have been scanned + push di + mov cx,-1 ; definately scan the entire thing +; +; scan for a 3 in the update list +; +@@findtile: + mov ax,ss + mov es,ax ; scan in the data segment + mov al,3 + pop di ; place to continue scaning from + repne scasb + cmp di,bp + je @@done + +;============ +; +; found a tile, see if it needs to be masked on +; +;============ + + push di + + sub di,[updateptr] + shl di,1 + mov si,[updatemapofs-2+di] ; offset from originmap + add si,[originmap] + + mov es,[mapsegs+2] ; foreground map plane segment + mov si,[es:si] ; foreground tile number + + or si,si + jz @@findtile ; 0 = no foreground tile + + mov bx,si + add bx,INTILE ;INTILE tile info table + mov es,[tinf] + test [BYTE PTR es:bx],80h ;high bit = masked tile + jz @@findtile + +;------------------- + +IFE GRMODE-CGAGR +;================= +; +; mask the tile CGA +; +;================= + + mov di,[blockstarts-2+di] + add di,[bufferofs] + mov es,[screenseg] + shl si,1 + mov ds,[grsegs+STARTTILE16M*2+si] + + mov bx,64 ;data starts 64 bytes after mask + + xor si,si + +lineoffset = 0 +REPT 16 + mov ax,[es:di+lineoffset] ;background + and ax,[si] ;mask + or ax,[si+bx] ;masked data + mov [es:di+lineoffset],ax ;background + inc si + inc si + mov ax,[es:di+lineoffset+2] ;background + and ax,[si] ;mask + or ax,[si+bx] ;masked data + mov [es:di+lineoffset+2],ax ;background + inc si + inc si +lineoffset = lineoffset + SCREENWIDTH +ENDM +ENDIF + +;------------------- + +IFE GRMODE-EGAGR +;================= +; +; mask the tile +; +;================= + + mov [BYTE planemask],1 + mov [BYTE planenum],0 + + mov di,[blockstarts-2+di] + add di,[bufferofs] + mov [cs:screenstartcs],di + mov es,[screenseg] + shl si,1 + mov ds,[grsegs+STARTTILE16M*2+si] + + mov bx,32 ;data starts 32 bytes after mask + +@@planeloopm: + mov dx,SC_INDEX + mov al,SC_MAPMASK + mov ah,[ss:planemask] + WORDOUT + mov dx,GC_INDEX + mov al,GC_READMAP + mov ah,[ss:planenum] + WORDOUT + + xor si,si + mov di,[cs:screenstartcs] +lineoffset = 0 +REPT 16 + mov cx,[es:di+lineoffset] ;background + and cx,[si] ;mask + or cx,[si+bx] ;masked data + inc si + inc si + mov [es:di+lineoffset],cx +lineoffset = lineoffset + SCREENWIDTH +ENDM + add bx,32 ;the mask is now further away + inc [ss:planenum] + shl [ss:planemask],1 ;shift plane mask over for next plane + cmp [ss:planemask],10000b ;done all four planes? + je @@drawn ;drawn all four planes + jmp @@planeloopm + +@@drawn: +ENDIF + +;------------------- + + mov ax,ss + mov ds,ax + mov cx,-1 ;definately scan the entire thing + + jmp @@findtile + +ENDP + + +END + diff --git a/ID_SD.C b/ID_SD.C new file mode 100644 index 0000000..c8ee346 --- /dev/null +++ b/ID_SD.C @@ -0,0 +1,1295 @@ +/* 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 Engine +// ID_SD.c - Sound Manager +// v1.1d1 +// By Jason Blochowiak +// + +// +// This module handles dealing with generating sound on the appropriate +// hardware +// +// Depends on: User Mgr (for parm checking) +// +// Globals: +// For User Mgr: +// SoundSourcePresent - Sound Source thingie present? +// SoundBlasterPresent - SoundBlaster card present? +// AdLibPresent - AdLib card present? +// SoundMode - What device is used for sound effects +// (Use SM_SetSoundMode() to set) +// MusicMode - What device is used for music +// (Use SM_SetMusicMode() to set) +// For Cache Mgr: +// NeedsDigitized - load digitized sounds? +// NeedsMusic - load music? +// + +#pragma hdrstop // Wierdo thing with MUSE + +#include + +#ifdef _MUSE_ // Will be defined in ID_Types.h +#include "ID_SD.h" +#else +#include "ID_HEADS.H" +#endif +#pragma hdrstop +#pragma warn -pia + +#define SDL_SoundFinished() {SoundNumber = SoundPriority = 0;} + +// Macros for AdLib stuff +#define selreg(n) outportb(0x388,n) +#define writereg(n) outportb(0x389,n) +#define readstat() inportb(0x388) + +// Global variables + boolean SoundSourcePresent,SoundBlasterPresent,AdLibPresent, + NeedsDigitized,NeedsMusic; + SDMode SoundMode; + SMMode MusicMode; + longword TimeCount; + word HackCount; + word *SoundTable; // Really * _seg *SoundTable, but that don't work + boolean ssIsTandy; + word ssPort = 2; + +// Internal variables +static boolean SD_Started; +static boolean TimerDone; +static word TimerVal,TimerDelay10,TimerDelay25,TimerDelay100; +static longword TimerDivisor,TimerCount; +static char *ParmStrings[] = + { + "noal", + nil + }; +static void (*SoundUserHook)(void); +static word SoundNumber,SoundPriority; +static void interrupt (*t0OldService)(void); +//static word t0CountTable[] = {8,8,8,8,40,40}; +static long LocalTime; + +// PC Sound variables +static byte pcLastSample,far *pcSound; +static longword pcLengthLeft; +static word pcSoundLookup[255]; + +// AdLib variables +static boolean alNoCheck; +static byte far *alSound; +static word alBlock; +static longword alLengthLeft; +static longword alTimeCount; +static Instrument alZeroInst; + +// This table maps channel numbers to carrier and modulator op cells +static byte carriers[9] = { 3, 4, 5,11,12,13,19,20,21}, + modifiers[9] = { 0, 1, 2, 8, 9,10,16,17,18}, +// This table maps percussive voice numbers to op cells + pcarriers[5] = {19,0xff,0xff,0xff,0xff}, + pmodifiers[5] = {16,17,18,20,21}; + +// Sequencer variables +static boolean sqActive; +static word alFXReg; +static ActiveTrack *tracks[sqMaxTracks], + mytracks[sqMaxTracks]; +static word sqMode,sqFadeStep; +static word far *sqHack,far *sqHackPtr,sqHackLen,sqHackSeqLen; +static long sqHackTime; + +// Internal routines + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_SetTimer0() - Sets system timer 0 to the specified speed +// +/////////////////////////////////////////////////////////////////////////// +#pragma argsused +static void +SDL_SetTimer0(word speed) +{ +#ifndef TPROF // If using Borland's profiling, don't screw with the timer + outportb(0x43,0x36); // Change timer 0 + outportb(0x40,speed); + outportb(0x40,speed >> 8); + TimerDivisor = speed; +#else + TimerDivisor = 0x10000; +#endif +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_SetIntsPerSec() - Uses SDL_SetTimer0() to set the number of +// interrupts generated by system timer 0 per second +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_SetIntsPerSec(word ints) +{ + SDL_SetTimer0(1192030 / ints); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_TimingService() - Used by SDL_InitDelay() to determine a timing +// value for the current system that we're running on +// +/////////////////////////////////////////////////////////////////////////// +static void interrupt +SDL_TimingService(void) +{ + TimerVal = _CX; + TimerDone++; + + outportb(0x20,0x20); // Ack interrupt +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_InitDelay() - Sets up TimerDelay's for SDL_Delay() +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_InitDelay(void) +{ + int i; + word timer; + + setvect(8,SDL_TimingService); // Set to my timer 0 ISR + + SDL_SetIntsPerSec(1000); // Time 1ms + + for (i = 0,timer = 0;i < 10;i++) // Do timing test 10 times + { + asm xor dx,dx // Zero DX + asm mov cx,0xffff // Put starting value in CX + asm mov [TimerDone],cx // TimerDone = false - 1 +startloop: + asm or [TimerDone],0 + asm jnz startloop // Make sure we're at the start +loop: + asm test [TimerDone],1 // See if TimerDone flag got hit + asm jnz done // Yep - drop out of the loop + asm loop loop +done: + + if (0xffff - TimerVal > timer) + timer = 0xffff - TimerVal; + } + timer += timer / 2; // Use some slop + TimerDelay10 = timer / (1000 / 10); + TimerDelay25 = timer / (1000 / 25); + TimerDelay100 = timer / (1000 / 100); + + SDL_SetTimer0(0); // Reset timer 0 + + setvect(8,t0OldService); // Set back to old ISR +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_Delay() - Delays the specified amount of time +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_Delay(word delay) +{ + if (!delay) + return; + +asm mov cx,[delay] +loop: +asm test [TimerDone],0 // Useless code - just for timing equivilency +asm jnz done +asm loop loop +done:; +} + +// +// PC Sound code +// + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_PCPlaySound() - Plays the specified sound on the PC speaker +// +/////////////////////////////////////////////////////////////////////////// +#ifdef _MUSE_ +void +#else +static void +#endif +SDL_PCPlaySound(PCSound far *sound) +{ +asm pushf +asm cli + + pcLastSample = -1; + pcLengthLeft = sound->common.length; + pcSound = sound->data; + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_PCStopSound() - Stops the current sound playing on the PC Speaker +// +/////////////////////////////////////////////////////////////////////////// +#ifdef _MUSE_ +void +#else +static void +#endif +SDL_PCStopSound(void) +{ +asm pushf +asm cli + + (long)pcSound = 0; + +asm in al,0x61 // Turn the speaker off +asm and al,0xfd // ~2 +asm out 0x61,al + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_PCService() - Handles playing the next sample in a PC sound +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_PCService(void) +{ + byte s; + word t; + + if (pcSound) + { + s = *pcSound++; + if (s != pcLastSample) + { + asm pushf + asm cli + + pcLastSample = s; + if (s) // We have a frequency! + { + t = pcSoundLookup[s]; + asm mov bx,[t] + + asm mov al,0xb6 // Write to channel 2 (speaker) timer + asm out 43h,al + asm mov al,bl + asm out 42h,al // Low byte + asm mov al,bh + asm out 42h,al // High byte + + asm in al,0x61 // Turn the speaker & gate on + asm or al,3 + asm out 0x61,al + } + else // Time for some silence + { + asm in al,0x61 // Turn the speaker & gate off + asm and al,0xfc // ~3 + asm out 0x61,al + } + + asm popf + } + + if (!(--pcLengthLeft)) + { + SDL_PCStopSound(); + SDL_SoundFinished(); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ShutPC() - Turns off the pc speaker +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_ShutPC(void) +{ +asm pushf +asm cli + + pcSound = 0; + +asm in al,0x61 // Turn the speaker & gate off +asm and al,0xfc // ~3 +asm out 0x61,al + +asm popf +} + +// AdLib Code + +/////////////////////////////////////////////////////////////////////////// +// +// alOut(n,b) - Puts b in AdLib card register n +// +/////////////////////////////////////////////////////////////////////////// +void +alOut(byte n,byte b) +{ +asm pushf +asm cli + +asm mov dx,0x388 +asm mov al,[n] +asm out dx,al +#if 0 + SDL_Delay(TimerDelay10); +#else +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +#endif + +asm mov dx,0x389 +asm mov al,[b] +asm out dx,al + +asm popf + +#if 0 + SDL_Delay(TimerDelay25); +#else +asm mov dx,0x388 +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx + +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx + +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx + +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +asm in al, dx +#endif +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// SDL_SetInstrument() - Puts an instrument into a generator +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_SetInstrument(int track,int which,Instrument far *inst,boolean percussive) +{ + byte c,m; + + if (percussive) + { + c = pcarriers[which]; + m = pmodifiers[which]; + } + else + { + c = carriers[which]; + m = modifiers[which]; + } + + tracks[track - 1]->inst = *inst; + tracks[track - 1]->percussive = percussive; + + alOut(m + alChar,inst->mChar); + alOut(m + alScale,inst->mScale); + alOut(m + alAttack,inst->mAttack); + alOut(m + alSus,inst->mSus); + alOut(m + alWave,inst->mWave); + + // Most percussive instruments only use one cell + if (c != 0xff) + { + alOut(c + alChar,inst->cChar); + alOut(c + alScale,inst->cScale); + alOut(c + alAttack,inst->cAttack); + alOut(c + alSus,inst->cSus); + alOut(c + alWave,inst->cWave); + } + + alOut(which + alFeedCon,inst->nConn); // DEBUG - I think this is right +} +#endif + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ALStopSound() - Turns off any sound effects playing through the +// AdLib card +// +/////////////////////////////////////////////////////////////////////////// +#ifdef _MUSE_ +void +#else +static void +#endif +SDL_ALStopSound(void) +{ +asm pushf +asm cli + + (long)alSound = 0; + alOut(alFreqH + 0,0); + +asm popf +} + +static void +SDL_AlSetFXInst(Instrument far *inst) +{ + byte c,m; + + m = modifiers[0]; + c = carriers[0]; + alOut(m + alChar,inst->mChar); + alOut(m + alScale,inst->mScale); + alOut(m + alAttack,inst->mAttack); + alOut(m + alSus,inst->mSus); + alOut(m + alWave,inst->mWave); + alOut(c + alChar,inst->cChar); + alOut(c + alScale,inst->cScale); + alOut(c + alAttack,inst->cAttack); + alOut(c + alSus,inst->cSus); + alOut(c + alWave,inst->cWave); + // DEBUG!!! - I just put this in +// alOut(alFeedCon,inst->nConn); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ALPlaySound() - Plays the specified sound on the AdLib card +// +/////////////////////////////////////////////////////////////////////////// +#ifdef _MUSE_ +void +#else +static void +#endif +SDL_ALPlaySound(AdLibSound far *sound) +{ + Instrument far *inst; + + SDL_ALStopSound(); + +asm pushf +asm cli + + alLengthLeft = sound->common.length; + alSound = sound->data; + alBlock = ((sound->block & 7) << 2) | 0x20; + inst = &sound->inst; + + if (!(inst->mSus | inst->cSus)) + { + asm popf + Quit("SDL_ALPlaySound() - Bad instrument"); + } + + SDL_AlSetFXInst(inst); + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ALSoundService() - Plays the next sample out through the AdLib card +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_ALSoundService(void) +{ + byte s; + + if (alSound) + { + s = *alSound++; + if (!s) + alOut(alFreqH + 0,0); + else + { + alOut(alFreqL + 0,s); + alOut(alFreqH + 0,alBlock); + } + + if (!(--alLengthLeft)) + { + (long)alSound = 0; + alOut(alFreqH + 0,0); + SDL_SoundFinished(); + } + } +} + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// SDL_SelectMeasure() - sets up sequencing variables for a given track +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_SelectMeasure(ActiveTrack *track) +{ + track->seq = track->moods[track->mood]; + track->nextevent = 0; +} +#endif + +static void +SDL_ALService(void) +{ + byte a,v; + word w; + + if (!sqActive) + return; + + while (sqHackLen && (sqHackTime <= alTimeCount)) + { + w = *sqHackPtr++; + sqHackTime = alTimeCount + *sqHackPtr++; + asm mov dx,[w] + asm mov [a],dl + asm mov [v],dh + alOut(a,v); + sqHackLen -= 4; + } + alTimeCount++; + if (!sqHackLen) + { + sqHackPtr = (word far *)sqHack; + sqHackLen = sqHackSeqLen; + alTimeCount = sqHackTime = 0; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_ShutAL() - Shuts down the AdLib card for sound effects +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_ShutAL(void) +{ +asm pushf +asm cli + + alOut(alEffects,0); + alOut(alFreqH + 0,0); + SDL_AlSetFXInst(&alZeroInst); + alSound = 0; + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_CleanAL() - Totally shuts down the AdLib card +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_CleanAL(void) +{ + int i; + +asm pushf +asm cli + + alOut(alEffects,0); + for (i = 1;i < 0xf5;i++) + alOut(i,0); + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_StartAL() - Starts up the AdLib card for sound effects +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_StartAL(void) +{ + alFXReg = 0; + alOut(alEffects,alFXReg); + SDL_AlSetFXInst(&alZeroInst); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_DetectAdLib() - Determines if there's an AdLib (or SoundBlaster +// emulating an AdLib) present +// +/////////////////////////////////////////////////////////////////////////// +static boolean +SDL_DetectAdLib(void) +{ + byte status1,status2; + int i; + + alOut(4,0x60); // Reset T1 & T2 + alOut(4,0x80); // Reset IRQ + status1 = readstat(); + alOut(2,0xff); // Set timer 1 + alOut(4,0x21); // Start timer 1 + SDL_Delay(TimerDelay100); + + status2 = readstat(); + alOut(4,0x60); + alOut(4,0x80); + + if (((status1 & 0xe0) == 0x00) && ((status2 & 0xe0) == 0xc0)) + { + for (i = 1;i <= 0xf5;i++) // Zero all the registers + alOut(i,0); + + alOut(1,0x20); // Set WSE=1 + alOut(8,0); // Set CSM=0 & SEL=0 + + return(true); + } + else + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_t0Service() - My timer 0 ISR which handles the different timings and +// dispatches to whatever other routines are appropriate +// +/////////////////////////////////////////////////////////////////////////// +static void interrupt +SDL_t0Service(void) +{ +static word count = 1; + +#if 0 // for debugging +asm mov dx,STATUS_REGISTER_1 +asm in al,dx +asm mov dx,ATR_INDEX +asm mov al,ATR_OVERSCAN +asm out dx,al +asm mov al,4 // red +asm out dx,al +#endif + + HackCount++; + + if (MusicMode == smm_AdLib) + { + SDL_ALService(); + if (!(++count & 7)) + { + LocalTime++; + TimeCount++; + if (SoundUserHook) + SoundUserHook(); + } + if (!(count & 3)) + { + switch (SoundMode) + { + case sdm_PC: + SDL_PCService(); + break; + case sdm_AdLib: + SDL_ALSoundService(); + break; + } + } + } + else + { + if (!(++count & 1)) + { + LocalTime++; + TimeCount++; + if (SoundUserHook) + SoundUserHook(); + } + switch (SoundMode) + { + case sdm_PC: + SDL_PCService(); + break; + case sdm_AdLib: + SDL_ALSoundService(); + break; + } + } + +asm mov ax,[WORD PTR TimerCount] +asm add ax,[WORD PTR TimerDivisor] +asm mov [WORD PTR TimerCount],ax +asm jnc myack + t0OldService(); // If we overflow a word, time to call old int handler +asm jmp olddone +myack:; + outportb(0x20,0x20); // Ack the interrupt +olddone:; + +#if 0 // for debugging +asm mov dx,STATUS_REGISTER_1 +asm in al,dx +asm mov dx,ATR_INDEX +asm mov al,ATR_OVERSCAN +asm out dx,al +asm mov al,3 // blue +asm out dx,al +asm mov al,0x20 // normal +asm out dx,al +#endif +} + +//////////////////////////////////////////////////////////////////////////// +// +// SDL_ShutDevice() - turns off whatever device was being used for sound fx +// +//////////////////////////////////////////////////////////////////////////// +static void +SDL_ShutDevice(void) +{ + switch (SoundMode) + { + case sdm_PC: + SDL_ShutPC(); + break; + case sdm_AdLib: + SDL_ShutAL(); + break; + } + SoundMode = sdm_Off; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_CleanDevice() - totally shuts down all sound devices +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_CleanDevice(void) +{ + if ((SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib)) + SDL_CleanAL(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDL_StartDevice() - turns on whatever device is to be used for sound fx +// +/////////////////////////////////////////////////////////////////////////// +static void +SDL_StartDevice(void) +{ + switch (SoundMode) + { + case sdm_AdLib: + SDL_StartAL(); + break; + } + SoundNumber = SoundPriority = 0; +} + +static void +SDL_SetTimerSpeed(void) +{ + word rate; + + if (MusicMode == smm_AdLib) + rate = TickBase * 8; + else + rate = TickBase * 2; + SDL_SetIntsPerSec(rate); +} + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// SD_SetSoundMode() - Sets which sound hardware to use for sound effects +// +/////////////////////////////////////////////////////////////////////////// +boolean +SD_SetSoundMode(SDMode mode) +{ + boolean result; + word tableoffset; + + SD_StopSound(); + +#ifndef _MUSE_ + switch (mode) + { + case sdm_Off: + NeedsDigitized = false; + result = true; + break; + case sdm_PC: + tableoffset = STARTPCSOUNDS; + NeedsDigitized = false; + result = true; + break; + case sdm_AdLib: + if (AdLibPresent) + { + tableoffset = STARTADLIBSOUNDS; + NeedsDigitized = false; + result = true; + } + break; + default: + result = false; + break; + } +#endif + + if (result && (mode != SoundMode)) + { + SDL_ShutDevice(); + SoundMode = mode; +#ifndef _MUSE_ + SoundTable = (word *)(&audiosegs[tableoffset]); +#endif + SDL_StartDevice(); + } + + SDL_SetTimerSpeed(); + + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_SetMusicMode() - sets the device to use for background music +// +/////////////////////////////////////////////////////////////////////////// +boolean +SD_SetMusicMode(SMMode mode) +{ + boolean result; + + SD_FadeOutMusic(); + while (SD_MusicPlaying()) + ; + + switch (mode) + { + case smm_Off: + NeedsMusic = false; + result = true; + break; + case smm_AdLib: + if (AdLibPresent) + { + NeedsMusic = true; + result = true; + } + break; + default: + result = false; + break; + } + + if (result) + MusicMode = mode; + + SDL_SetTimerSpeed(); + + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_Startup() - starts up the Sound Mgr +// Detects all additional sound hardware and installs my ISR +// +/////////////////////////////////////////////////////////////////////////// +void +SD_Startup(void) +{ + int i; + + if (SD_Started) + return; + + ssIsTandy = false; + alNoCheck = false; +#ifndef _MUSE_ + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],ParmStrings)) + { + case 0: // No AdLib detection + alNoCheck = true; + break; + } + } +#endif + + SoundUserHook = 0; + + t0OldService = getvect(8); // Get old timer 0 ISR + + SDL_InitDelay(); // SDL_InitDelay() uses t0OldService + + setvect(8,SDL_t0Service); // Set to my timer 0 ISR + LocalTime = TimeCount = alTimeCount = 0; + + SD_SetSoundMode(sdm_Off); + SD_SetMusicMode(smm_Off); + + if (!alNoCheck) + AdLibPresent = SDL_DetectAdLib(); + + for (i = 0;i < 255;i++) + pcSoundLookup[i] = i * 60; + + SD_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_Default() - Sets up the default behaviour for the Sound Mgr whether +// the config file was present or not. +// +/////////////////////////////////////////////////////////////////////////// +void +SD_Default(boolean gotit,SDMode sd,SMMode sm) +{ + boolean gotsd,gotsm; + + gotsd = gotsm = gotit; + + if (gotsd) // Make sure requested sound hardware is available + { + switch (sd) + { + case sdm_AdLib: + gotsd = AdLibPresent; + break; + } + } + if (!gotsd) + { + if (AdLibPresent) + sd = sdm_AdLib; + else + sd = sdm_PC; + } + if (sd != SoundMode) + SD_SetSoundMode(sd); + + + if (gotsm) // Make sure requested music hardware is available + { + switch (sm) + { + case sdm_AdLib: + gotsm = AdLibPresent; + break; + } + } + if (!gotsm) + { + if (AdLibPresent) + sm = smm_AdLib; + } + if (sm != MusicMode) + SD_SetMusicMode(sm); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_Shutdown() - shuts down the Sound Mgr +// Removes sound ISR and turns off whatever sound hardware was active +// +/////////////////////////////////////////////////////////////////////////// +void +SD_Shutdown(void) +{ + if (!SD_Started) + return; + + SD_MusicOff(); + SDL_ShutDevice(); + SDL_CleanDevice(); + + asm pushf + asm cli + + SDL_SetTimer0(0); + + setvect(8,t0OldService); + + asm popf + + SD_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_SetUserHook() - sets the routine that the Sound Mgr calls every 1/70th +// of a second from its timer 0 ISR +// +/////////////////////////////////////////////////////////////////////////// +void +SD_SetUserHook(void (* hook)(void)) +{ + SoundUserHook = hook; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_PlaySound() - plays the specified sound on the appropriate hardware +// +/////////////////////////////////////////////////////////////////////////// +void +SD_PlaySound(soundnames sound) +{ + SoundCommon far *s; + + if ((SoundMode == sdm_Off) || (sound == -1)) + return; + + s = MK_FP(SoundTable[sound],0); + if (!s) + Quit("SD_PlaySound() - Uncached sound"); + if (!s->length) + Quit("SD_PlaySound() - Zero length sound"); + if (s->priority < SoundPriority) + return; + + switch (SoundMode) + { + case sdm_PC: + SDL_PCPlaySound((void far *)s); + break; + case sdm_AdLib: + SDL_ALPlaySound((void far *)s); + break; + } + + SoundNumber = sound; + SoundPriority = s->priority; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_SoundPlaying() - returns the sound number that's playing, or 0 if +// no sound is playing +// +/////////////////////////////////////////////////////////////////////////// +word +SD_SoundPlaying(void) +{ + boolean result = false; + + switch (SoundMode) + { + case sdm_PC: + result = pcSound? true : false; + break; + case sdm_AdLib: + result = alSound? true : false; + break; + } + + if (result) + return(SoundNumber); + else + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_StopSound() - if a sound is playing, stops it +// +/////////////////////////////////////////////////////////////////////////// +void +SD_StopSound(void) +{ + switch (SoundMode) + { + case sdm_PC: + SDL_PCStopSound(); + break; + case sdm_AdLib: + SDL_ALStopSound(); + break; + } + + SDL_SoundFinished(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_WaitSoundDone() - waits until the current sound is done playing +// +/////////////////////////////////////////////////////////////////////////// +void +SD_WaitSoundDone(void) +{ + while (SD_SoundPlaying()) + ; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_MusicOn() - turns on the sequencer +// +/////////////////////////////////////////////////////////////////////////// +void +SD_MusicOn(void) +{ + sqActive = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_MusicOff() - turns off the sequencer and any playing notes +// +/////////////////////////////////////////////////////////////////////////// +void +SD_MusicOff(void) +{ + word i; + + + switch (MusicMode) + { + case smm_AdLib: + alFXReg = 0; + alOut(alEffects,0); + for (i = 0;i < sqMaxTracks;i++) + alOut(alFreqH + i + 1,0); + break; + } + sqActive = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_StartMusic() - starts playing the music pointed to +// +/////////////////////////////////////////////////////////////////////////// +void +SD_StartMusic(MusicGroup far *music) +{ + SD_MusicOff(); +asm pushf +asm cli + + if (MusicMode == smm_AdLib) + { + sqHackPtr = sqHack = music->values; + sqHackSeqLen = sqHackLen = music->length; + sqHackTime = 0; + alTimeCount = 0; + SD_MusicOn(); + } + +asm popf +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_FadeOutMusic() - starts fading out the music. Call SD_MusicPlaying() +// to see if the fadeout is complete +// +/////////////////////////////////////////////////////////////////////////// +void +SD_FadeOutMusic(void) +{ + switch (MusicMode) + { + case smm_AdLib: + // DEBUG - quick hack to turn the music off + SD_MusicOff(); + break; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// SD_MusicPlaying() - returns true if music is currently playing, false if +// not +// +/////////////////////////////////////////////////////////////////////////// +boolean +SD_MusicPlaying(void) +{ + boolean result; + + switch (MusicMode) + { + case smm_AdLib: + result = false; + // DEBUG - not written + break; + default: + result = false; + } + + return(result); +} diff --git a/ID_SD.H b/ID_SD.H new file mode 100644 index 0000000..701cb87 --- /dev/null +++ b/ID_SD.H @@ -0,0 +1,205 @@ +/* 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 Engine +// ID_SD.h - Sound Manager Header +// v1.0d1 +// By Jason Blochowiak +// + +#ifndef __TYPES__ +#include "ID_Types.h" +#endif + +#ifndef __ID_SD__ +#define __ID_SD__ + +#ifdef __DEBUG__ +#define __DEBUG_SoundMgr__ +#endif + +#define TickBase 70 // 70Hz per tick - used as a base for timer 0 + +typedef enum { + sdm_Off, + sdm_PC,sdm_AdLib, + } SDMode; +typedef enum { + smm_Off,smm_AdLib + } SMMode; + +typedef struct + { + longword length; + word priority; + } SoundCommon; + +// PC Sound stuff +#define pcTimer 0x42 +#define pcTAccess 0x43 +#define pcSpeaker 0x61 + +#define pcSpkBits 3 + +typedef struct + { + SoundCommon common; + byte data[1]; + } PCSound; + +// Registers for the Sound Blaster card - needs to be offset by n0 +#define sbReset 0x206 +#define sbReadData 0x20a +#define sbWriteCmd 0x20c +#define sbWriteData 0x20c +#define sbWriteStat 0x20c +#define sbDataAvail 0x20e + +typedef struct + { + SoundCommon common; + word hertz; + byte bits, + reference, + data[1]; + } SampledSound; + +// Registers for the AdLib card +// Operator stuff +#define alChar 0x20 +#define alScale 0x40 +#define alAttack 0x60 +#define alSus 0x80 +#define alWave 0xe0 +// Channel stuff +#define alFreqL 0xa0 +#define alFreqH 0xb0 +#define alFeedCon 0xc0 +// Global stuff +#define alEffects 0xbd + +typedef struct + { + byte mChar,cChar, + mScale,cScale, + mAttack,cAttack, + mSus,cSus, + mWave,cWave, + nConn, + + // These are only for Muse - these bytes are really unused + voice, + mode, + unused[3]; + } Instrument; + +typedef struct + { + SoundCommon common; + Instrument inst; + byte block, + data[1]; + } AdLibSound; + +// +// Sequencing stuff +// +#define sqMaxTracks 10 +#define sqMaxMoods 1 // DEBUG + +#define sev_Null 0 // Does nothing +#define sev_NoteOff 1 // Turns a note off +#define sev_NoteOn 2 // Turns a note on +#define sev_NotePitch 3 // Sets the pitch of a currently playing note +#define sev_NewInst 4 // Installs a new instrument +#define sev_NewPerc 5 // Installs a new percussive instrument +#define sev_PercOn 6 // Turns a percussive note on +#define sev_PercOff 7 // Turns a percussive note off +#define sev_SeqEnd -1 // Terminates a sequence + +// Flags for MusicGroup.flags +#define sf_Melodic 0 +#define sf_Percussive 1 + +#if 1 +typedef struct + { + word length, + values[1]; + } MusicGroup; +#else +typedef struct + { + word flags, + count, + offsets[1]; + } MusicGroup; +#endif + +typedef struct + { + /* This part needs to be set up by the user */ + word mood,far *moods[sqMaxMoods]; + + /* The rest is set up by the code */ + Instrument inst; + boolean percussive; + word far *seq; + longword nextevent; + } ActiveTrack; + +#define sqmode_Normal 0 +#define sqmode_FadeIn 1 +#define sqmode_FadeOut 2 + +#define sqMaxFade 64 // DEBUG + + +// Global variables +extern boolean AdLibPresent, + NeedsMusic; // For Caching Mgr +extern SDMode SoundMode; +extern SMMode MusicMode; +extern longword TimeCount; // Global time in ticks + +// Function prototypes +extern void SD_Startup(void), + SD_Shutdown(void), + SD_Default(boolean gotit,SDMode sd,SMMode sm), + SD_PlaySound(soundnames sound), + SD_StopSound(void), + SD_WaitSoundDone(void), + SD_StartMusic(MusicGroup far *music), + SD_MusicOn(void), + SD_MusicOff(void), + SD_FadeOutMusic(void), + SD_SetUserHook(void (*hook)(void)); +extern boolean SD_MusicPlaying(void), + SD_SetSoundMode(SDMode mode), + SD_SetMusicMode(SMMode mode); +extern word SD_SoundPlaying(void); + +#ifdef _MUSE_ // MUSE Goes directly to the lower level routines +extern void SDL_PCPlaySound(PCSound far *sound), + SDL_PCStopSound(void), + SDL_ALPlaySound(AdLibSound far *sound), + SDL_ALStopSound(void); +#endif + +#endif diff --git a/ID_STRS.H b/ID_STRS.H new file mode 100644 index 0000000..686e281 --- /dev/null +++ b/ID_STRS.H @@ -0,0 +1,128 @@ +/* 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. + */ + +#define S_LOADING "Loading" +#define S_EMPTYSPOT "Empty" +#define S_SVGACOMP "SVGA Compatibility Mode Enabled." +#define S_READYPRESS " Ready - Press a Key " +#define S_NOSFX "NO SOUND EFFECTS" +#define S_PCSPKR "PC SPEAKER" +#define S_ALSB "ADLIB/SOUNDBLASTER" +#define S_QUIET "QUIET ADLIB/SOUNDBLASTER" +#define S_NOMUSIC "NO MUSIC" +#define S_BEGINE "BEGIN EASY GAME" +#define S_BEGINN "BEGIN NORMAL GAME" +#define S_BEGINH "BEGIN HARD GAME" +#define S_UPLEFT "UP & LEFT" +#define S_UP "UP" +#define S_UPRIGHT "UP & RIGHT" +#define S_RIGHT "RIGHT" +#define S_DNRIGHT "DOWN & RIGHT" +#define S_DN "DOWN" +#define S_DNLEFT "DOWN & LEFT" +#define S_LEFT "LEFT" + +#define S_JUMP "JUMP" +#define S_POGO "POGO" +#define S_FIRE "FIRE" +#define S_MOVEMENT "MOVEMENT" +#define S_BUTTONS "BUTTONS" + +#define S_SOUND "SOUND" +#define S_MUSIC "MUSIC" +#define S_OPTIONS "OPTIONS" +#define S_USEKB "USE KEYBOARD" +#define S_USEJOY1 "USE JOYSTICK #1" +#define S_USEJOY2 "USE JOYSTICK #2" +#define S_NEWGAME "NEW GAME" +#define S_LOADGAME "LOAD GAME" +#define S_SAVEGAME "SAVE GAME" +#define S_CONFIG "CONFIGURE" +#define S_ENDGAME "END GAME" +#define S_PADDLEWAR "PADDLE WAR" +#define S_QUIT "QUIT" + +#define S_ESCBACK "ESC TO BACK OUT" +#define S_ESCBACK1 "ESC to back out" +#define S_ESCQUIT "ESC to quit" +#define S_F1HELP "F1 for help" +#define S_REALLYEND "REALLY END CURRENT GAME?" +#define S_PRESSY "PRESS Y TO END IT" +#define S_REALLYQUIT "REALLY QUIT?" +#define S_PRESSYQ "PRESS Y TO QUIT" +#define S_INAGAME "YOU'RE IN A GAME" +#define S_PRESSY2L "PRESS Y TO LOAD GAME" +#define S_PRESSY4N "PRESS Y FOR NEW GAME" + +#define S_USLERROR "Error: " +#define S_USLUNKNOWN "Unknown" +#define S_USLDISKFULL "Disk is Full" +#define S_USLFILEINC "File is Incomplete" +#define S_PRESSKEY "PRESS ANY KEY" +#define S_PRESSKEY1 "Press any key" + +#define S_SBOXON "SCORE BOX (ON)" +#define S_SBOXOFF "SCORE BOX (OFF)" +#define S_SVGAON "SVGA COMPATIBILITY (ON)" +#define S_SVGAOFF "SVGA COMPATIBILITY (OFF)" +#define S_2BON "TWO-BUTTON FIRING (ON)" +#define S_2BOFF "TWO-BUTTON FIRING (OFF)" + +#define S_SBOXNOWON "Score box now on" +#define S_SBOXNOWOFF "Score box now off" +#define S_SVGANOWON "SVGA compatibility now on" +#define S_SVGANOWOFF "SVGA compatibility now off" +#define S_2BNOWON "Two-button firing now on" +#define S_2BNOWOFF "Two-button firing now off" + +#define S_KEYBOARD "KEYBOARD" +#define S_JOY1 "JOYSTICK #1" +#define S_JOY2 "JOYSTICK #2" +#define S_MOUSE "MOUSE" +#define S_CONTROL "CONTROL: " +#define S_KEYUSED "Key already used" +#define S_PB1 "and press button #1" +#define S_PB2 "and press button #2" +#define S_MJUL "Move Joystick to upper left" +#define S_MJLR "Move Joystick to lower right" + +#define S_USINGJ1 "USING "S_JOY1 +#define S_USINGJ2 "USING "S_JOY2 +#define S_TYPENAME "Type name" +#define S_ENTERACC "Enter accepts" +#define S_UNTITLED "Untitled" +#define S_SAVING "Saving" +#define S_YOULOST "You lost!" +#define S_YOUWON "You won!" +#define S_ARRMOVE "Arrows move" +#define S_ENTERSEL "Enter selects" + +#define S_RETGAME "RETURN TO GAME" +#define S_RETDEMO "RETURN TO DEMO" +#define S_CTRLPANEL "Control Panel" +#define S_QUITTING "Quitting..." + +#define S_WHATNAME "What is the name of this creature?" +#define S_SORRY "Sorry, that's not quite right." +#define S_CHECKMAN "Please check your manual and try again." + +#define S_BADCARD "Improper video card! If you really have an EGA/VGA card that I am not\n"\ + "detecting, use the -HIDDENCARD command line parameter!" +#define S_BADCARD1 "Improper video card! If you really have a CGA card that I am not\n"\ + "detecting, use the -HIDDENCARD command line parameter!" + diff --git a/ID_US.C b/ID_US.C new file mode 100644 index 0000000..c1bb93e --- /dev/null +++ b/ID_US.C @@ -0,0 +1,3691 @@ +/* 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 Engine +// ID_US.c - User Manager +// v1.0d1 +// By Jason Blochowiak +// + +// +// This module handles dealing with user input & feedback +// +// Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching, +// and Refresh Mgrs, Memory Mgr for background save/restore +// +// Globals: +// ingame - Flag set by game indicating if a game is in progress +// abortgame - Flag set if the current game should be aborted (if a load +// game fails) +// loadedgame - Flag set if a game was loaded +// abortprogram - Normally nil, this points to a terminal error message +// if the program needs to abort +// restartgame - Normally set to gd_Continue, this is set to one of the +// difficulty levels if a new game should be started +// PrintX, PrintY - Where the User Mgr will print (global coords) +// WindowX,WindowY,WindowW,WindowH - The dimensions of the current +// window +// + +// DEBUG - handle LPT3 for Sound Source + +#include "ID_HEADS.H" + +#define CTL_M_ADLIBUPPIC CTL_S_ADLIBUPPIC +#define CTL_M_ADLIBDNPIC CTL_S_ADLIBDNPIC + +#pragma hdrstop + +#pragma warn -pia + +#define MaxX 320 +#define MaxY 200 + +#define MaxHelpLines 500 + +#define MaxHighName 57 +#define MaxScores 10 +typedef struct + { + char name[MaxHighName + 1]; + long score; + word completed; + } HighScore; + +#define MaxGameName 32 +#define MaxSaveGames 7 +typedef struct + { + char signature[4]; + boolean present; + char name[MaxGameName + 1]; + } SaveGame; + +// Hack import for TED launch support +extern boolean tedlevel; +extern word tedlevelnum; +extern void TEDDeath(void); +static char *ParmStrings[] = {"TEDLEVEL","NOWAIT",""}; + + +// Global variables + boolean ingame,abortgame,loadedgame; + char *abortprogram; + GameDiff restartgame = gd_Continue; + word PrintX,PrintY; + word WindowX,WindowY,WindowW,WindowH; + +// Internal variables +static boolean US_Started; +static boolean GameIsDirty, + HighScoresDirty, + QuitToDos, + ResumeGame, + NoWait; + +static memptr LineOffsets; + +static boolean Button0,Button1, + CursorBad; +static int CursorX,CursorY; + +static void (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString, + (*USL_DrawString)(char far *) = VWB_DrawPropString; + +static boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int); +static void (*USL_ResetGame)(void); +static SaveGame Games[MaxSaveGames]; +static HighScore Scores[MaxScores] = + { + {"Sir Lancelot",500}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0} + }; + +// Internal routines + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed +// from DOS. +// +/////////////////////////////////////////////////////////////////////////// +#pragma warn -par +#pragma warn -rch +int +USL_HardError(word errval,int ax,int bp,int si) +{ +#define IGNORE 0 +#define RETRY 1 +#define ABORT 2 +extern void ShutdownId(void); + +static char buf[32]; +static WindowRec wr; +static boolean oldleavedriveon; + int di; + char c,*s,*t; + + + di = _DI; + + oldleavedriveon = LeaveDriveOn; + LeaveDriveOn = false; + + if (ax < 0) + s = "Device Error"; + else + { + if ((di & 0x00ff) == 0) + s = "Drive ~ is Write Protected"; + else + s = "Error on Drive ~"; + for (t = buf;*s;s++,t++) // Can't use sprintf() + if ((*t = *s) == '~') + *t = (ax & 0x00ff) + 'A'; + *t = '\0'; + s = buf; + } + + c = peekb(0x40,0x49); // Get the current screen mode + if ((c < 4) || (c == 7)) + goto oh_kill_me; + + // DEBUG - handle screen cleanup + + US_SaveWindow(&wr); + US_CenterWindow(30,3); + US_CPrint(s); + US_CPrint("(R)etry or (A)bort?"); + VW_UpdateScreen(); + IN_ClearKeysDown(); + +asm sti // Let the keyboard interrupts come through + + while (true) + { + switch (IN_WaitForASCII()) + { + case key_Escape: + case 'a': + case 'A': + goto oh_kill_me; + break; + case key_Return: + case key_Space: + case 'r': + case 'R': + US_ClearWindow(); + VW_UpdateScreen(); + US_RestoreWindow(&wr); + LeaveDriveOn = oldleavedriveon; + return(RETRY); + break; + } + } + +oh_kill_me: + abortprogram = s; + ShutdownId(); + fprintf(stderr,"Terminal Error: %s\n",s); + if (tedlevel) + fprintf(stderr,"You launched from TED. I suggest that you reboot...\n"); + + return(ABORT); +#undef IGNORE +#undef RETRY +#undef ABORT +} +#pragma warn +par +#pragma warn +rch + +/////////////////////////////////////////////////////////////////////////// +// +// USL_GiveSaveName() - Returns a pointer to a static buffer that contains +// the filename to use for the specified save game +// +/////////////////////////////////////////////////////////////////////////// +static char * +USL_GiveSaveName(word game) +{ +static char filename[32]; + char *s,*t; + + for (s = "SAVEGM",t = filename;*s;) + *t++ = *s++; + *t++ = game + '0'; + for (s = "."EXTENSION;*s;) + *t++ = *s++; + *t = '\0'; + + return(filename); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after +// reading or writing the save game headers +// +/////////////////////////////////////////////////////////////////////////// +void +US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void)) +{ + USL_LoadGame = load; + USL_SaveGame = save; + USL_ResetGame = reset; +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ReadConfig() - Reads the configuration file, if present, and sets +// things up accordingly. If it's not present, uses defaults. This file +// includes the high scores. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ReadConfig(void) +{ + boolean gotit; + int file; + SDMode sd; + SMMode sm; + ControlType ctl; + + if ((file = open("CONFIG."EXTENSION,O_BINARY | O_RDONLY)) != -1) + { + read(file,Scores,sizeof(HighScore) * MaxScores); + read(file,&sd,sizeof(sd)); + read(file,&sm,sizeof(sm)); + read(file,&ctl,sizeof(ctl)); + read(file,&(KbdDefs[0]),sizeof(KbdDefs[0])); + close(file); + + HighScoresDirty = false; + gotit = true; + } + else + { + sd = sdm_Off; + sm = smm_Off; + ctl = ctrl_Keyboard; + + gotit = false; + HighScoresDirty = true; + } + + SD_Default(gotit,sd,sm); + IN_Default(gotit,ctl); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_WriteConfig() - Writes out the current configuration, including the +// high scores. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_WriteConfig(void) +{ + int file; + + file = open("CONFIG."EXTENSION,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + if (file != -1) + { + write(file,Scores,sizeof(HighScore) * MaxScores); + write(file,&SoundMode,sizeof(SoundMode)); + write(file,&MusicMode,sizeof(MusicMode)); + write(file,&(Controls[0]),sizeof(Controls[0])); + write(file,&(KbdDefs[0]),sizeof(KbdDefs[0])); + close(file); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CheckSavedGames() - Checks to see which saved games are present +// & valid +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_CheckSavedGames(void) +{ + boolean ok; + char *filename; + word i; + int file; + SaveGame *game; + + USL_SaveGame = 0; + USL_LoadGame = 0; + + for (i = 0,game = Games;i < MaxSaveGames;i++,game++) + { + filename = USL_GiveSaveName(i); + ok = false; + if ((file = open(filename,O_BINARY | O_RDONLY)) != -1) + { + if + ( + (read(file,game,sizeof(*game)) == sizeof(*game)) + && (!strcmp(game->signature,EXTENSION)) + ) + ok = true; + + close(file); + } + + if (ok) + game->present = true; + else + { + strcpy(game->signature,EXTENSION); + game->present = false; + strcpy(game->name,"Empty"); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Startup() - Starts the User Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +US_Startup(void) +{ + if (US_Started) + return; + + harderr(USL_HardError); // Install the fatal error handler + + US_InitRndT(true); // Initialize the random number generator + + USL_ReadConfig(); // Read config file + + US_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Setup() - Does the disk access part of the User Mgr's startup +// +/////////////////////////////////////////////////////////////////////////// +void +US_Setup(void) +{ + USL_CheckSavedGames(); // Check which saved games are present +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Shutdown() - Shuts down the User Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +US_Shutdown(void) +{ + if (!US_Started) + return; + + if (!abortprogram) + USL_WriteConfig(); + + US_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CheckParm() - checks to see if a string matches one of a set of +// strings. The check is case insensitive. The routine returns the +// index of the string that matched, or -1 if no matches were found +// +/////////////////////////////////////////////////////////////////////////// +int +US_CheckParm(char *parm,char **strings) +{ + char cp,cs, + *p,*s; + int i; + + while (!isalpha(*parm)) // Skip non-alphas + parm++; + + for (i = 0;*strings && **strings;i++) + { + for (s = *strings++,p = parm,cs = cp = 0;cs == cp;) + { + cs = *s++; + if (!cs) + return(i); + cp = *p++; + + if (isupper(cs)) + cs = tolower(cs); + if (isupper(cp)) + cp = tolower(cp); + } + } + return(-1); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ScreenDraw() - Draws a chunk of the text screen (called only by +// US_TextScreen()) +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ScreenDraw(word x,word y,char *s,byte attr) +{ + byte far *screen; + + // screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2)); + // while (*s) + // { + // *screen++ = *s++; + // *screen++ = attr; + // } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it, +// and moves the cursor to the leftmost column of the bottom line +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ClearTextScreen(void) +{ + // Set to 80x25 color text mode + _AL = 3; // Mode 3 + _AH = 0x00; + geninterrupt(0x10); + + // Use BIOS to move the cursor to the bottom of the screen + _AH = 0x0f; + geninterrupt(0x10); // Get current video mode into _BH + _DL = 0; // Lefthand side of the screen + _DH = 24; // Bottom row + _AH = 0x02; + geninterrupt(0x10); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_TextScreen() - Puts up the startup text screen +// Note: These are the only User Manager functions that can be safely called +// before the User Mgr has been started up +// +/////////////////////////////////////////////////////////////////////////// +void +US_TextScreen(void) +{ + word i,n, + sx,sy; +extern char far introscn; + + USL_ClearTextScreen(); + +// _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2); + + // Check for TED launching here + for (i = 1;i < _argc;i++) + { + n = US_CheckParm(_argv[i],ParmStrings); + if (n == 0) + { + tedlevelnum = atoi(_argv[i + 1]); + if (tedlevelnum >= 0) + { + tedlevel = true; + return; + } + else + break; + } + else if (n == 1) + { + NoWait = true; + return; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_Show() - Changes the appearance of one of the fields on the text +// screen. Possibly adds a checkmark in front of it and highlights it +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_Show(word x,word y,word w,boolean show,boolean hilight) +{ + byte far *screen; + + screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2)); + *screen++ = show? 251 : ' '; // Checkmark char or space + *screen = 0x48; + if (show && hilight) + { + for (w++;w--;screen += 2) + *screen = 0x4f; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ShowMem() - Right justifies a longword in one of the memory fields on +// the text screen +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ShowMem(word x,word y,long mem) +{ + char buf[16]; + word i; + + for (i = strlen(ltoa(mem,buf,10));i < 5;i++) + USL_ScreenDraw(x++,y," ",0x48); + USL_ScreenDraw(x,y,buf,0x48); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_UpdateTextScreen() - Called after the ID libraries are started up. +// Displays what hardware is present. +// +/////////////////////////////////////////////////////////////////////////// +void +US_UpdateTextScreen(void) +{ + boolean b; + byte far *screen; + word i; + longword totalmem; + + // Show video card info + b = (grmode == CGAGR); + USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b); + b = (grmode == EGAGR); + USL_Show(21,8,4,(videocard >= EGAcard) && (videocard <= VGAcard),b); + b = (grmode == VGAGR); + USL_Show(21,9,4,videocard == VGAcard,b); + if (compatability) + USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f); + + // Show input device info + USL_Show(60,7,8,true,true); + USL_Show(60,8,11,JoysPresent[0],true); + USL_Show(60,9,11,JoysPresent[1],true); + USL_Show(60,10,5,MousePresent,true); + + // Show sound hardware info + USL_Show(21,14,11,true,SoundMode == sdm_PC); + b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib); + USL_Show(21,15,5,AdLibPresent && !SoundBlasterPresent, + b && !SoundBlasterPresent); + USL_Show(21,16,13,SoundBlasterPresent, + SoundBlasterPresent && (b || (SoundMode == sdm_SoundBlaster))); + USL_Show(21,17,13,SoundSourcePresent,SoundMode == sdm_SoundSource); + + // Show memory available/used + USL_ShowMem(63,15,mminfo.mainmem / 1024); + USL_Show(53,15,23,true,true); + USL_ShowMem(63,16,mminfo.EMSmem / 1024); + USL_Show(53,16,23,mminfo.EMSmem? true : false,true); + USL_ShowMem(63,17,mminfo.XMSmem / 1024); + USL_Show(53,17,23,mminfo.XMSmem? true : false,true); + totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem; + USL_ShowMem(63,18,totalmem / 1024); + screen = MK_FP(0xb800,1 + (((63 - 1) * 2) + (18 * 80 * 2))); + for (i = 0;i < 13;i++,screen += 2) + *screen = 0x4f; + + // Change Initializing... to Loading... + USL_ScreenDraw(27,22," Loading... ",0x9c); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_FinishTextScreen() - After the main program has finished its initial +// loading, this routine waits for a keypress and then clears the screen +// +/////////////////////////////////////////////////////////////////////////// +void +US_FinishTextScreen(void) +{ + // Change Loading... to Press a Key + // USL_ScreenDraw(29,22," Ready - Press a Key ",0x9a); + + if (!(tedlevel || NoWait)) + { + IN_ClearKeysDown(); + // IN_Ack(); + } + IN_ClearKeysDown(); + + USL_ClearTextScreen(); +} + +// Window/Printing routines + +/////////////////////////////////////////////////////////////////////////// +// +// US_SetPrintRoutines() - Sets the routines used to measure and print +// from within the User Mgr. Primarily provided to allow switching +// between masked and non-masked fonts +// +/////////////////////////////////////////////////////////////////////////// +void +US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *)) +{ + USL_MeasureString = measure; + USL_DrawString = print; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Print() - Prints a string in the current window. Newlines are +// supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_Print(char *s) +{ + char c,*se; + word w,h; + + while (*s) + { + se = s; + while ((c = *se) && (c != '\n')) + se++; + *se = '\0'; + + USL_MeasureString(s,&w,&h); + px = PrintX; + py = PrintY; + USL_DrawString(s); + + s = se; + if (c) + { + *se = c; + s++; + + PrintX = WindowX; + PrintY += h; + } + else + PrintX += w; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintUnsigned() - Prints an unsigned long +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintUnsigned(longword n) +{ + char buffer[32]; + + US_Print(ultoa(n,buffer,10)); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintSigned() - Prints a signed long +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintSigned(long n) +{ + char buffer[32]; + + US_Print(ltoa(n,buffer,10)); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_PrintInCenter() - Prints a string in the center of the given rect +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_PrintInCenter(char *s,Rect r) +{ + word w,h, + rw,rh; + + USL_MeasureString(s,&w,&h); + rw = r.lr.x - r.ul.x; + rh = r.lr.y - r.ul.y; + + px = r.ul.x + ((rw - w) / 2); + py = r.ul.y + ((rh - h) / 2); + USL_DrawString(s); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintCentered() - Prints a string centered in the current window. +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintCentered(char *s) +{ + Rect r; + + r.ul.x = WindowX; + r.ul.y = WindowY; + r.lr.x = r.ul.x + WindowW; + r.lr.y = r.ul.y + WindowH; + + USL_PrintInCenter(s,r); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CPrintLine() - Prints a string centered on the current line and +// advances to the next line. Newlines are not supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_CPrintLine(char *s) +{ + word w,h; + + USL_MeasureString(s,&w,&h); + + if (w > WindowW) + Quit("US_CPrintLine() - String exceeds width"); + px = WindowX + ((WindowW - w) / 2); + py = PrintY; + USL_DrawString(s); + PrintY += h; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CPrint() - Prints a string in the current window. Newlines are +// supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_CPrint(char *s) +{ + char c,*se; + word w,h; + + while (*s) + { + se = s; + while ((c = *se) && (c != '\n')) + se++; + *se = '\0'; + + US_CPrintLine(s); + + s = se; + if (c) + { + *se = c; + s++; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ClearWindow() - Clears the current window to white and homes the +// cursor +// +/////////////////////////////////////////////////////////////////////////// +void +US_ClearWindow(void) +{ + VWB_Bar(WindowX,WindowY,WindowW,WindowH,WHITE); + PrintX = WindowX; + PrintY = WindowY; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_DrawWindow() - Draws a frame and sets the current window parms +// +/////////////////////////////////////////////////////////////////////////// +void +US_DrawWindow(word x,word y,word w,word h) +{ + word i, + sx,sy,sw,sh; + + WindowX = x * 8; + WindowY = y * 8; + WindowW = w * 8; + WindowH = h * 8; + + PrintX = WindowX; + PrintY = WindowY; + + sx = (x - 1) * 8; + sy = (y - 1) * 8; + sw = (w + 1) * 8; + sh = (h + 1) * 8; + + US_ClearWindow(); + + VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6); + for (i = sx + 8;i <= sx + sw - 8;i += 8) + VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7); + VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8); + + for (i = sy + 8;i <= sy + sh - 8;i += 8) + VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// +void +US_CenterWindow(word w,word h) +{ + US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CenterSaveWindow() - Generates a window of a given width & height in +// the middle of the screen, saving the background +// +/////////////////////////////////////////////////////////////////////////// +void +US_CenterSaveWindow(word w,word h,memptr *save) +{ + word x,y, + screen; + + x = ((MaxX / 8) - w) / 2; + y = ((MaxY / 8) - h) / 2; + MM_GetPtr(save,(w * h) * CHARWIDTH); + screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH); + VW_ScreenToMem(screen,*save,w * CHARWIDTH,h); + US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_RestoreSaveWindow() - Restores the background of the size of the +// current window from the memory specified by save +// +/////////////////////////////////////////////////////////////////////////// +void +US_RestoreSaveWindow(memptr *save) +{ + word screen; + + screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH); + VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH); + MM_FreePtr(save); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_SaveWindow() - Saves the current window parms into a record for +// later restoration +// +/////////////////////////////////////////////////////////////////////////// +void +US_SaveWindow(WindowRec *win) +{ + win->x = WindowX; + win->y = WindowY; + win->w = WindowW; + win->h = WindowH; + + win->px = PrintX; + win->py = PrintY; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_RestoreWindow() - Sets the current window parms to those held in the +// record +// +/////////////////////////////////////////////////////////////////////////// +void +US_RestoreWindow(WindowRec *win) +{ + WindowX = win->x; + WindowY = win->y; + WindowW = win->w; + WindowH = win->h; + + PrintX = win->px; + PrintY = win->py; +} + +// Cursor routines + +/////////////////////////////////////////////////////////////////////////// +// +// US_StartCursor() - Sets up the cursor for User Mgr use +// +/////////////////////////////////////////////////////////////////////////// +void +US_StartCursor(void) +{ + CursorInfo info; + + VW_SetCursor(CURSORARROWSPR); + CursorX = MaxX / 2; + CursorY = MaxY / 2; + VW_MoveCursor(CursorX,CursorY); + VW_ShowCursor(); + + IN_ReadCursor(&info); // Dispose of any accumulated movement +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ShutCursor() - Cleans up after US_StartCursor() +// +/////////////////////////////////////////////////////////////////////////// +void +US_ShutCursor(void) +{ + VW_HideCursor(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_UpdateCursor() - Gets the new cursor position & button states from +// the Input Mgr and tells the View Mgr where the cursor is +// +/////////////////////////////////////////////////////////////////////////// +boolean +US_UpdateCursor(void) +{ + CursorInfo info; + + IN_ReadCursor(&info); + if (info.x || info.y || CursorBad) + { + CursorX += info.x; + if (CursorX >= MaxX) + CursorX = MaxX - 1; + else if (CursorX < 0) + CursorX = 0; + + CursorY += info.y; + if (CursorY >= MaxY) + CursorY = MaxY - 1; + else if (CursorY < 0) + CursorY = 0; + + VW_MoveCursor(CursorX,CursorY); + CursorBad = false; + } + Button0 = info.button0; + Button1 = info.button1; + return(Button0 || Button1); +} + +// Input routines + +/////////////////////////////////////////////////////////////////////////// +// +// USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput() +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_XORICursor(int x,int y,char *s,word cursor) +{ + char buf[MaxString]; + word w,h; + + strcpy(buf,s); + buf[cursor] = '\0'; + USL_MeasureString(buf,&w,&h); + + px = x + w - 1; + py = y; + USL_DrawString("\x80"); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_LineInput() - Gets a line of user input at (x,y), the string defaults +// to whatever is pointed at by def. Input is restricted to maxchars +// chars or maxwidth pixels wide. If the user hits escape (and escok is +// true), nothing is copied into buf, and false is returned. If the +// user hits return, the current string is copied into buf, and true is +// returned +// +/////////////////////////////////////////////////////////////////////////// +boolean +US_LineInput(int x,int y,char *buf,char *def,boolean escok, + int maxchars,int maxwidth) +{ + boolean redraw, + cursorvis,cursormoved, + done,result; + ScanCode sc; + char c, + s[MaxString],olds[MaxString]; + word i, + cursor, + w,h, + len; + longword lasttime; + + VW_HideCursor(); + + if (def) + strcpy(s,def); + else + *s = '\0'; + *olds = '\0'; + cursor = strlen(s); + cursormoved = redraw = true; + + cursorvis = done = false; + lasttime = TimeCount; + LastASCII = key_None; + LastScan = sc_None; + + while (!done) + { + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + + asm pushf + asm cli + + sc = LastScan; + LastScan = sc_None; + c = LastASCII; + LastASCII = key_None; + + asm popf + + switch (sc) + { + case sc_LeftArrow: + if (cursor) + cursor--; + c = key_None; + cursormoved = true; + break; + case sc_RightArrow: + if (s[cursor]) + cursor++; + c = key_None; + cursormoved = true; + break; + case sc_Home: + cursor = 0; + c = key_None; + cursormoved = true; + break; + case sc_End: + cursor = strlen(s); + c = key_None; + cursormoved = true; + break; + + case sc_Return: + strcpy(buf,s); + done = true; + result = true; + c = key_None; + break; + case sc_Escape: + if (escok) + { + done = true; + result = false; + } + c = key_None; + break; + + case sc_BackSpace: + if (cursor) + { + strcpy(s + cursor - 1,s + cursor); + cursor--; + redraw = true; + } + c = key_None; + cursormoved = true; + break; + case sc_Delete: + if (s[cursor]) + { + strcpy(s + cursor,s + cursor + 1); + redraw = true; + } + c = key_None; + cursormoved = true; + break; + + case 0x4c: // Keypad 5 + case sc_UpArrow: + case sc_DownArrow: + case sc_PgUp: + case sc_PgDn: + case sc_Insert: + c = key_None; + break; + } + + if (c) + { + len = strlen(s); + USL_MeasureString(s,&w,&h); + + if + ( + isprint(c) + && (len < MaxString - 1) + && ((!maxchars) || (len < maxchars)) + && ((!maxwidth) || (w < maxwidth)) + ) + { + for (i = len + 1;i > cursor;i--) + s[i] = s[i - 1]; + s[cursor++] = c; + redraw = true; + } + } + + if (redraw) + { + px = x; + py = y; + USL_DrawString(olds); + strcpy(olds,s); + + px = x; + py = y; + USL_DrawString(s); + + redraw = false; + } + + if (cursormoved) + { + cursorvis = false; + lasttime = TimeCount - TickBase; + + cursormoved = false; + } + if (TimeCount - lasttime > TickBase / 2) + { + lasttime = TimeCount; + + cursorvis ^= true; + } + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + + VW_UpdateScreen(); + } + + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + if (!result) + { + px = x; + py = y; + USL_DrawString(olds); + } + VW_ShowCursor(); + VW_UpdateScreen(); + + IN_ClearKeysDown(); + return(result); +} + +// Control panel routines + +static boolean FlushHelp; +static WindowRec HelpWindow,BottomWindow; +typedef enum + { + uic_Draw,uic_Hit + } UserCall; +typedef enum + { + uii_Bad,uii_Button,uii_RadioButton,uii_CheckBox,uii_KeyCap + } UIType; +#define ui_Normal 0 +#define ui_Selected 1 +#define ui_Disabled 2 + + // Prototype the custom routines +static boolean USL_CtlButtonCustom(UserCall,word,word), + USL_CtlPButtonCustom(UserCall,word,word), + USL_CtlPSButtonCustom(UserCall,word,word), + USL_CtlPRButtonCustom(UserCall,word,word), + USL_CtlHButtonCustom(UserCall,word,word), + USL_CtlDButtonCustom(UserCall,word,word), + USL_CtlDEButtonCustom(UserCall,word,word), + USL_CtlDLButtonCustom(UserCall,word,word), + USL_CtlDSButtonCustom(UserCall,word,word), + USL_CtlSButtonCustom(UserCall,word,word), + USL_CtlCButtonCustom(UserCall,word,word), + USL_CtlCKbdButtonCustom(UserCall,word,word), + USL_CtlCJoyButtonCustom(UserCall,word,word); + + // The structure of a user interaction item +typedef struct { + Rect r; // The enclosing rectangle + UIType type; // The type of item + int picup,picdown; // What to draw when up/down + char *help; // Floating help string + ScanCode key; // Key equiv + word sel; // Interaction flags (ui_XXX) + boolean (*custom)(UserCall,word,word); // Custom routine + char *text; // Text for some items + } UserItem; +typedef struct { + ScanCode key; + word i,n, // Hit CtlPanels2[i][n] + toi,ton; // Move to CtlPanels2[toi][ton] + } HotKey; // MARK + +static ScanCode *KeyMaps[] = + { + &KbdDefs[0].button0,&KbdDefs[0].button1, + &KbdDefs[0].upleft,&KbdDefs[0].up,&KbdDefs[0].upright, + &KbdDefs[0].left, &KbdDefs[0].right, + &KbdDefs[0].downleft,&KbdDefs[0].down,&KbdDefs[0].downright, + }; + +// Some macros to make rectangle definition quite a bit less unpleasant +#define CtlPanelX 8 +#define CtlPanelY 4 +#define CtlPanel2X (8*8) +#define CtlPanel2Y (2*8) +#define CtlPanel3X (8*8) +#define CtlPanel3Y (7*8) + +#define CtlPanelR(n) { CtlPanelX,CtlPanelY+(32 * (n)),\ + CtlPanelX+40,CtlPanelY+(32 * (n)) + 32} +#define CtlPanel2R(x,y) { CtlPanel2X+(x)*8,CtlPanel2Y+(y)*8,\ + CtlPanel2X+32+(x)*8,CtlPanel2Y+24+(y)*8} +#define CtlPanel3R(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\ + CtlPanel3X+32+(x)*8,CtlPanel3Y+24+(y)*8} +static UserItem CtlPanels[] = + { +{CtlPanelR(0),uii_RadioButton,CTL_STARTUPPIC,CTL_STARTDNPIC,"Start or Resume a Game",sc_None,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(1),uii_RadioButton,CTL_HELPUPPIC,CTL_HELPDNPIC,"Get Help With Commander Keen",sc_None,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(2),uii_RadioButton,CTL_DISKUPPIC,CTL_DISKDNPIC,"Load / Save / Quit",sc_None,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(3),uii_RadioButton,CTL_CONTROLSUPPIC,CTL_CONTROLSDNPIC,"Choose Controls",sc_C,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(4),uii_RadioButton,CTL_SOUNDUPPIC,CTL_SOUNDDNPIC,"Select Sound Device",sc_F2,ui_Normal,USL_CtlButtonCustom}, +{CtlPanelR(5),uii_RadioButton,CTL_MUSICUPPIC,CTL_MUSICDNPIC,"Turn Music On / Off",sc_F7,ui_Normal,USL_CtlButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlPPanels[] = + { +{CtlPanel2R(10,0),uii_RadioButton,CTL_P_NEWGAMEUPPIC,CTL_P_NEWGAMEDNPIC,"Choose Difficulty for the New Game",sc_F5,ui_Normal,USL_CtlPButtonCustom}, +{CtlPanel2R(15,0),uii_RadioButton,CTL_P_RESUMEUPPIC,CTL_P_RESUMEDNPIC,"Go Back to Current Game",sc_None,ui_Normal,USL_CtlPButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlPSPanels[] = + { +{CtlPanel3R(13,5),uii_Button,CTL_P_MEDUPPIC,CTL_P_MEDDNPIC,"Start New Game in Normal Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom}, +{CtlPanel3R(8,5),uii_Button,CTL_P_EASYUPPIC,CTL_P_EASYDNPIC,"Start New Game in Easy Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom}, +{CtlPanel3R(18,5),uii_Button,CTL_P_HARDUPPIC,CTL_P_HARDDNPIC,"Start New Game in Hard Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlPRPanels[] = + { +{CtlPanel3R(13,5),uii_Button,CTL_P_GORESUMEUPPIC,CTL_P_GORESUMEDNPIC,"Resume Current Game",sc_None,ui_Normal,USL_CtlPRButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlHPanels[] = + { +{CtlPanel2R(8,0),uii_Button,CTL_H_LOSTUPPIC,CTL_H_LOSTDNPIC,"Help Me, I'm Lost!",sc_F1,ui_Normal,USL_CtlHButtonCustom}, +{CtlPanel2R(13,0),uii_Button,CTL_H_CTRLUPPIC,CTL_H_CTRLDNPIC,"Get Help with Controls",sc_None,ui_Normal,USL_CtlHButtonCustom}, +{CtlPanel2R(18,0),uii_Button,CTL_H_STORYUPPIC,CTL_H_STORYDNPIC,"Read Story & Game Tips",sc_None,ui_Normal,USL_CtlHButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlDPanels[] = + { +{CtlPanel2R(9,0),uii_RadioButton,CTL_D_LSGAMEUPPIC,CTL_D_LSGAMEDNPIC,"Load or Save a Game",sc_F6,ui_Normal,USL_CtlDButtonCustom}, +{CtlPanel2R(15,0),uii_RadioButton,CTL_D_DOSUPPIC,CTL_D_DOSDNPIC,"Exit to DOS",sc_Q,ui_Normal,USL_CtlDButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlDLSPanels[] = + { +#define CtlPanel3LSR(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\ + CtlPanel3X+32+(x)*8,CtlPanel3Y+16+(y)*8} +{CtlPanel3LSR(1,0),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,0),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,2),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,2),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,4),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,4),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,6),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,6),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,8),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,8),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,10),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,10),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{CtlPanel3LSR(1,12),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom}, +{CtlPanel3LSR(6,12),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlDEPanels[] = + { +#define CtlPanel3ER(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\ + CtlPanel3X+40+(x)*8,CtlPanel3Y+24+(y)*8} +{CtlPanel3ER(12,5),uii_Button,CTL_D_EXITUPPIC,CTL_D_EXITDNPIC,"Really Exit to DOS",sc_None,ui_Normal,USL_CtlDEButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlCPanels[] = + { +{CtlPanel2R(8,0),uii_RadioButton,CTL_C_KBDUPPIC,CTL_C_KBDDNPIC,"Use / Configure Keyboard",sc_F3,ui_Normal,USL_CtlCButtonCustom}, +{CtlPanel2R(13,0),uii_RadioButton,CTL_C_JOY1UPPIC,CTL_C_JOY1DNPIC,"Use / Configure Joystick 1",sc_None,ui_Normal,USL_CtlCButtonCustom}, +{CtlPanel2R(18,0),uii_RadioButton,CTL_C_JOY2UPPIC,CTL_C_JOY2DNPIC,"Use / Configure Joystick 2",sc_None,ui_Normal,USL_CtlCButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, +#define CtlPanelKC3R(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\ + CtlPanel3X+56+(x)*8,CtlPanel3Y+32+(y)*8} + CtlCKbdPanels[] = + { +{CtlPanelKC3R(1,2),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key for Jumping",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(1,6),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key for Throwing",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(8,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up & Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(15,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(22,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up & Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(8,4),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(22,4),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(8,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down & Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(15,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{CtlPanelKC3R(22,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down & Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlCJoyPanels[] = + { +{CtlPanel3R(13,5),uii_Button,CTL_C_CALIBRATEUPPIC,CTL_C_CALIBRATEDNPIC,"Configure Joystick",sc_None,ui_Normal,USL_CtlCJoyButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlSPanels[] = + { +{CtlPanel2R(3,0),uii_RadioButton,CTL_S_NOSNDUPPIC,CTL_S_NOSNDDNPIC,"Turn Sound Off",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{CtlPanel2R(8,0),uii_RadioButton,CTL_S_PCSNDUPPIC,CTL_S_PCSNDDNPIC,"Use PC Speaker",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{CtlPanel2R(13,0),uii_RadioButton,CTL_S_ADLIBUPPIC,CTL_S_ADLIBDNPIC,"Use AdLib Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{CtlPanel2R(18,0),uii_RadioButton,CTL_S_SNDBLUPPIC,CTL_S_SNDBLDNPIC,"Use SoundBlaster Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{CtlPanel2R(23,0),uii_RadioButton,CTL_S_SNDSRCUPPIC,CTL_S_SNDSRCDNPIC,"Use Sound Source Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlSSSPanels[] = + { +{CtlPanel3R(7,2),uii_CheckBox,CTL_CHECKUPPIC,CTL_CHECKDNPIC,"Turn Tandy Mode On / Off",sc_None,ui_Normal,0,"Tandy Mode"}, +{CtlPanel3R(7,6),uii_CheckBox,CTL_CHECKUPPIC,CTL_CHECKDNPIC,"Switch between LPT1 & LPT2",sc_None,ui_Normal,0,"Use LPT2"}, +{-1,-1,-1,-1,uii_Bad} + }, + CtlMPanels[] = + { +{CtlPanel2R(9,0),uii_RadioButton,CTL_M_NOMUSUPPIC,CTL_M_NOMUSDNPIC,"Background Music Off"}, +{CtlPanel2R(15,0),uii_RadioButton,CTL_M_ADLIBUPPIC,CTL_M_ADLIBDNPIC,"Use AdLib/SoundBlaster Music"}, +{-1,-1,-1,-1,uii_Bad} + }, + *CtlPanels2[] = + { + CtlPPanels, // Start + CtlHPanels, // Help + CtlDPanels, // Disk + CtlCPanels, // Controls + CtlSPanels, // Sound + CtlMPanels // Music + }, + *TheItems[4] = {CtlPanels}; +static int CtlPanelButton; + +/////////////////////////////////////////////////////////////////////////// +// +// USL_TurnOff() - Goes through a list of UserItems and sets them all to +// the normal state +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_TurnOff(UserItem *ip) +{ + while (ip->type != uii_Bad) + { + ip->sel = ui_Normal; + ip++; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_FindDown() - Finds which UserItem, if any, is selected in the given +// list +// +/////////////////////////////////////////////////////////////////////////// +static int +USL_FindDown(UserItem *ip) +{ + int i; + + for (i = 0;ip->type != uii_Bad;i++,ip++) + if (ip->sel & ui_Selected) + return(i); + return(-1); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ShowHelp() - Shows the specified string in the help window +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ShowHelp(char *s) +{ + WindowRec wr; + + if (!s) + return; + + US_SaveWindow(&wr); + US_RestoreWindow(&HelpWindow); + + US_ClearWindow(); + US_PrintCentered(s); + + US_RestoreWindow(&wr); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HandleError() - Handles telling the user that there's been an error +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_HandleError(int num) +{ + char buf[64]; + + strcpy(buf,"Error: "); + if (num < 0) + strcat(buf,"Unknown"); + else if (num == ENOMEM) + strcat(buf,"Disk is Full"); + else if (num == EINVFMT) + strcat(buf,"File is Incomplete"); + else + strcat(buf,sys_errlist[num]); + + VW_HideCursor(); + + fontcolor = F_SECONDCOLOR; + USL_ShowHelp(buf); + fontcolor = F_BLACK; + VW_UpdateScreen(); + + IN_ClearKeysDown(); + IN_Ack(); + + VW_ShowCursor(); + VW_UpdateScreen(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DrawItem() - Draws a UserItem. If there's a custom routine, this will +// call it with a uic_Draw command. If the custom routine returns true, +// then the routine handled all of the drawing. If it returns false, +// then this routine does the default drawing. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_DrawItem(word hiti,word hitn) +{ + boolean handled,centered; + char *text; + word w,h; + int picup,picdown; + Rect r; + UserItem *ip; + + ip = &TheItems[hiti][hitn]; + if (ip->custom) + handled = ip->custom(uic_Draw,hiti,hitn); + else + handled = false; + + if (!handled) + { + picup = ip->picup; + picdown = ip->picdown; + switch (ip->type) + { + case uii_CheckBox: + px = ip->r.lr.x + 8; + py = ip->r.ul.y + 8; + text = ip->text; + centered = false; + break; + case uii_KeyCap: + if (!(ip->sel & ui_Selected)) + { + text = ip->text; + if (text) + { + r = ip->r; + centered = true; + } + } + else + text = nil; + break; + default: + text = nil; + break; + } + + VWB_DrawPic(ip->r.ul.x,ip->r.ul.y, + (ip->sel & ui_Selected)? picdown : picup); + if (text) + { + if (centered) + USL_PrintInCenter(text,r); + else + { + USL_MeasureString(text,&w,&h); + VWB_Bar(px,py,w + 7,h,WHITE); + USL_DrawString(text); + } + } + if (ip->sel & ui_Disabled) + { + if ((picup == CTL_D_LOADUPPIC) || (picup == CTL_D_SAVEUPPIC)) + VWB_DrawMPic(ip->r.ul.x,ip->r.ul.y,CTL_LSMASKPICM); + else + VWB_DrawMPic(ip->r.ul.x,ip->r.ul.y,CTL_LITTLEMASKPICM); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DoHit() - Handles a hit on a UserItem. If there's a custom routine, +// it will be called. If it returns true, then don't do anything +// more. If it returns false, then use the standard behaviour +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_DoHit(word hiti,word hitn) +{ + boolean handled; + word i; + UserItem *ip; + + ip = &TheItems[hiti][hitn]; + if (ip->custom) + handled = ip->custom(uic_Hit,hiti,hitn); + else + handled = false; + + if (!handled) + { + if (TheItems[hiti][hitn].sel & ui_Disabled) + { + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("This Item is Disabled"); + fontcolor = F_BLACK; + return; + } + + FlushHelp = true; + + switch (ip->type) + { + case uii_Button: + // Must have a custom routine to handle hits - this just redraws + ip->sel ^= ui_Selected; + USL_DrawItem(hiti,hitn); + case uii_CheckBox: + ip->sel ^= ui_Selected; + USL_DrawItem(hiti,hitn); + break; + case uii_RadioButton: + for (i = 0,ip = TheItems[hiti];ip->type != uii_Bad;i++,ip++) + { + if + ( + (i != hitn) + && (ip->type == uii_RadioButton) + && (ip->sel & ui_Selected) + ) + { + ip->sel &= ~ui_Selected; + USL_DrawItem(hiti,i); + } + } + TheItems[hiti][hitn].sel |= ui_Selected; + USL_DrawItem(hiti,hitn); + break; + case uii_KeyCap: + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_IsInRect() - Checks to see if the coordinates given are within any +// of the Rects in the UserItem list. If so, returns true & sets the +// index & number for lookup. If not, returns false. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_IsInRect(word x,word y,word *index,word *number) +{ + UserItem *item,**items; + + items = TheItems; + *index = 0; + while (*items) + { + item = *items; + *number = 0; + while (item->type != uii_Bad) + { + if + ( + (x >= item->r.ul.x) + && (x < item->r.lr.x) + && (y >= item->r.ul.y) + && (y < item->r.lr.y) + ) + return(true); + (*number)++; + item++; + } + + (*index)++; + items++; + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_TrackItem() - Tracks the given item. If the cursor is inside of the +// item, it's redrawn as down. If the cursor is outside, the item is +// drawn in its original state. Returns true if the button was released +// while the cursor was inside the item, or false if it wasn't. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_TrackItem(word hiti,word hitn) +{ + boolean inside,last; + word ini,inn, + on, + sel,othersel; + UserItem *ip,*op; + + ip = &TheItems[hiti][hitn]; + sel = ip->sel; + if (ip->type == uii_RadioButton) + { + inside = false; + for (op = TheItems[hiti],on = 0;op->type != uii_Bad;op++,on++) + { + if (op->sel & ui_Selected) + { + inside = true; + break; + } + } + if (!inside) + op = ip; + othersel = op->sel; + } + else + op = nil; + + if (ip->sel & ui_Disabled) + { + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("This item is disabled"); + fontcolor = F_BLACK; + + while (US_UpdateCursor()) + VW_UpdateScreen(); + + FlushHelp = true; + return(false); + } + + last = false; + do + { + USL_IsInRect(CursorX,CursorY,&ini,&inn); + inside = (ini == hiti) && (inn == hitn); + if (inside != last) + { + if (inside) + { + if (op) + { + op->sel &= ~ui_Selected; + ip->sel |= ui_Selected; + } + else + ip->sel = sel ^ ui_Selected; + } + else + { + if (op && (op != ip)) + { + op->sel |= ui_Selected; + ip->sel &= ~ui_Selected; + } + else + ip->sel = sel; + } + + USL_DrawItem(hiti,hitn); + if (op && (op != ip)) + USL_DrawItem(hiti,on); + + last = inside; + } + VW_UpdateScreen(); + } while (US_UpdateCursor()); + + if (op) + op->sel = othersel; + ip->sel = sel; + if (!inside) + { + if (op && (op != ip)) + USL_DrawItem(hiti,on); + USL_DrawItem(hiti,hitn); + VW_UpdateScreen(); + } + + return(inside); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_GlideCursor() - Smoothly moves the cursor to the given location +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_GlideCursor(long newx,long newy) +{ + word steps; + long x,y, + dx,dy; + + if (grmode == CGAGR) + steps = 1; + else + steps = 8; + + x = (long)CursorX << 16; + dx = ((newx << 16) - x) / steps; + y = (long)CursorY << 16; + dy = ((newy << 16) - y) / steps; + + while ((CursorX != newx) || (CursorY != newy)) + { + x += dx; + y += dy; + + CursorX = x >> 16; + CursorY = y >> 16; + VW_MoveCursor(CursorX,CursorY); + VW_UpdateScreen(); + } + CursorBad = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_FindRect() - Code so ugly you'll puke! Given a Rect and direction, +// this routine will try to find a UserItem to move the cursor to +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_FindRect(Rect r,Motion xd,Motion yd) +{ + word i,i1,i2,i3; + Motion m1,m2; + Point diffs[9],diff,*dp; + Rect *rp,*good,*goods[9]; + UserItem *ip,**items; + + for (m1 = motion_Up,dp = diffs;m1 <= motion_Down;m1++) + { + for (m2 = motion_Left;m2 <= motion_Right;m2++,dp++) + { + dp->x = m2 * 1024; + dp->y = m1 * 1024; + } + } + for (i = 0;i < 9;i++) + goods[i] = nil; + + // Find out which octants all of the rects (except r) are in + for (items = TheItems;*items;items++) + { + for (ip = *items;ip->type != uii_Bad;ip++) + { + rp = &ip->r; + diff.x = rp->ul.x - r.ul.x; + diff.y = rp->ul.y - r.ul.y; + if (!(diff.x || diff.y)) + continue; + + if // 1,4,7 + ( + ((rp->ul.x >= r.ul.x) && (rp->ul.x < r.lr.x)) + || ((rp->lr.x > r.ul.x) && (rp->lr.x <= r.lr.x)) + ) + { + if (rp->lr.y <= r.ul.y) + { + if (!(goods[1] && (diff.y < diffs[1].y))) + { + goods[1] = rp; + diffs[1] = diff; + } + } + else if (rp->ul.y >= r.lr.y) + { + if (!(goods[7] && (diff.y > diffs[7].y))) + { + goods[7] = rp; + diffs[7] = diff; + } + } + } + + if // 3,4,5 + ( + ((rp->ul.y >= r.ul.y) && (rp->ul.y < r.lr.y)) + || ((rp->lr.y > r.ul.y) && (rp->lr.y <= r.lr.y)) + ) + { + if (rp->lr.x <= r.ul.x) + { + if (!(goods[3] && (diff.x < diffs[3].x))) + { + goods[3] = rp; + diffs[3] = diff; + } + } + else if (rp->ul.x >= r.lr.x) + { + if (!(goods[5] && (diff.x > diffs[5].x))) + { + goods[5] = rp; + diffs[5] = diff; + } + } + } + + if (rp->ul.x < r.ul.x) // 0,6 + { + if (rp->lr.y <= r.ul.y) + { + if + ( + (!goods[0]) + || (diff.y > diffs[0].y) + || (diff.x > diffs[6].x) + ) + { + goods[0] = rp; + diffs[0] = diff; + } + } + else if (rp->ul.y >= r.lr.y) + { + if + ( + (!goods[6]) + || (diff.y < diffs[6].y) + || (diff.x > diffs[6].x) + ) + { + goods[6] = rp; + diffs[6] = diff; + } + } + } + + if (rp->lr.x > r.lr.x) // 2,8 + { + if (rp->lr.y <= r.ul.y) + { + if + ( + (!goods[2]) + || (diff.y > diffs[2].y) + || (diff.x < diffs[2].x) + ) + { + goods[2] = rp; + diffs[2] = diff; + } + } + else if (rp->ul.y >= r.lr.y) + { + if + ( + (!goods[8]) + || (diff.y < diffs[8].y) + || (diff.x < diffs[8].x) + ) + { + goods[8] = rp; + diffs[8] = diff; + } + } + } + } + } + + switch (yd) + { + case motion_Up: + i1 = 1,i2 = 0,i3 = 2; + break; + case motion_None: + switch (xd) + { + case motion_Left: + i1 = 3,i2 = 0,i3 = 6; + break; + case motion_Right: + i1 = 5,i2 = 8,i3 = 2; + break; + } + break; + case motion_Down: + i1 = 7,i2 = 8,i3 = 6; + break; + } + + ( + (good = goods[i1]) + || (good = goods[i2]) + || (good = goods[i3]) + || (good = &r) + ); +#if 0 + CursorX = good->lr.x - 8; + CursorY = good->lr.y - 8; + CursorBad = true; + US_UpdateCursor(); +#endif + USL_GlideCursor(good->lr.x - 8,good->lr.y - 8); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlButtonCustom() - The custom routine for all of the Control Panel +// (leftmost) buttons. Clears all of the other item lists, clears the +// large area, and draws the line dividing the top and bottom areas. +// Then it sets up and draws the appropriate top row of icons. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + if (n == CtlPanelButton) + return(true); + + US_ClearWindow(); + for (j = 8;j < 38;j++) + { + VWB_DrawTile8M(j * 8,6 * 8,10); + VWB_DrawTile8M(j * 8,21 * 8,10); + } + VWB_DrawTile8M(7 * 8,6 * 8,9); + VWB_DrawTile8M(38 * 8,6 * 8,11); + VWB_DrawTile8M(7 * 8,21 * 8,9); + VWB_DrawTile8M(38 * 8,21 * 8,11); + + for (j = 1;j < 4;j++) + TheItems[j] = nil; + + // Set to new button + CtlPanelButton = n; + + // Draw new items + TheItems[1] = ip = CtlPanels2[CtlPanelButton]; + j = 0; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j); + if (ip->sel & ui_Selected) + USL_DoHit(i + 1,j); + j++; + ip++; + } + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlCKbdButtonCustom() - The custom routine for the keyboard keycaps. +// This routine gets a scancode and puts it in the appropriate +// KbdDefs[0] member. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlCKbdButtonCustom(UserCall call,word i,word n) +{ + boolean state; + word j; + ScanCode scan; + longword time; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + + fontcolor = F_SECONDCOLOR; + USL_ShowHelp(ip->help); + fontcolor = F_BLACK; + VW_HideCursor(); + VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,ip->picdown); + VW_UpdateScreen(); + + LastScan = sc_None; + time = TimeCount; + state = true; + do + { + if (TimeCount - time > 35) // Half-second delays + { + state ^= true; + VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,state? ip->picdown : ip->picup); + VW_UpdateScreen(); + time = TimeCount; + } + if (US_UpdateCursor()) + { + while (US_UpdateCursor()) + ; + scan = sc_Escape; + break; + } + + asm pushf + asm cli + if (LastScan == sc_LShift) + LastScan = sc_None; + asm popf + } while (!(scan = LastScan)); + IN_ClearKey(scan); + if (scan != sc_Escape) + { + for (j = 0,state = false;j < 10;j++) + { + if (j == n) + continue; + if (*(KeyMaps[j]) == scan) + { + state = true; + break; + } + } + if (state) + { + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("That Key is Already Used!"); + fontcolor = F_BLACK; + } + else + { + ip->text = IN_GetScanName(scan); + *(KeyMaps[n]) = scan; + FlushHelp = true; + } + } + + USL_DrawItem(i,n); + VW_ShowCursor(); + VW_UpdateScreen(); + + return(true); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlCJoyButtonCustom() - The custom button routine for joystick +// calibration +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlCJoyButtonCustom(UserCall call,word i,word n) +{ + word joy, + minx,maxx, + miny,maxy; + + i++,n++; // Shut the compiler up + + if (call != uic_Hit) + return(false); + + IN_ClearKeysDown(); + joy = USL_FindDown(CtlCPanels) - 1; + + VW_HideCursor(); + FlushHelp = true; + fontcolor = F_SECONDCOLOR; + + USL_ShowHelp("Move Joystick to the Upper-Left"); + VW_UpdateScreen(); + while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joy)) + ; + if (LastScan != sc_Escape) + { + IN_GetJoyAbs(joy,&minx,&miny); + while (IN_GetJoyButtonsDB(joy)) + ; + + USL_ShowHelp("Move Joystick to the Lower-Right"); + VW_UpdateScreen(); + while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joy)) + ; + if (LastScan != sc_Escape) + { + IN_GetJoyAbs(0,&maxx,&maxy); + IN_SetupJoy(joy,minx,maxx,miny,maxy); + } + } + + if (LastScan != sc_Escape) + while (IN_GetJoyButtonsDB(joy)) + ; + + if (LastScan) + IN_ClearKeysDown(); + + fontcolor = F_BLACK; + VW_ShowCursor(); + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ClearBottom() - Clears the bottom part of the window +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ClearBottom(void) +{ + WindowRec wr; + + US_SaveWindow(&wr); + US_RestoreWindow(&BottomWindow); + + US_ClearWindow(); + + US_RestoreWindow(&wr); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_FormatHelp() - Formats helptext. Runs through and calculates the +// number of lines, and the offset for the start of each line. Stops +// after len bytes or when it hits a tilde ('~'). Munges the text. +// +/////////////////////////////////////////////////////////////////////////// +static word +USL_FormatHelp(char far *text,long len) +{ + word line, + w,h, + far *off; + char c, + far *s,far *l,far *le; + + WindowX += 4; + WindowW -= 4; + + MM_GetPtr(&LineOffsets,MaxHelpLines * sizeof(word)); + off = (word far *)LineOffsets; + for (line = 0,le = l = s = text;(s - text < len) && (*s != '~');s++) + { + if ((c = *s) == '\n') + { + *s = '\0'; + *off++ = l - text; // Save offset of start of line + line++; // Bump line number + le = l = s + 1; // Set start of line ptr + } + + if (c == '\r') + c = *s = ' '; + if // Strip orphaned spaces + ( + (c == ' ') + && (s == l) + && (*(s - 1) == '\0') + && (*(s + 1) != ' ') + && (s > text) + ) + le = l = s + 1; + else if (c == ' ') + { + *s = '\0'; + USL_MeasureString(l,&w,&h); + if (w >= WindowW) // If string width exceeds window, + { + *s = c; // Replace null char with proper char + *le = '\0'; // Go back to last line end + *off++ = l - text; // Save offset of start of line + line++; // Bump line number + l = s = le + 1; // Start next time through after last line end + } + else + { + *s = c; // Width still ok - put char back + le = s; // And save ptr to last ok end of word + } + } + } + + WindowX -= 4; + WindowW += 4; + + return(line); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DrawHelp() - Draws helptext in the current window +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_DrawHelp(char far *text,word start,word end,word line,word h,word far *lp) +{ + px = WindowX + 4; + py = WindowY + (line * h); + for (lp += start;start < end;start++,px = WindowX + 4,py += h) + USL_DrawString(text + *lp++); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DoHelp() - Formats and displays the specified help +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_DoHelp(memptr text,long len) +{ + boolean done, + moved; + int scroll; + word i, + pixdiv, + w,h, + lines,cur,page, + top,num,loc, + far *lp, + base,srcbase,destbase; + ScanCode waitkey; + longword lasttime; + WindowRec wr; + CursorInfo info; + + USL_ShowHelp("Arrow Keys Move / Escape Exits"); + fontcolor = F_BLACK; + + US_SaveWindow(&wr); + US_RestoreWindow(&BottomWindow); + US_ClearWindow(); + + VW_HideCursor(); + VW_UpdateScreen(); + + lines = USL_FormatHelp((char far *)text,len); + USL_MeasureString("",&w,&h); + page = WindowH / h; + cur = 0; + lp = LineOffsets; + + IN_ClearKeysDown(); + moved = true; + lasttime = 0; + scroll = 0; + done = false; + waitkey = sc_None; + while (!done) + { + if (moved) + { + while (TimeCount - lasttime < 5) + ; + lasttime = TimeCount; + + if (scroll == -1) + { + top = cur; + num = 1; + loc = 0; + } + else if (scroll == +1) + { + num = 1; + loc = page - 1; + top = cur + loc; + } + else + { + top = cur; + num = (page < lines)? page : lines; + loc = 0; + } + if (scroll) + { + if (grmode == CGAGR) + { + pixdiv = 4; + base = bufferofs + panadjust + (WindowX / pixdiv); + } + else if (grmode == EGAGR) + { + VWB_Bar(WindowX,WindowY + (loc * h),WindowW,num * h,WHITE); + USL_DrawHelp((char far *)text,top,top + num,loc,h,lp); + + pixdiv = 8; + base = displayofs + panadjust + (WindowX / pixdiv); + } + else if (grmode == VGAGR) + pixdiv = 1; + + if (scroll == 1) + { + srcbase = base + ylookup[WindowY + h]; + destbase = base + ylookup[WindowY]; + if (grmode == EGAGR) + { + EGAWRITEMODE(1); + VW_WaitVBL(1); + } + VW_ScreenToScreen(srcbase,destbase,WindowW / pixdiv, + WindowH - h); + } + else + { + i = WindowY + (h * (page - 1)); + srcbase = base + ylookup[i - h]; + destbase = base + ylookup[i]; + base = ylookup[h]; + for (i = page - 1;i;i--,srcbase -= base,destbase -= base) + VW_ScreenToScreen(srcbase,destbase,WindowW / pixdiv,h); + } + if (grmode == CGAGR) + { + VWB_Bar(WindowX,WindowY + (loc * h),WindowW,num * h,WHITE); + USL_DrawHelp((char far *)text,top,top + num,loc,h,lp); + VW_UpdateScreen(); + } + else if (grmode == EGAGR) + { + base = panadjust + (WindowX / pixdiv) + + ylookup[WindowY + (loc * h)]; + VW_ScreenToScreen(base + bufferofs,base + displayofs, + WindowW / pixdiv,h); + } + } + else + { + US_ClearWindow(); + USL_DrawHelp((char far *)text,top,top + num,loc,h,lp); + VW_UpdateScreen(); + } + + moved = false; + scroll = 0; + } + + if (waitkey) + while (IN_KeyDown(waitkey)) + ; + waitkey = sc_None; + + IN_ReadCursor(&info); + if (info.y < 0) + { + if (cur > 0) + { + scroll = -1; + cur--; + moved = true; + } + } + else if (info.y > 0) + { + if (cur + page < lines) + { + scroll = +1; + cur++; + moved = true; + } + } + else if (info.button0 || info.button1) + done = true; + else if (IN_KeyDown(LastScan)) + { + switch (LastScan) + { + case sc_Escape: + done = true; + break; + case sc_UpArrow: + if (cur > 0) + { + scroll = -1; + cur--; + moved = true; + } + break; + case sc_DownArrow: + if (cur + page < lines) + { + scroll = +1; + cur++; + moved = true; + } + break; + case sc_PgUp: + if (cur > page) + cur -= page; + else + cur = 0; + moved = true; + waitkey = sc_PgUp; + break; + case sc_PgDn: + if (cur + page < lines) + { + cur += page; + if (cur + page >= lines) + cur = lines - page; + moved = true; + } + waitkey = sc_PgDn; + break; + } + } + } + IN_ClearKeysDown(); + do + { + IN_ReadCursor(&info); + } while (info.button0 || info.button1); + + VW_ShowCursor(); + US_ClearWindow(); + VW_UpdateScreen(); + US_RestoreWindow(&wr); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlHButtonCustom() - The custom routine for all of the help buttons +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlHButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + ip->sel |= ui_Selected; + USL_DrawItem(i,n); + + USL_ClearBottom(); + + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("Loading & Formatting Text..."); + VW_UpdateScreen(); + +#ifdef HELPTEXTLINKED // Ugly hack because of lack of disk space... + { +extern char far gametext,far context,far story; + char far *buf; + memptr dupe; + + switch (n) + { + case 0: + buf = &gametext; + break; + case 1: + buf = &context; + break; + case 2: + buf = &story; + break; + } + + MM_GetPtr(&dupe,5000); + _fmemcpy((char far *)dupe,buf,5000); + + USL_DoHelp(dupe,5000); + + MM_FreePtr(&dupe); + if (LineOffsets) + MM_FreePtr(&LineOffsets); + } +#else + { + char *name; + int file; + long len; + memptr buf; + + switch (n) + { + case 0: + name = "GAMETEXT."EXTENSION; + break; + case 1: + name = "CONTEXT."EXTENSION; + break; + case 2: + name = "STORY."EXTENSION; + break; + default: + Quit("Bad help button number"); + } + + if ((file = open(name,O_RDONLY | O_TEXT)) == -1) + USL_HandleError(errno); + else + { + len = filelength(file); + MM_GetPtr(&buf,len); + + if (CA_FarRead(file,(byte far *)buf,len)) + USL_DoHelp(buf,len); + else + USL_HandleError(errno); + + close(file); + MM_FreePtr(&buf); + } + + if (LineOffsets) + MM_FreePtr(&LineOffsets); + } +#endif + + fontcolor = F_BLACK; + + ip->sel &= ~ui_Selected; + USL_DrawItem(i,n); + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlDButtonCustom() - The custom routine for all of the disk buttons. +// Sets up the bottom area of the window with the appropriate buttons +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlDButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + USL_ClearBottom(); + + j = 0; + TheItems[i + 1] = ip = n? CtlDEPanels : CtlDLSPanels; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j++); + ip++; + } + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_DLSRect() - Draw the rectangle for the save game names +// +/////////////////////////////////////////////////////////////////////////// +static Rect +USL_DLSRect(UserItem *ip) +{ + Rect r; + + r.ul.x = ip->r.lr.x + 40 + 2; + r.ul.y = ip->r.ul.y + 2; + r.lr.x = WindowX + WindowW - 8 - 2; + r.lr.y = ip->r.lr.y - 2; + + VWB_Bar(r.ul.x,r.ul.y,r.lr.x - r.ul.x,r.lr.y - r.ul.y,WHITE); + + VWB_Hlin(r.ul.x,r.lr.x,r.ul.y,BLACK); + VWB_Hlin(r.ul.x,r.lr.x,r.lr.y,BLACK); + VWB_Vlin(r.ul.y,r.lr.y,r.ul.x,BLACK); + VWB_Vlin(r.ul.y,r.lr.y,r.lr.x,BLACK); + + px = r.ul.x + 2; + py = r.ul.y + 2; + + return(r); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlDLButtonCustom() - The load game custom routine +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlDLButtonCustom(UserCall call,word i,word n) +{ + char *filename, + msg[MaxGameName + 12]; + word err; + int file; + UserItem *ip; + SaveGame *game; + WindowRec wr; + + // DEBUG - deal with warning user about loading a game causing abort + + game = &Games[n / 2]; + ip = &TheItems[i][n]; + + switch (call) + { + case uic_Draw: + if (!loadedgame) + { + USL_DLSRect(ip); + fontcolor = game->present? F_BLACK : F_FIRSTCOLOR; + USL_DrawString(game->present? game->name : "Empty"); + fontcolor = F_BLACK; + } + break; + case uic_Hit: + if (ip->sel & ui_Disabled) + return(false); + + LeaveDriveOn++; + filename = USL_GiveSaveName(n / 2); + + US_SaveWindow(&wr); + US_CenterWindow(30,3); + strcpy(msg,"Loading `"); + strcat(msg,game->name); + strcat(msg,"\'"); + US_PrintCentered(msg); + VW_HideCursor(); + VW_UpdateScreen(); + + err = 0; + if ((file = open(filename,O_BINARY | O_RDONLY)) != -1) + { + if (read(file,game,sizeof(*game)) == sizeof(*game)) + { + if (USL_LoadGame) + if (!USL_LoadGame(file)) + USL_HandleError(err = errno); + } + else + USL_HandleError(err = errno); + close(file); + } + else + USL_HandleError(err = errno); + if (err) + abortgame = true; + else + loadedgame = true; + game->present = true; + + if (loadedgame) + Paused = true; + + VW_ShowCursor(); + US_RestoreWindow(&wr); + + LeaveDriveOn--; + break; + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlDSButtonCustom() - The save game custom routine +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlDSButtonCustom(UserCall call,word i,word n) +{ + boolean ok; + char *filename; + word err; + int file; + Rect r; + UserItem *ip; + SaveGame *game; + WindowRec wr; + + if (call != uic_Hit) + return(false); + + game = &Games[n / 2]; + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + FlushHelp = true; + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("Enter Game Name / Escape Aborts"); + fontcolor = F_BLACK; + + r = USL_DLSRect(ip - 1); + ok = US_LineInput(px,py,game->name,game->present? game->name : nil,true, + MaxGameName,r.lr.x - r.ul.x - 8); + if (!strlen(game->name)) + strcpy(game->name,"Untitled"); + if (ok) + { + US_SaveWindow(&wr); + US_CenterWindow(10,3); + US_PrintCentered("Saving"); + VW_HideCursor(); + VW_UpdateScreen(); + + LeaveDriveOn++; + filename = USL_GiveSaveName(n / 2); + err = 0; + file = open(filename,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + if (file != -1) + { + if (write(file,game,sizeof(*game)) == sizeof(*game)) + { + if (USL_SaveGame) + ok = USL_SaveGame(file); + if (!ok) + USL_HandleError(err = errno); + } + else + USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno)); + close(file); + } + else + USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno)); + if (err) + { + remove(filename); + ok = false; + } + LeaveDriveOn--; + + VW_ShowCursor(); + US_RestoreWindow(&wr); + USL_DoHit(i - 1,0); + VW_UpdateScreen(); + } + + if (!game->present) + game->present = ok; + + if (ok) + { + GameIsDirty = false; + (ip - 1)->sel &= ~ui_Disabled; + } + + USL_DrawItem(i,n - 1); +// USL_CtlDLButtonCustom(uic_Draw,i,n - 1); + + return(true); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlSButtonCustom() - The custom routine for all of the sound buttons +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlSButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + USL_ClearBottom(); + + if (n == sdm_SoundSource) + { + j = 0; + TheItems[i + 1] = ip = CtlSSSPanels; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j++); + ip++; + } + } + else + TheItems[i + 1] = nil; + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlPButtonCustom() - The custom routine for all of the start game btns +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlPButtonCustom(UserCall call,word i,word n) +{ + word j; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + USL_ClearBottom(); + + j = 0; + TheItems[i + 1] = ip = n? CtlPRPanels : CtlPSPanels; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j++); + ip++; + } + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_GiveAbortWarning() - Draws a string that warns the user that an +// action they're about to take will abort the game in progress +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_GiveAbortWarning(void) +{ + WindowRec wr; + + if (!GameIsDirty) + return; + + US_SaveWindow(&wr); + US_RestoreWindow(&BottomWindow); + US_HomeWindow(); + PrintY += 5; + + VWB_Bar(WindowX,WindowY,WindowW,30,WHITE); + fontcolor = F_SECONDCOLOR; + US_CPrint("Warning! If you do this, you'll"); + US_CPrint("abort the current game."); + fontcolor = F_BLACK; + + US_RestoreWindow(&wr); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlPSButtonCustom() - The custom routine for the start game button +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlPSButtonCustom(UserCall call,word i,word n) +{ + boolean result; + UserItem *ip; + + i++; // Shut the compiler up + + switch (call) + { + case uic_Hit: + switch (n) + { + case 0: + restartgame = gd_Normal; + break; + case 1: + restartgame = gd_Easy; + break; + case 2: + restartgame = gd_Hard; + break; + } + if (restartgame && ingame && USL_ResetGame) + USL_ResetGame(); + result = false; + break; + case uic_Draw: + USL_GiveAbortWarning(); + result = false; + break; + default: + result = false; + break; + } + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlPRButtonCustom() - The custom routine for the resume game button +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlPRButtonCustom(UserCall call,word i,word n) +{ + if (call != uic_Hit) + return(false); + + i++,n++; // Shut the compiler up + ResumeGame = true; + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlDEButtonCustom() - The custom routine for the exit to DOS button +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlDEButtonCustom(UserCall call,word i,word n) +{ + boolean result; + UserItem *ip; + + i++,n++; // Shut the compiler up + + switch (call) + { + case uic_Hit: + QuitToDos = true; + break; + case uic_Draw: + USL_GiveAbortWarning(); + default: + result = false; + break; + } + return(result); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CtlCButtonCustom() - The custom routine for all of the control +// buttons +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CtlCButtonCustom(UserCall call,word i,word n) +{ + word j; + Point p; + UserItem *ip; + + if (call != uic_Hit) + return(false); + + ip = &TheItems[i][n]; + if (ip->sel & ui_Disabled) + return(false); + + USL_ClearBottom(); + if (n == 0) // Keyboard + { + TheItems[i + 1] = ip = CtlCKbdPanels; + p = CtlCKbdPanels[2].r.lr; + VWB_DrawPic(p.x,p.y,CTL_DIRSPIC); + } + else + TheItems[i + 1] = ip = CtlCJoyPanels; + + j = 0; + while (ip && (ip->type != uii_Bad)) + { + USL_DrawItem(i + 1,j++); + ip++; + } + + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HitHotKey() - After a hotkey was hit, move the cursor to the first +// selected item in the group after the group containing the item +// holding the hotkey +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_HitHotKey(int i,int n) +{ + UserItem *ip; + + if (ip = TheItems[++i]) + { + if ((n = USL_FindDown(TheItems[i])) == -1) + n = 0; + ip += n; + CursorX = ip->r.lr.x - 8; + CursorY = ip->r.lr.y - 8; + CursorBad = true; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CheckScan() - Checks to see if the scancode in LastScan corresponds +// to anything in the list of useritems. If so, selects the item. +// +/////////////////////////////////////////////////////////////////////////// +static boolean +USL_CheckScan(word *ci,word *cn) +{ + word i,n; + UserItem *ip; + + if (!LastScan) + return(false); + +#if 1 // DEBUG - probably kill this code + // Use 1..? for the items across the top row + if (TheItems[1] && !IN_KeyDown(sc_RShift)) + { + for (i = 0,ip = TheItems[1];(ip->type != uii_Bad) && (i < 9);i++,ip++) + ; + for (n = 0;n < i;n++) + { + if (LastScan == 2 + n) // Numbers from 1..9 + { + if (!(TheItems[1][n].sel & ui_Disabled)) + { + LastScan = sc_None; + USL_DoHit(1,n); + return(true); + } + } + } + } + + // Use Alt-1..6 for the items in the leftmost column + if (IN_KeyDown(sc_RShift)) + { + n = LastScan - 2; + if (n < 6) // Numbers from 1..6 + { + USL_DoHit(0,n); + LastScan = sc_None; + return(true); + } + } +#endif + + // Check normal hotkeys for the leftmost column + for (i = 0;CtlPanels[i].type != uii_Bad;i++) + { + if (CtlPanels[i].key == LastScan) + { + LastScan = sc_None; + USL_DoHit(0,i); + *ci = 0; + *cn = i; + USL_HitHotKey(0,i); + return(true); + } + } + + // Check normal hotkeys for the top row + for (i = 0;i < 6;i++) + { + for (n = 0,ip = CtlPanels2[i];ip && ip->type != uii_Bad;n++,ip++) + { + if ((ip->key == LastScan) && !(ip->sel & ui_Disabled)) + { + LastScan = sc_None; + USL_DoHit(0,i); + USL_DoHit(1,n); + *ci = 1; + *cn = n; + USL_HitHotKey(1,n); + return(true); + } + } + } + return(false); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the +// values of all the appropriate variables +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_SetUpCtlPanel(void) +{ + word i,j; + + GameIsDirty = ingame; + + // Set up restart game + USL_TurnOff(CtlPPanels); + CtlPPanels[0].sel = ingame? ui_Normal : ui_Selected; + CtlPPanels[1].sel = ingame? ui_Selected : ui_Disabled; + + // Set up disk stuff - default to load/save game + USL_TurnOff(CtlDPanels); + CtlDPanels[0].sel = ui_Selected; + + // Set up load/save buttons + USL_TurnOff(CtlDLSPanels); + for (i = 0;i < MaxSaveGames;i++) + { + if (!Games[i].present) + CtlDLSPanels[i * 2].sel = ui_Disabled; + if (!ingame) + CtlDLSPanels[(i * 2) + 1].sel = ui_Disabled; + } + + // Set up Controls + USL_TurnOff(CtlCPanels); + CtlCPanels[1].sel = JoysPresent[0]? ui_Normal : ui_Disabled; + CtlCPanels[2].sel = JoysPresent[1]? ui_Normal : ui_Disabled; + if (Controls[0] == ctrl_Keyboard) + i = 0; + else + i = (Controls[0] == ctrl_Joystick1)? 1 : 2; + CtlCPanels[i].sel |= ui_Selected; + if (JoysPresent[1] && !JoysPresent[0]) + CtlCPanels[2].key = sc_F4; + else + CtlCPanels[1].key = sc_F4; + + // Set up Keyboard + for (i = 0;i < 10;i++) + CtlCKbdPanels[i].text = IN_GetScanName(*(KeyMaps[i])); + + // Set up Sounds + USL_TurnOff(CtlSPanels); + CtlSPanels[sdm_AdLib].sel = AdLibPresent? ui_Normal : ui_Disabled; +#if 0 // DEBUG - hack because no space for digitized sounds on Keen Dreams + CtlSPanels[sdm_SoundBlaster].sel = + SoundBlasterPresent? ui_Normal : ui_Disabled; + CtlSPanels[sdm_SoundSource].sel = + SoundSourcePresent? ui_Normal : ui_Disabled; +#else + CtlSPanels[sdm_SoundBlaster].sel = ui_Disabled; + CtlSPanels[sdm_SoundSource].sel = ui_Disabled; +#endif + CtlSPanels[SoundMode].sel |= ui_Selected; + + // Set up SoundSource + USL_TurnOff(CtlSSSPanels); + CtlSSSPanels[0].sel = ssIsTandy? ui_Selected : ui_Normal; + CtlSSSPanels[1].sel = (ssPort == 2)? ui_Selected : ui_Normal; + + // Set up Music + USL_TurnOff(CtlMPanels); + CtlMPanels[smm_AdLib].sel = AdLibPresent? ui_Normal : ui_Disabled; + CtlMPanels[MusicMode].sel |= ui_Selected; +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_TearDownCtlPanel() - Given the state of the control panel, sets the +// modes and values as appropriate +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_TearDownCtlPanel(void) +{ + int i; + + i = USL_FindDown(CtlCPanels); + if (i != -1) + { + i = i? (i == 1? ctrl_Joystick1 : ctrl_Joystick2) : ctrl_Keyboard; + IN_SetControlType(0,i); + } + + CtlCPanels[1].key = CtlCPanels[2].key = sc_None; + + i = USL_FindDown(CtlSPanels); + if (i != -1) + SD_SetSoundMode(i); + + ssIsTandy = CtlSSSPanels[0].sel & ui_Selected; + ssPort = (CtlSSSPanels[1].sel & ui_Selected)? 2 : 1; + + i = USL_FindDown(CtlMPanels); + if (i != -1) + { + SD_SetMusicMode(i); + + if (!QuitToDos) + { + US_CenterWindow(20,8); + US_CPrint("Loading"); +#if 0 + fontcolor = F_SECONDCOLOR; + US_CPrint("Sounds"); + fontcolor = F_BLACK; +#endif + VW_UpdateScreen(); + + CA_LoadAllSounds(); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ControlPanel() - This is the main routine for the control panel +// +/////////////////////////////////////////////////////////////////////////// +void +US_ControlPanel(void) +{ + char gamename[MaxGameName + 10 + 1]; + ScanCode c; + boolean done, + buttondown,inrect; + word hiti,hitn, + i,n, + lasti,lastn, + lastx,lasty; + longword lasttime; + Point p; + Rect userect; + UserItem *ip; + + c = LastScan; + if (c == sc_Escape) // Map escape from game to Exit to DOS + c = sc_Q; + + CA_UpLevel(); + for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++) + CA_MarkGrChunk(i); + CA_MarkGrChunk(CTL_LITTLEMASKPICM); + CA_MarkGrChunk(CTL_LSMASKPICM); + CA_CacheMarks("Options Screen"); + + USL_SetUpCtlPanel(); + + US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString); + fontcolor = F_BLACK; + + VW_InitDoubleBuffer(); + + VWB_Bar(0,0,MaxX,MaxY,FIRSTCOLOR); + US_DrawWindow(8,22,30,2); + US_SaveWindow(&HelpWindow); + US_DrawWindow(8,7,30,14); + US_SaveWindow(&BottomWindow); + US_DrawWindow(8,1,30,20); + + for (ip = CtlPanels;ip->type != uii_Bad;ip++) + VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,ip->picup); + + US_StartCursor(); + CursorX = (8 * 8) + ((MaxX - (8 * 8)) / 2); + CursorBad = true; + + CtlPanelButton = -1; + LastScan = c; + USL_CheckScan(&i,&n); + if (CtlPanelButton == -1) + USL_DoHit(0,0); + + ResumeGame = false; + done = false; + FlushHelp = true; + lastx = lasty = -1; + while + ( + (restartgame == gd_Continue) + && !(done || loadedgame || ResumeGame) + ) + { + VW_UpdateScreen(); + + buttondown = US_UpdateCursor(); + inrect = USL_IsInRect(CursorX,CursorY,&i,&n); + + if (FlushHelp) + { + lasti = -2; + lasttime = TimeCount; + FlushHelp = false; + } + if (inrect) + { + if ((lasti != i) || (lastn != n)) + { + // If over a Load button + if + ( + (CtlPanelButton == 2) + && (i == 2) + && (TheItems[1][0].sel & ui_Selected) + && (Games[n / 2].present) + && !(n & 1) + ) + { + strcpy(gamename,"Load `"); + strcat(gamename,Games[n / 2].name); + strcat(gamename,"'"); + USL_ShowHelp(gamename); + } + else + USL_ShowHelp(TheItems[i][n].help); + lasti = i; + lastn = n; + } + } + else if (lasti != (word)-1) + { + USL_ShowHelp("Select a Button"); + lasti = -1; + } + + hiti = i; + hitn = n; + + if (inrect) + userect = TheItems[i][n].r; + else + { + userect.ul.x = CursorX; + userect.ul.y = CursorY; + userect.lr = userect.ul; + } + + if (IN_KeyDown(sc_UpArrow)) + { + USL_FindRect(userect,motion_None,motion_Up); + buttondown = false; + IN_ClearKey(sc_UpArrow); + } + else if (IN_KeyDown(sc_DownArrow)) + { + USL_FindRect(userect,motion_None,motion_Down); + buttondown = false; + IN_ClearKey(sc_DownArrow); + } + else if (IN_KeyDown(sc_LeftArrow)) + { + USL_FindRect(userect,motion_Left,motion_None); + buttondown = false; + IN_ClearKey(sc_LeftArrow); + } + else if (IN_KeyDown(sc_RightArrow)) + { + USL_FindRect(userect,motion_Right,motion_None); + buttondown = false; + IN_ClearKey(sc_RightArrow); + } + else if + ( + IN_KeyDown(c = sc_Return) + || IN_KeyDown(c = KbdDefs[0].button0) + || IN_KeyDown(c = KbdDefs[0].button1) + ) + { + IN_ClearKey(c); + if (inrect) + { + ip = &TheItems[hiti][hitn]; + + if ((ip->type == uii_Button) && !(ip->sel & ui_Disabled)) + { + lasttime = TimeCount; + + ip->sel |= ui_Selected; + USL_DrawItem(hiti,hitn); + VW_UpdateScreen(); + + while (TimeCount - lasttime < TickBase / 4) + ; + lasttime = TimeCount; + + ip->sel &= ~ui_Selected; + USL_DrawItem(hiti,hitn); + VW_UpdateScreen(); + + while (TimeCount - lasttime < TickBase / 4) + ; + } + + USL_DoHit(hiti,hitn); + } + } + else if (USL_CheckScan(&i,&n)) + ; + else if (buttondown && inrect && USL_TrackItem(hiti,hitn)) + USL_DoHit(hiti,hitn); + + if (LastScan == sc_Escape) + { + IN_ClearKey(sc_Escape); + done = true; + } + + if (QuitToDos) + done = true; + + if ((lastx != CursorX) || (lasty != CursorY)) + { + lastx = CursorX; + lasty = CursorY; + lasttime = TimeCount; + } + if (TimeCount - lasttime > TickBase * 10) + { + if (((TimeCount - lasttime) / TickBase) & 2) + fontcolor = F_SECONDCOLOR; + USL_ShowHelp("Press F1 for Help"); + fontcolor = F_BLACK; + } + } + + US_ShutCursor(); + + USL_TearDownCtlPanel(); + + if (restartgame && USL_ResetGame) + USL_ResetGame(); + + if (QuitToDos) + { + if (tedlevel) + TEDDeath(); + else + { + US_CenterWindow(20,3); + fontcolor = F_SECONDCOLOR; + US_PrintCentered("Now Exiting to DOS..."); + fontcolor = F_BLACK; + VW_UpdateScreen(); + Quit(nil); + } + } + + CA_DownLevel(); +} + +// High score routines + +/////////////////////////////////////////////////////////////////////////// +// +// US_DisplayHighScores() - Assumes that double buffering has been started. +// If passed a -1 will just display the high scores, but if passed +// a non-negative number will display that entry in red and let the +// user type in a name +// +/////////////////////////////////////////////////////////////////////////// +void +US_DisplayHighScores(int which) +{ + char buffer[16],*str; + word i, + w,h, + x,y; + HighScore *s; + + US_CenterWindow(30,MaxScores + (MaxScores / 2)); + + x = WindowX + (WindowW / 2); + US_Print(" Name"); + PrintX = x + 20; + US_Print("Score"); + PrintX = x + 60; + US_Print("Done\n\n"); + PrintY -= 3; + + for (i = WindowX;i < WindowX + WindowW;i += 8) + VWB_DrawTile8M(i,WindowY + 8,10); + VWB_DrawTile8M(WindowX - 8,WindowY + 8,9); + VWB_DrawTile8M(WindowX + WindowW,WindowY + 8,11); + + for (i = 0,s = Scores;i < MaxScores;i++,s++) + { + fontcolor = (i == which)? F_SECONDCOLOR : F_BLACK; + + if (i != which) + { + US_Print(" "); + if (strlen(s->name)) + US_Print(s->name); + else + US_Print("-"); + } + else + y = PrintY; + + PrintX = x + (7 * 8); + ultoa(s->score,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX -= w; + US_Print(buffer); + + PrintX = x + 60; + if (s->completed) + US_PrintUnsigned(s->completed); + else + US_Print("-"); + + US_Print("\n"); + } + + if (which != -1) + { + fontcolor = F_SECONDCOLOR; + PrintY = y; + PrintX = WindowX; + US_Print(" "); + strcpy(Scores[which].name,""); + US_LineInput(PrintX,PrintY,Scores[which].name,nil,true,MaxHighName, + (WindowW / 2) - 8); + } + fontcolor = F_BLACK; + + VW_UpdateScreen(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CheckHighScore() - Checks gamestate to see if the just-ended game +// should be entered in the high score list. If so, lets the user +// enter their name +// +/////////////////////////////////////////////////////////////////////////// +void +US_CheckHighScore(long score,word other) +{ + word i,j, + n; + HighScore myscore; + + strcpy(myscore.name,""); + myscore.score = score; + myscore.completed = other; + + for (i = 0,n = -1;i < MaxScores;i++) + { + if + ( + (myscore.score > Scores[i].score) + || ( + (myscore.score == Scores[i].score) + && (myscore.completed > Scores[i].completed) + ) + ) + { + for (j = MaxScores;--j > i;) + Scores[j] = Scores[j - 1]; + Scores[i] = myscore; + + n = i; + HighScoresDirty = true; + break; + } + } + + VW_InitDoubleBuffer(); + VWB_Bar(0,0,MaxX,MaxY,FIRSTCOLOR); + + US_DisplayHighScores(n); + IN_UserInput(5 * TickBase,false); +} diff --git a/ID_US.H b/ID_US.H new file mode 100644 index 0000000..f802cc0 --- /dev/null +++ b/ID_US.H @@ -0,0 +1,147 @@ +/* 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 Engine +// ID_US.h - Header file for the User Manager +// v1.0d1 +// By Jason Blochowiak +// + +#ifndef __TYPES__ +#include "ID_Types.h" +#endif + +#ifndef __ID_US__ +#define __ID_US__ + +#ifdef __DEBUG__ +#define __DEBUG_UserMgr__ +#endif + +//#define HELPTEXTLINKED + +#define MaxX 320 +#define MaxY 200 + +#define MaxHelpLines 500 + +#define MaxHighName 57 +#define MaxScores 7 +typedef struct + { + char name[MaxHighName + 1]; + long score; + word completed; + } HighScore; + +#define MaxGameName 32 +#define MaxSaveGames 6 +typedef struct + { + char signature[4]; + word *oldtest; + boolean present; + char name[MaxGameName + 1]; + } SaveGame; + +#define MaxString 128 // Maximum input string size + +typedef struct + { + int x,y, + w,h, + px,py; + } WindowRec; // Record used to save & restore screen windows + +typedef enum + { + gd_Continue, + gd_Easy, + gd_Normal, + gd_Hard + } GameDiff; + +// Hack import for TED launch support +extern boolean tedlevel; +extern word tedlevelnum; +extern void TEDDeath(void); + +extern boolean ingame, // Set by game code if a game is in progress + abortgame, // Set if a game load failed + loadedgame, // Set if the current game was loaded + NoWait, + HighScoresDirty; +extern char *abortprogram; // Set to error msg if program is dying +extern GameDiff restartgame; // Normally gd_Continue, else starts game +extern word PrintX,PrintY; // Current printing location in the window +extern word WindowX,WindowY,// Current location of window + WindowW,WindowH;// Current size of window + +extern boolean Button0,Button1, + CursorBad; +extern int CursorX,CursorY; + +extern void (*USL_MeasureString)(char far *,word *,word *), + (*USL_DrawString)(char far *); + +extern boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int); +extern void (*USL_ResetGame)(void); +extern SaveGame Games[MaxSaveGames]; +extern HighScore Scores[]; + +#define US_HomeWindow() {PrintX = WindowX; PrintY = WindowY;} + +extern void US_Startup(void), + US_Setup(void), + US_Shutdown(void), + US_InitRndT(boolean randomize), + US_SetLoadSaveHooks(boolean (*load)(int), + boolean (*save)(int), + void (*reset)(void)), + US_TextScreen(void), + US_UpdateTextScreen(void), + US_FinishTextScreen(void), + US_ControlPanel(void), + US_DrawWindow(word x,word y,word w,word h), + US_CenterWindow(word,word), + US_SaveWindow(WindowRec *win), + US_RestoreWindow(WindowRec *win), + US_ClearWindow(void), + US_SetPrintRoutines(void (*measure)(char far *,word *,word *), + void (*print)(char far *)), + US_PrintCentered(char *s), + US_CPrint(char *s), + US_CPrintLine(char *s), + US_Print(char *s), + US_PrintUnsigned(longword n), + US_PrintSigned(long n), + US_StartCursor(void), + US_ShutCursor(void), + US_ControlPanel(void), + US_CheckHighScore(long score,word other), + US_DisplayHighScores(int which); +extern boolean US_UpdateCursor(void), + US_LineInput(int x,int y,char *buf,char *def,boolean escok, + int maxchars,int maxwidth); +extern int US_CheckParm(char *parm,char **strings), + US_RndT(void); + + void USL_PrintInCenter(char *s,Rect r); + char *USL_GiveSaveName(word game); +#endif diff --git a/ID_US_1.C b/ID_US_1.C new file mode 100644 index 0000000..ad14b46 --- /dev/null +++ b/ID_US_1.C @@ -0,0 +1,1285 @@ +/* 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 Engine +// ID_US_1.c - User Manager - General routines +// v1.1d1 +// By Jason Blochowiak +// Hacked up for Catacomb 3D +// + +// +// This module handles dealing with user input & feedback +// +// Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching, +// and Refresh Mgrs, Memory Mgr for background save/restore +// +// Globals: +// ingame - Flag set by game indicating if a game is in progress +// abortgame - Flag set if the current game should be aborted (if a load +// game fails) +// loadedgame - Flag set if a game was loaded +// abortprogram - Normally nil, this points to a terminal error message +// if the program needs to abort +// restartgame - Normally set to gd_Continue, this is set to one of the +// difficulty levels if a new game should be started +// PrintX, PrintY - Where the User Mgr will print (global coords) +// WindowX,WindowY,WindowW,WindowH - The dimensions of the current +// window +// + +#include "ID_HEADS.H" + +#pragma hdrstop + +#pragma warn -pia + + +// Special imports +extern boolean showscorebox; +#ifdef KEEN +extern boolean oldshooting; +extern ScanCode firescan; +#else + ScanCode firescan; +#endif + +// Global variables + char *abortprogram; + boolean NoWait, + HighScoresDirty; + word PrintX,PrintY; + word WindowX,WindowY,WindowW,WindowH; + +// Internal variables +#define ConfigVersion 1 + +static char *ParmStrings[] = {"TEDLEVEL","NOWAIT"}, + *ParmStrings2[] = {"COMP","NOCOMP"}; +static boolean US_Started; + + boolean Button0,Button1, + CursorBad; + int CursorX,CursorY; + + void (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString, + (*USL_DrawString)(char far *) = VWB_DrawPropString; + + boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int); + void (*USL_ResetGame)(void); + SaveGame Games[MaxSaveGames]; + HighScore Scores[MaxScores] = + { + {"Sir Lancelot",500,3}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + {"",0}, + }; + +// Internal routines + +// Public routines + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed +// from DOS. +// +/////////////////////////////////////////////////////////////////////////// +#pragma warn -par +#pragma warn -rch +int +USL_HardError(word errval,int ax,int bp,int si) +{ +#define IGNORE 0 +#define RETRY 1 +#define ABORT 2 +extern void ShutdownId(void); + +static char buf[32]; +static WindowRec wr; + int di; + char c,*s,*t; + + + di = _DI; + + if (ax < 0) + s = "Device Error"; + else + { + if ((di & 0x00ff) == 0) + s = "Drive ~ is Write Protected"; + else + s = "Error on Drive ~"; + for (t = buf;*s;s++,t++) // Can't use sprintf() + if ((*t = *s) == '~') + *t = (ax & 0x00ff) + 'A'; + *t = '\0'; + s = buf; + } + + c = peekb(0x40,0x49); // Get the current screen mode + if ((c < 4) || (c == 7)) + goto oh_kill_me; + + // DEBUG - handle screen cleanup + + US_SaveWindow(&wr); + US_CenterWindow(30,3); + US_CPrint(s); + US_CPrint("(R)etry or (A)bort?"); + VW_UpdateScreen(); + IN_ClearKeysDown(); + +asm sti // Let the keyboard interrupts come through + + while (true) + { + switch (IN_WaitForASCII()) + { + case key_Escape: + case 'a': + case 'A': + goto oh_kill_me; + break; + case key_Return: + case key_Space: + case 'r': + case 'R': + US_ClearWindow(); + VW_UpdateScreen(); + US_RestoreWindow(&wr); + return(RETRY); + break; + } + } + +oh_kill_me: + abortprogram = s; + ShutdownId(); + fprintf(stderr,"Terminal Error: %s\n",s); + if (tedlevel) + fprintf(stderr,"You launched from TED. I suggest that you reboot...\n"); + + return(ABORT); +#undef IGNORE +#undef RETRY +#undef ABORT +} +#pragma warn +par +#pragma warn +rch + +/////////////////////////////////////////////////////////////////////////// +// +// USL_GiveSaveName() - Returns a pointer to a static buffer that contains +// the filename to use for the specified save game +// +/////////////////////////////////////////////////////////////////////////// +char * +USL_GiveSaveName(word game) +{ +static char name[] = "SAVEGAMx."EXTENSION; + + name[7] = '0' + game; + return(name); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after +// reading or writing the save game headers +// +/////////////////////////////////////////////////////////////////////////// +void +US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void)) +{ + USL_LoadGame = load; + USL_SaveGame = save; + USL_ResetGame = reset; +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ReadConfig() - Reads the configuration file, if present, and sets +// things up accordingly. If it's not present, uses defaults. This file +// includes the high scores. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ReadConfig(void) +{ + boolean gotit; + char sig[sizeof(EXTENSION)]; + word version; + int file; + SDMode sd; + SMMode sm; + ControlType ctl; + + if ((file = open("CONFIG."EXTENSION,O_BINARY | O_RDONLY)) != -1) + { + read(file,sig,sizeof(EXTENSION)); + read(file,&version,sizeof(version)); + if (strcmp(sig,EXTENSION) || (version != ConfigVersion)) + { + close(file); + goto rcfailed; + } + read(file,Scores,sizeof(HighScore) * MaxScores); + read(file,&sd,sizeof(sd)); + read(file,&sm,sizeof(sm)); + read(file,&ctl,sizeof(ctl)); + read(file,&(KbdDefs[0]),sizeof(KbdDefs[0])); + read(file,&showscorebox,sizeof(showscorebox)); + read(file,&compatability,sizeof(compatability)); +#ifdef KEEN + read(file,&oldshooting,sizeof(oldshooting)); + read(file,&firescan,sizeof(firescan)); +#endif + close(file); + + HighScoresDirty = false; + gotit = true; + } + else + { +rcfailed: + sd = sdm_Off; + sm = smm_Off; + ctl = ctrl_Keyboard; + showscorebox = true; +#ifdef KEEN + oldshooting = false; +#endif + + gotit = false; + HighScoresDirty = true; + } + + SD_Default(gotit,sd,sm); + IN_Default(gotit,ctl); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_WriteConfig() - Writes out the current configuration, including the +// high scores. +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_WriteConfig(void) +{ + word version; + int file; + + version = ConfigVersion; + file = open("CONFIG."EXTENSION,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + if (file != -1) + { + write(file,EXTENSION,sizeof(EXTENSION)); + write(file,&version,sizeof(version)); + write(file,Scores,sizeof(HighScore) * MaxScores); + write(file,&SoundMode,sizeof(SoundMode)); + write(file,&MusicMode,sizeof(MusicMode)); + if // Hack + ( + (Controls[0] == ctrl_Joystick1) + || (Controls[0] == ctrl_Joystick2) + ) + Controls[0] = ctrl_Keyboard; + write(file,&(Controls[0]),sizeof(Controls[0])); + write(file,&(KbdDefs[0]),sizeof(KbdDefs[0])); + write(file,&showscorebox,sizeof(showscorebox)); + write(file,&compatability,sizeof(compatability)); +#ifdef KEEN + write(file,&oldshooting,sizeof(oldshooting)); + write(file,&firescan,sizeof(firescan)); +#endif + close(file); + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_CheckSavedGames() - Checks to see which saved games are present +// & valid +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_CheckSavedGames(void) +{ + boolean ok; + char *filename; + word i; + int file; + SaveGame *game; + + USL_SaveGame = 0; + USL_LoadGame = 0; + + for (i = 0,game = Games;i < MaxSaveGames;i++,game++) + { + filename = USL_GiveSaveName(i); + ok = false; + if ((file = open(filename,O_BINARY | O_RDONLY)) != -1) + { + if + ( + (read(file,game,sizeof(*game)) == sizeof(*game)) + && (!strcmp(game->signature,EXTENSION)) + && (game->oldtest == &PrintX) + ) + ok = true; + + close(file); + } + + if (ok) + game->present = true; + else + { + strcpy(game->signature,EXTENSION); + game->present = false; + strcpy(game->name,"Empty"); + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Startup() - Starts the User Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +US_Startup(void) +{ + int i; + + if (US_Started) + return; + + harderr(USL_HardError); // Install the fatal error handler + + US_InitRndT(true); // Initialize the random number generator + + USL_ReadConfig(); // Read config file + + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],ParmStrings2)) + { + case 0: + if (grmode == EGAGR) + compatability = true; + break; + case 1: + compatability = false; + break; + } + } + + US_Started = true; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Setup() - Does the disk access part of the User Mgr's startup +// +/////////////////////////////////////////////////////////////////////////// +void +US_Setup(void) +{ + USL_CheckSavedGames(); // Check which saved games are present +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Shutdown() - Shuts down the User Mgr +// +/////////////////////////////////////////////////////////////////////////// +void +US_Shutdown(void) +{ + if (!US_Started) + return; + + if (!abortprogram) + USL_WriteConfig(); + + US_Started = false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CheckParm() - checks to see if a string matches one of a set of +// strings. The check is case insensitive. The routine returns the +// index of the string that matched, or -1 if no matches were found +// +/////////////////////////////////////////////////////////////////////////// +int +US_CheckParm(char *parm,char **strings) +{ + char cp,cs, + *p,*s; + int i; + + while (!isalpha(*parm)) // Skip non-alphas + parm++; + + for (i = 0;*strings && **strings;i++) + { + for (s = *strings++,p = parm,cs = cp = 0;cs == cp;) + { + cs = *s++; + if (!cs) + return(i); + cp = *p++; + + if (isupper(cs)) + cs = tolower(cs); + if (isupper(cp)) + cp = tolower(cp); + } + } + return(-1); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ScreenDraw() - Draws a chunk of the text screen (called only by +// US_TextScreen()) +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ScreenDraw(word x,word y,char *s,byte attr) +{ + byte far *screen,far *oscreen; + + screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2)); + oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) + 1; + while (*s) + { + *screen++ = *s++; + if (attr != 0xff) + { + *screen++ = (attr & 0x8f) | (*oscreen & 0x70); + oscreen += 2; + } + else + screen++; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it, +// and moves the cursor to the leftmost column of the bottom line +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ClearTextScreen(void) +{ + // Set to 80x25 color text mode + _AL = 3; // Mode 3 + _AH = 0x00; + geninterrupt(0x10); + + // Use BIOS to move the cursor to the bottom of the screen + _AH = 0x0f; + geninterrupt(0x10); // Get current video mode into _BH + _DL = 0; // Lefthand side of the screen + _DH = 24; // Bottom row + _AH = 0x02; + geninterrupt(0x10); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_TextScreen() - Puts up the startup text screen +// Note: These are the only User Manager functions that can be safely called +// before the User Mgr has been started up +// +/////////////////////////////////////////////////////////////////////////// +void +US_TextScreen(void) +{ + word i,n; + + USL_ClearTextScreen(); + + _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2); + + // Check for TED launching here + for (i = 1;i < _argc;i++) + { + n = US_CheckParm(_argv[i],ParmStrings); + if (n == 0) + { + tedlevelnum = atoi(_argv[i + 1]); + if (tedlevelnum >= 0) + { + tedlevel = true; + return; + } + else + break; + } + else if (n == 1) + { + NoWait = true; + return; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_Show() - Changes the appearance of one of the fields on the text +// screen. Possibly adds a checkmark in front of it and highlights it +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_Show(word x,word y,word w,boolean show,boolean hilight) +{ + byte far *screen,far *oscreen; + + screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2)); + oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) - 1; + *screen++ = show? 251 : ' '; // Checkmark char or space +// *screen = 0x48; +// *screen = (*oscreen & 0xf0) | 8; + oscreen += 2; + if (show && hilight) + { + for (w++;w--;screen += 2,oscreen += 2) + *screen = (*oscreen & 0xf0) | 0x0f; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_ShowMem() - Right justifies a longword in one of the memory fields on +// the text screen +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_ShowMem(word x,word y,long mem) +{ + char buf[16]; + word i; + + for (i = strlen(ltoa(mem,buf,10));i < 5;i++) + USL_ScreenDraw(x++,y," ",0xff); + USL_ScreenDraw(x,y,buf,0xff); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_UpdateTextScreen() - Called after the ID libraries are started up. +// Displays what hardware is present. +// +/////////////////////////////////////////////////////////////////////////// +void +US_UpdateTextScreen(void) +{ + boolean b; + longword totalmem; + + // Show video card info + b = (grmode == CGAGR); + USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b); + b = (grmode == EGAGR); + USL_Show(21,8,4,(videocard >= EGAcard) && (videocard <= VGAcard),b); + b = (grmode == VGAGR); + USL_Show(21,9,4,videocard == VGAcard,b); + if (compatability) + USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f); + + // Show input device info + USL_Show(60,7,8,true,true); + USL_Show(60,8,11,JoysPresent[0],true); + USL_Show(60,9,11,JoysPresent[1],true); + USL_Show(60,10,5,MousePresent,true); + + // Show sound hardware info + USL_Show(21,14,11,true,SoundMode == sdm_PC); + b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib); + USL_Show(21,15,14,AdLibPresent,b); + if (b && AdLibPresent) // Hack because of two lines + { + byte far *screen,far *oscreen; + word x,y,w; + + x = 21; + y = 16; + w = 14; + screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2) - 1); + oscreen = (&introscn + 7) + (x * 2) + (y * 80 * 2) - 1; + oscreen += 2; + for (w++;w--;screen += 2,oscreen += 2) + *screen = (*oscreen & 0xf0) | 0x0f; + } + + // Show memory available/used + USL_ShowMem(63,15,mminfo.mainmem / 1024); + USL_Show(53,15,23,true,true); + USL_ShowMem(63,16,mminfo.EMSmem / 1024); + USL_Show(53,16,23,mminfo.EMSmem? true : false,true); + USL_ShowMem(63,17,mminfo.XMSmem / 1024); + USL_Show(53,17,23,mminfo.XMSmem? true : false,true); + totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem; + USL_ShowMem(63,18,totalmem / 1024); + USL_Show(53,18,23,true,true); // DEBUG + USL_ScreenDraw(52,18," ",0xff); + + // Change Initializing... to Loading... + USL_ScreenDraw(27,22," Loading... ",0x9c); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_FinishTextScreen() - After the main program has finished its initial +// loading, this routine waits for a keypress and then clears the screen +// +/////////////////////////////////////////////////////////////////////////// +void +US_FinishTextScreen(void) +{ +static byte colors[] = {4,6,13,15,15,15,15,15,15}; + boolean up; + int i,c; + + // Change Loading... to Press a Key + + if (!(tedlevel || NoWait)) + { + // IN_ClearKeysDown(); + for (i = 0,up = true;!IN_UserInput(4,true);) + { + c = colors[i]; + if (up) + { + if (++i == 9) + i = 8,up = false; + } + else + { + if (--i < 0) + i = 1,up = true; + } + + // USL_ScreenDraw(29,22," Ready - Press a Key ",0x00 + c); + } + } + // else + // USL_ScreenDraw(29,22," Ready - Press a Key ",0x9a); + // IN_ClearKeysDown(); + + USL_ClearTextScreen(); +} + +// Window/Printing routines + +/////////////////////////////////////////////////////////////////////////// +// +// US_SetPrintRoutines() - Sets the routines used to measure and print +// from within the User Mgr. Primarily provided to allow switching +// between masked and non-masked fonts +// +/////////////////////////////////////////////////////////////////////////// +void +US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *)) +{ + USL_MeasureString = measure; + USL_DrawString = print; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_Print() - Prints a string in the current window. Newlines are +// supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_Print(char *s) +{ + char c,*se; + word w,h; + + while (*s) + { + se = s; + while ((c = *se) && (c != '\n')) + se++; + *se = '\0'; + + USL_MeasureString(s,&w,&h); + px = PrintX; + py = PrintY; + USL_DrawString(s); + + s = se; + if (c) + { + *se = c; + s++; + + PrintX = WindowX; + PrintY += h; + } + else + PrintX += w; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintUnsigned() - Prints an unsigned long +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintUnsigned(longword n) +{ + char buffer[32]; + + US_Print(ultoa(n,buffer,10)); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintSigned() - Prints a signed long +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintSigned(long n) +{ + char buffer[32]; + + US_Print(ltoa(n,buffer,10)); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_PrintInCenter() - Prints a string in the center of the given rect +// +/////////////////////////////////////////////////////////////////////////// +void +USL_PrintInCenter(char *s,Rect r) +{ + word w,h, + rw,rh; + + USL_MeasureString(s,&w,&h); + rw = r.lr.x - r.ul.x; + rh = r.lr.y - r.ul.y; + + px = r.ul.x + ((rw - w) / 2); + py = r.ul.y + ((rh - h) / 2); + USL_DrawString(s); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_PrintCentered() - Prints a string centered in the current window. +// +/////////////////////////////////////////////////////////////////////////// +void +US_PrintCentered(char *s) +{ + Rect r; + + r.ul.x = WindowX; + r.ul.y = WindowY; + r.lr.x = r.ul.x + WindowW; + r.lr.y = r.ul.y + WindowH; + + USL_PrintInCenter(s,r); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CPrintLine() - Prints a string centered on the current line and +// advances to the next line. Newlines are not supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_CPrintLine(char *s) +{ + word w,h; + + USL_MeasureString(s,&w,&h); + + if (w > WindowW) + Quit("US_CPrintLine() - String exceeds width"); + px = WindowX + ((WindowW - w) / 2); + py = PrintY; + USL_DrawString(s); + PrintY += h; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CPrint() - Prints a string in the current window. Newlines are +// supported. +// +/////////////////////////////////////////////////////////////////////////// +void +US_CPrint(char *s) +{ + char c,*se; + + while (*s) + { + se = s; + while ((c = *se) && (c != '\n')) + se++; + *se = '\0'; + + US_CPrintLine(s); + + s = se; + if (c) + { + *se = c; + s++; + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ClearWindow() - Clears the current window to white and homes the +// cursor +// +/////////////////////////////////////////////////////////////////////////// +void +US_ClearWindow(void) +{ + VWB_Bar(WindowX,WindowY,WindowW,WindowH,WHITE); + PrintX = WindowX; + PrintY = WindowY; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_DrawWindow() - Draws a frame and sets the current window parms +// +/////////////////////////////////////////////////////////////////////////// +void +US_DrawWindow(word x,word y,word w,word h) +{ + word i, + sx,sy,sw,sh; + + WindowX = x * 8; + WindowY = y * 8; + WindowW = w * 8; + WindowH = h * 8; + + PrintX = WindowX; + PrintY = WindowY; + + sx = (x - 1) * 8; + sy = (y - 1) * 8; + sw = (w + 1) * 8; + sh = (h + 1) * 8; + + US_ClearWindow(); + + VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6); + for (i = sx + 8;i <= sx + sw - 8;i += 8) + VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7); + VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8); + + for (i = sy + 8;i <= sy + sh - 8;i += 8) + VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// +void +US_CenterWindow(word w,word h) +{ + US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_CenterSaveWindow() - Generates a window of a given width & height in +// the middle of the screen, saving the background +// +/////////////////////////////////////////////////////////////////////////// +void +US_CenterSaveWindow(word w,word h,memptr *save) +{ + word x,y, + screen; + + x = ((MaxX / 8) - w) / 2; + y = ((MaxY / 8) - h) / 2; + MM_GetPtr(save,(w * h) * CHARWIDTH); + screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH); + VW_ScreenToMem(screen,*save,w * CHARWIDTH,h); + US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_RestoreSaveWindow() - Restores the background of the size of the +// current window from the memory specified by save +// +/////////////////////////////////////////////////////////////////////////// +void +US_RestoreSaveWindow(memptr *save) +{ + word screen; + + screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH); + VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH); + MM_FreePtr(save); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_SaveWindow() - Saves the current window parms into a record for +// later restoration +// +/////////////////////////////////////////////////////////////////////////// +void +US_SaveWindow(WindowRec *win) +{ + win->x = WindowX; + win->y = WindowY; + win->w = WindowW; + win->h = WindowH; + + win->px = PrintX; + win->py = PrintY; +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_RestoreWindow() - Sets the current window parms to those held in the +// record +// +/////////////////////////////////////////////////////////////////////////// +void +US_RestoreWindow(WindowRec *win) +{ + WindowX = win->x; + WindowY = win->y; + WindowW = win->w; + WindowH = win->h; + + PrintX = win->px; + PrintY = win->py; +} + +// Cursor routines + +#if 0 +/////////////////////////////////////////////////////////////////////////// +// +// US_StartCursor() - Sets up the cursor for User Mgr use +// +/////////////////////////////////////////////////////////////////////////// +void +US_StartCursor(void) +{ + CursorInfo info; + + VW_SetCursor(CURSORARROWSPR); + CursorX = MaxX / 2; + CursorY = MaxY / 2; + VW_MoveCursor(CursorX,CursorY); + VW_ShowCursor(); + + IN_ReadCursor(&info); // Dispose of any accumulated movement +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ShutCursor() - Cleans up after US_StartCursor() +// +/////////////////////////////////////////////////////////////////////////// +void +US_ShutCursor(void) +{ + VW_HideCursor(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_UpdateCursor() - Gets the new cursor position & button states from +// the Input Mgr and tells the View Mgr where the cursor is +// +/////////////////////////////////////////////////////////////////////////// +boolean +US_UpdateCursor(void) +{ + CursorInfo info; + + IN_ReadCursor(&info); + if (info.x || info.y || CursorBad) + { + CursorX += info.x; + if (CursorX >= MaxX) + CursorX = MaxX - 1; + else if (CursorX < 0) + CursorX = 0; + + CursorY += info.y; + if (CursorY >= MaxY) + CursorY = MaxY - 1; + else if (CursorY < 0) + CursorY = 0; + + VW_MoveCursor(CursorX,CursorY); + CursorBad = false; + } + Button0 = info.button0; + Button1 = info.button1; + return(Button0 || Button1); +} +#endif + +// Input routines + +/////////////////////////////////////////////////////////////////////////// +// +// USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput() +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_XORICursor(int x,int y,char *s,word cursor) +{ + char buf[MaxString]; + word w,h; + + strcpy(buf,s); + buf[cursor] = '\0'; + USL_MeasureString(buf,&w,&h); + + px = x + w - 1; + py = y; + USL_DrawString("\x80"); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_LineInput() - Gets a line of user input at (x,y), the string defaults +// to whatever is pointed at by def. Input is restricted to maxchars +// chars or maxwidth pixels wide. If the user hits escape (and escok is +// true), nothing is copied into buf, and false is returned. If the +// user hits return, the current string is copied into buf, and true is +// returned +// +/////////////////////////////////////////////////////////////////////////// +boolean +US_LineInput(int x,int y,char *buf,char *def,boolean escok, + int maxchars,int maxwidth) +{ + boolean redraw, + cursorvis,cursormoved, + done,result; + ScanCode sc; + char c, + s[MaxString],olds[MaxString]; + word i, + cursor, + w,h, + len; + longword lasttime; + + VW_HideCursor(); + + if (def) + strcpy(s,def); + else + *s = '\0'; + *olds = '\0'; + cursor = strlen(s); + cursormoved = redraw = true; + + cursorvis = done = false; + lasttime = TimeCount; + LastASCII = key_None; + LastScan = sc_None; + + while (!done) + { + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + + asm pushf + asm cli + + sc = LastScan; + LastScan = sc_None; + c = LastASCII; + LastASCII = key_None; + + asm popf + + switch (sc) + { + case sc_LeftArrow: + if (cursor) + cursor--; + c = key_None; + cursormoved = true; + break; + case sc_RightArrow: + if (s[cursor]) + cursor++; + c = key_None; + cursormoved = true; + break; + case sc_Home: + cursor = 0; + c = key_None; + cursormoved = true; + break; + case sc_End: + cursor = strlen(s); + c = key_None; + cursormoved = true; + break; + + case sc_Return: + strcpy(buf,s); + done = true; + result = true; + c = key_None; + break; + case sc_Escape: + if (escok) + { + done = true; + result = false; + } + c = key_None; + break; + + case sc_BackSpace: + if (cursor) + { + strcpy(s + cursor - 1,s + cursor); + cursor--; + redraw = true; + } + c = key_None; + cursormoved = true; + break; + case sc_Delete: + if (s[cursor]) + { + strcpy(s + cursor,s + cursor + 1); + redraw = true; + } + c = key_None; + cursormoved = true; + break; + + case 0x4c: // Keypad 5 + case sc_UpArrow: + case sc_DownArrow: + case sc_PgUp: + case sc_PgDn: + case sc_Insert: + c = key_None; + break; + } + + if (c) + { + len = strlen(s); + USL_MeasureString(s,&w,&h); + + if + ( + isprint(c) + && (len < MaxString - 1) + && ((!maxchars) || (len < maxchars)) + && ((!maxwidth) || (w < maxwidth)) + ) + { + for (i = len + 1;i > cursor;i--) + s[i] = s[i - 1]; + s[cursor++] = c; + redraw = true; + } + } + + if (redraw) + { + px = x; + py = y; + USL_DrawString(olds); + strcpy(olds,s); + + px = x; + py = y; + USL_DrawString(s); + + redraw = false; + } + + if (cursormoved) + { + cursorvis = false; + lasttime = TimeCount - TickBase; + + cursormoved = false; + } + if (TimeCount - lasttime > TickBase / 2) + { + lasttime = TimeCount; + + cursorvis ^= true; + } + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + + VW_UpdateScreen(); + } + + if (cursorvis) + USL_XORICursor(x,y,s,cursor); + if (!result) + { + px = x; + py = y; + USL_DrawString(olds); + } + VW_ShowCursor(); + VW_UpdateScreen(); + + IN_ClearKeysDown(); + return(result); +} diff --git a/ID_US_2.C b/ID_US_2.C new file mode 100644 index 0000000..d8b4dea --- /dev/null +++ b/ID_US_2.C @@ -0,0 +1,1818 @@ +/* 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 Engine +// ID_US.c - User Manager - User interface +// v1.1d1 +// By Jason Blochowiak +// Hacked up for Catacomb 3D +// + +#include "ID_HEADS.H" +#pragma hdrstop + +#pragma warn -pia + +// Special imports +extern boolean showscorebox; +#ifdef KEEN +extern boolean oldshooting; +extern ScanCode firescan; +#else + ScanCode firescan; +#endif + +// Global variables + boolean ingame,abortgame,loadedgame; + GameDiff restartgame = gd_Continue; + +// Internal variables +static boolean GameIsDirty, + QuitToDos, + CtlPanelDone; + +// Forward reference prototypes +static void USL_SetupCard(void); + +// Control panel data + +#define CtlPanelSX 74 +#define CtlPanelSY 48 +#define CtlPanelEX 234 +#define CtlPanelEY 150 +#define CtlPanelW (CtlPanelEX - CtlPanelSX) +#define CtlPanelH (CtlPanelEY - CtlPanelSY) + +#define TileBase 92 + +// DEBUG - CGA +#define BackColor 0 +#define HiliteColor (BackColor ^ 12) +#define NohiliteColor (BackColor ^ 4) + +typedef enum + { + uc_None, + uc_Return, + uc_Abort, + uc_Quit, + uc_Loaded, + uc_SEasy, + uc_SNormal, + uc_SHard, + } UComm; +typedef enum + { + uii_Bad, + uii_Button,uii_RadioButton,uii_Folder + } UIType; +typedef enum + { + ui_Normal = 0, + ui_Pushed = 1, + ui_Selected = 2, + ui_Disabled = 4, + ui_Separated = 8 + } UIFlags; +#define UISelectFlags (ui_Pushed | ui_Selected | ui_Disabled) + +typedef enum + { + uic_SetupCard,uic_DrawCard,uic_TouchupCard, + uic_DrawIcon,uic_Draw,uic_Hit + } UserCall; + +typedef struct UserItem + { + UIType type; + UIFlags flags; + ScanCode hotkey; + char *text; + UComm comm; + void far *child; // Should be (UserItemGroup *) + + word x,y; + } UserItem; +typedef struct UserItemGroup + { + word x,y; + graphicnums title; + ScanCode hotkey; + UserItem far *items; + boolean (*custom)(UserCall,struct UserItem far *); // Custom routine + + word cursor; + struct UserItemGroup far *parent; + } UserItemGroup; + +static char *BottomS1,*BottomS2,*BottomS3; +static UComm Communication; +static ScanCode *KeyMaps[] = + { + &KbdDefs[0].button0, + &KbdDefs[0].button1, + &firescan, + &KbdDefs[0].upleft, + &KbdDefs[0].up, + &KbdDefs[0].upright, + &KbdDefs[0].right, + &KbdDefs[0].downright, + &KbdDefs[0].down, + &KbdDefs[0].downleft, + &KbdDefs[0].left + }; + +// Custom routine prototypes +static boolean USL_ConfigCustom(UserCall call,struct UserItem far *item), + USL_KeyCustom(UserCall call,struct UserItem far *item), + USL_KeySCustom(UserCall call,struct UserItem far *item), + USL_Joy1Custom(UserCall call,struct UserItem far *item), + USL_Joy2Custom(UserCall call,struct UserItem far *item), + USL_LoadCustom(UserCall call,struct UserItem far *item), + USL_SaveCustom(UserCall call,struct UserItem far *item), + USL_ScoreCustom(UserCall call,struct UserItem far *item), + USL_CompCustom(UserCall call,struct UserItem far *item), +#ifdef KEEN + USL_TwoCustom(UserCall call,struct UserItem far *item), +#endif + USL_PongCustom(UserCall call,struct UserItem far *item); + +#define DefButton(key,text) uii_Button,ui_Normal,key,text +#define DefRButton(key,text) uii_RadioButton,ui_Normal,key,text +#define DefFolder(key,text,child) uii_Folder,ui_Normal,key,text,uc_None,child +#define CustomGroup(title,key,custom) 0,0,title,key,0,custom + UserItem far holder[] = + { + {DefButton(sc_None,"DEBUG")}, + {uii_Bad} + }; + UserItemGroup far holdergroup = {0,0,CP_MAINMENUPIC,sc_None,holder}; + + // Sound menu + UserItem far soundi[] = + { + {DefRButton(sc_N,"NO SOUND EFFECTS")}, + {DefRButton(sc_P,"PC SPEAKER")}, + {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")}, + {uii_Bad} + }; + UserItemGroup far soundgroup = {8,0,CP_SOUNDMENUPIC,sc_None,soundi}; + + // Music menu + UserItem far musici[] = + { + {DefRButton(sc_N,"NO MUSIC")}, + {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")}, + {uii_Bad} + }; + UserItemGroup far musicgroup = {8,0,CP_MUSICMENUPIC,sc_None,musici}; + + // New game menu + UserItem far newgamei[] = + { + {DefButton(sc_E,"BEGIN EASY GAME"),uc_SEasy}, + {DefButton(sc_N,"BEGIN NORMAL GAME"),uc_SNormal}, + {DefButton(sc_H,"BEGIN HARD GAME"),uc_SHard}, + {uii_Bad} + }; + UserItemGroup far newgamegroup = {8,0,CP_NEWGAMEMENUPIC,sc_None,newgamei,0,1}; + + // Load/Save game menu + UserItem far loadsavegamei[] = + { + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Button,ui_Normal,sc_None}, + {uii_Bad} + }; + UserItemGroup far loadgamegroup = {4,3,CP_LOADMENUPIC,sc_None,loadsavegamei,USL_LoadCustom}; + UserItemGroup far savegamegroup = {4,3,CP_SAVEMENUPIC,sc_None,loadsavegamei,USL_SaveCustom}; + + // Options menu + UserItemGroup far scoregroup = {0,0,0,sc_None,0,USL_ScoreCustom}; + UserItemGroup far compgroup = {0,0,0,sc_None,0,USL_CompCustom}; +#ifdef KEEN + UserItemGroup far twogroup = {0,0,0,sc_None,0,USL_TwoCustom}; +#endif + UserItem far optionsi[] = + { + {DefFolder(sc_S,"",&scoregroup)}, + {DefFolder(sc_C,"",&compgroup)}, +#ifdef KEEN + {DefFolder(sc_T,"",&twogroup)}, +#endif + {uii_Bad} + }; + UserItemGroup far optionsgroup = {8,0,CP_OPTIONSMENUPIC,sc_None,optionsi}; + + // Keyboard menu + UserItem far keyi[] = + { + {DefButton(sc_None,"UP & LEFT")}, + {DefButton(sc_None,"UP")}, + {DefButton(sc_None,"UP & RIGHT")}, + {DefButton(sc_None,"RIGHT")}, + {DefButton(sc_None,"DOWN & RIGHT")}, + {DefButton(sc_None,"DOWN")}, + {DefButton(sc_None,"DOWN & LEFT")}, + {DefButton(sc_None,"LEFT")}, + {uii_Bad} + }; + UserItemGroup far keygroup = {0,0,CP_KEYMOVEMENTPIC,sc_None,keyi,USL_KeyCustom}; + UserItem far keybi[] = + { +#ifdef KEEN + {DefButton(sc_J,"JUMP")}, + {DefButton(sc_P,"POGO")}, + {DefButton(sc_F,"FIRE")}, +#endif +#ifdef CAT3D + {DefButton(sc_J,"FIRE")}, + {DefButton(sc_P,"STRAFE")}, +#endif +#ifdef CPD + {DefButton(sc_J,"SHOOT")}, + {DefButton(sc_P,"BOMB")}, +#endif + {uii_Bad} + }; + UserItemGroup far keybgroup = {0,0,CP_KEYBUTTONPIC,sc_None,keybi,USL_KeyCustom}; + UserItem far keysi[] = + { + {DefFolder(sc_M,"MOVEMENT",&keygroup)}, + {DefFolder(sc_B,"BUTTONS",&keybgroup)}, + {uii_Bad} + }; + UserItemGroup far keysgroup = {8,0,CP_KEYBOARDMENUPIC,sc_None,keysi,USL_KeySCustom}; + + // Joystick #1 & #2 + UserItemGroup far joy1group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy1Custom)}; + UserItemGroup far joy2group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy2Custom)}; + + // Config menu + UserItem far configi[] = + { + {DefFolder(sc_S,"SOUND",&soundgroup)}, + {DefFolder(sc_M,"MUSIC",&musicgroup)}, + {uii_Folder,ui_Separated,sc_K,"USE KEYBOARD",uc_None,&keysgroup}, + {DefFolder(sc_None,"USE JOYSTICK #1",&joy1group)}, + {DefFolder(sc_None,"USE JOYSTICK #2",&joy2group)}, + {uii_Bad} + }; + UserItemGroup far configgroup = {8,0,CP_CONFIGMENUPIC,sc_None,configi,USL_ConfigCustom}; + + // Main menu + UserItemGroup far ponggroup = {0,0,0,sc_None,0,USL_PongCustom}; + UserItem far rooti[] = + { + {DefFolder(sc_N,"NEW GAME",&newgamegroup)}, + {DefFolder(sc_L,"LOAD GAME",&loadgamegroup)}, + {DefFolder(sc_S,"SAVE GAME",&savegamegroup)}, + {DefFolder(sc_C,"CONFIGURE",&configgroup)}, + {DefButton(sc_R,nil),uc_Return}, // Return to Game/Demo + {DefButton(sc_E,"END GAME"),uc_Abort}, + {DefFolder(sc_B,"SKULL 'N' BONES",&ponggroup)}, + {DefButton(sc_Q,"QUIT"),uc_Quit}, + {uii_Bad} + }; + UserItemGroup far rootgroup = {32,4,CP_MAINMENUPIC,sc_None,rooti}; +#undef DefButton +#undef DefFolder + +#define MaxCards 7 + word cstackptr; + UserItemGroup far *cardstack[MaxCards], + far *topcard; + +// Card stack code +static void +USL_SetupStack(void) +{ + cstackptr = 0; + cardstack[0] = topcard = &rootgroup; +} + +static void +USL_PopCard(void) +{ + if (!cstackptr) + return; + + topcard = cardstack[--cstackptr]; +} + +static void +USL_PushCard(UserItemGroup far *card) +{ + if (cstackptr == MaxCards - 1) + return; + + topcard = cardstack[++cstackptr] = card; +} + +static void +USL_DrawItemIcon(UserItem far *item) +{ + word flags,tile; + + if (topcard->custom && topcard->custom(uic_DrawIcon,item)) + return; + + flags = item->flags; + if (flags & ui_Disabled) + tile = TileBase + ((flags & ui_Selected)? 5 : 4); + else if ((item->type == uii_RadioButton) && (!(flags & ui_Pushed))) + tile = TileBase + ((flags & ui_Selected)? 3 : 2); + else + tile = TileBase + ((flags & ui_Selected)? 1 : 0); + VWB_DrawTile8(item->x,item->y,tile); +} + +static void +USL_DrawItem(UserItem far *item) +{ + if (topcard->custom && topcard->custom(uic_Draw,item)) + return; + + VWB_Bar(CtlPanelSX + 1,item->y, + CtlPanelEX - CtlPanelSX - 1,8,BackColor); // Clear out background + USL_DrawItemIcon(item); + if ((item->flags & ui_Selected) && !(item->flags & ui_Disabled)) + fontcolor = HiliteColor; + else + fontcolor = NohiliteColor; + px = item->x + 8; + py = item->y + 1; + USL_DrawString(item->text); + fontcolor = F_BLACK; +} + +#define MyLine(y) VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,y,12); + +static void +USL_DrawBottom(void) +{ + word w,h; + + fontcolor = NohiliteColor; + + px = CtlPanelSX + 4; + py = CtlPanelEY - 15; + USL_DrawString(BottomS1); + + USL_MeasureString(BottomS2,&w,&h); + px = CtlPanelEX - 4 - w; + USL_DrawString(BottomS2); + + USL_MeasureString(BottomS3,&w,&h); + px = CtlPanelSX + ((CtlPanelEX - CtlPanelSX - w) / 2); + py += h + 1; + USL_DrawString(BottomS3); + + fontcolor = F_WHITE; + MyLine(CtlPanelEY - 17); +} + +static void +USL_DrawCtlPanelContents(void) +{ + int x,y; + UserItem far *item; + + if (topcard->custom && topcard->custom(uic_DrawCard,nil)) + return; + + if (topcard->title) + { + // Draw the title + MyLine(CtlPanelSY + 7); + VWB_DrawPic(CtlPanelSX + 6,CtlPanelSY,topcard->title); + } + + USL_DrawBottom(); + + if (!topcard->items) + return; + + x = topcard->x + CtlPanelSX; + if (x % 8) + x += 8 - (x % 8); + y = topcard->y + CtlPanelSY + 12; + for (item = topcard->items;item->type != uii_Bad;item++) + { + if (item->flags & ui_Separated) + y += 8; + + item->x = x; + item->y = y; + USL_DrawItem(item); + y += 8; + } + if (topcard->custom) + topcard->custom(uic_TouchupCard,nil); +} + +static void +USL_DrawCtlPanel(void) +{ + if (topcard->items || topcard->title) + { + // Draw the backdrop + VWB_DrawPic(0,0,CP_MENUSCREENPIC); + + // Draw the contents + USL_DrawCtlPanelContents(); + } + + // Refresh the screen + VW_UpdateScreen(); +} + +static void +USL_DialogSetup(word w,word h,word *x,word *y) +{ + VWB_DrawMPic(CtlPanelSX,CtlPanelSY,CP_MENUMASKPICM); + + *x = CtlPanelSX + ((CtlPanelW - w) / 2); + *y = CtlPanelSY + ((CtlPanelH - h) / 2); + VWB_Bar(*x,*y,w + 1,h + 1,BackColor); + VWB_Hlin(*x - 1,*x + w + 1,*y - 1,NohiliteColor); + VWB_Hlin(*x - 1,*x + w + 1,*y + h + 1,NohiliteColor); + VWB_Vlin(*y - 1,*y + h + 1,*x - 1,NohiliteColor); + VWB_Vlin(*y - 1,*y + h + 1,*x + w + 1,NohiliteColor); +} + +static void +USL_ShowLoadSave(char *s,char *name) +{ + word x,y, + w,h, + tw,sw; + char msg[MaxGameName + 4]; + + strcpy(msg,"'"); + strcat(msg,name); + strcat(msg,"'"); + USL_MeasureString(s,&sw,&h); + USL_MeasureString(msg,&w,&h); + tw = ((sw > w)? sw : w) + 6; + USL_DialogSetup(tw,(h * 2) + 2,&x,&y); + py = y + 2; + px = x + ((tw - sw) / 2); + USL_DrawString(s); + py += h; + px = x + ((tw - w) / 2); + USL_DrawString(msg); + + VW_UpdateScreen(); + + IN_UserInput(100, true); +} + +static boolean +USL_CtlDialog(char *s1,char *s2,char *s3) +{ + word w,h,sh, + w1,w2,w3, + x,y; + ScanCode c; + CursorInfo cursorinfo; + + USL_MeasureString(s1,&w1,&h); + USL_MeasureString(s2,&w2,&h); + if (s3) + USL_MeasureString(s3,&w3,&h); + else + w3 = 0; + w = (w1 > w2)? ((w1 > w3)? w1 : w3) : ((w2 > w3)? w2 : w3); + w += 7; + sh = h; + h *= s3? 5 : 4; + + USL_DialogSetup(w,h,&x,&y); + + fontcolor = HiliteColor; + px = x + ((w - w1) / 2); + py = y + sh + 1; + USL_DrawString(s1); + py += (sh * 2) - 1; + + VWB_Hlin(x + 3,x + w - 3,py,NohiliteColor); + py += 2; + + fontcolor = NohiliteColor; + px = x + ((w - w2) / 2); + USL_DrawString(s2); + py += sh; + + if (s3) + { + px = x + ((w - w3) / 2); + USL_DrawString(s3); + } + + VW_UpdateScreen(); + + IN_ClearKeysDown(); + do + { + IN_ReadCursor(&cursorinfo); + if (cursorinfo.button0) + c = sc_Y; + else if (cursorinfo.button1) + c = sc_Escape; + else + c = LastScan; + } while (c == sc_None); + do + { + IN_ReadCursor(&cursorinfo); + } while (cursorinfo.button0 || cursorinfo.button1); + + IN_ClearKeysDown(); + USL_DrawCtlPanel(); + return(c == sc_Y); +} + +static boolean +USL_ConfirmComm(UComm comm) +{ + boolean confirm,dialog; + char *s1,*s2,*s3; + + if (!comm) + Quit("USL_ConfirmComm() - empty comm"); + + confirm = true; + dialog = false; + s3 = "ESC TO BACK OUT"; + switch (comm) + { + case uc_Abort: + s1 = "REALLY END CURRENT GAME?"; + s2 = "PRESS Y TO END IT"; + if (ingame && GameIsDirty) + dialog = true; + break; + case uc_Quit: + s1 = "REALLY QUIT?"; + s2 = "PRESS Y TO QUIT"; + dialog = true; + break; + case uc_Loaded: + s1 = "YOU'RE IN A GAME"; + s2 = "PRESS Y TO LOAD GAME"; + if (ingame && GameIsDirty) + dialog = true; + break; + case uc_SEasy: + case uc_SNormal: + case uc_SHard: + s1 = "YOU'RE IN A GAME"; + s2 = "PRESS Y FOR NEW GAME"; + if (ingame && GameIsDirty) + dialog = true; + break; + } + + confirm = dialog? USL_CtlDialog(s1,s2,s3) : true; + if (confirm) + { + Communication = comm; + CtlPanelDone = true; + } + return(confirm); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_HandleError() - Handles telling the user that there's been an error +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_HandleError(int num) +{ + char buf[64]; + + strcpy(buf,"Error: "); + if (num < 0) + strcat(buf,"Unknown"); + else if (num == ENOMEM) + strcat(buf,"Disk is Full"); + else if (num == EINVFMT) + strcat(buf,"File is Incomplete"); + else + strcat(buf,sys_errlist[num]); + + VW_HideCursor(); + + USL_CtlDialog(buf,"PRESS ANY KEY",nil); + VW_UpdateScreen(); + + IN_ClearKeysDown(); + IN_Ack(); + + VW_ShowCursor(); + VW_UpdateScreen(); +} + +// Custom routines +#if 0 +static boolean +USL_GenericCustom(UserCall call,UserItem far *item) +{ + boolean result; + + result = false; + switch (call) + { + } + return(result); +} +#endif + +static void +USL_SetOptionsText(void) +{ + optionsi[0].text = showscorebox? "SCORE BOX (ON)" : "SCORE BOX (OFF)"; + optionsi[1].text = compatability? "SVGA COMPATIBILITY (ON)" : "SVGA COMPATIBILITY (OFF)"; +#ifdef KEEN + optionsi[2].text = oldshooting? "TWO-BUTTON FIRING (ON)" : "TWO-BUTTON FIRING (OFF)"; + + keybi[2].flags &= ~ui_Disabled; + if (oldshooting) + keybi[2].flags |= ui_Disabled; +#endif +} + +#pragma argsused +static boolean +USL_ScoreCustom(UserCall call,UserItem far *item) +{ + if (call != uic_SetupCard) + return(false); + + showscorebox ^= true; + USL_CtlDialog(showscorebox? "Score box now on" : "Score box now off", + "Press any key",nil); + USL_SetOptionsText(); + return(true); +} + +#pragma argsused +static boolean +USL_CompCustom(UserCall call,UserItem far *item) +{ + if (call != uic_SetupCard) + return(false); + + compatability ^= true; + USL_CtlDialog(compatability? "SVGA compatibility now on" : "SVGA compatibility now off", + "Press any key",nil); + USL_SetOptionsText(); + return(true); +} + +#ifdef KEEN +#pragma argsused +static boolean +USL_TwoCustom(UserCall call,UserItem far *item) +{ + if (call != uic_SetupCard) + return(false); + + oldshooting ^= true; + USL_CtlDialog(oldshooting? "Two-button firing now on" : "Two-button firing now off", + "Press any key",nil); + USL_SetOptionsText(); + return(true); +} +#endif + +static boolean +USL_ConfigCustom(UserCall call,UserItem far *item) +{ +static char *CtlNames[] = {"KEYBOARD","KEYBOARD","JOYSTICK #1","JOYSTICK #2","MOUSE"}; + char *s; + word w,h, + tw; + + if (call == uic_TouchupCard) + { + s = "CONTROL: "; + USL_MeasureString(s,&w,&h); + tw = w; + USL_MeasureString(CtlNames[Controls[0]],&w,&h); + tw += w; + py = CtlPanelEY - 18 - h; + px = CtlPanelSX + ((CtlPanelW - tw) / 2); + fontcolor = NohiliteColor; + USL_DrawString(s); + USL_DrawString(CtlNames[Controls[0]]); + } + item++; // Shut the compiler up + return(false); +} + +static void +USL_CKSetKey(UserItem far *item,word i) +{ + boolean on; + word j; + ScanCode scan; + longword time; + CursorInfo cursorinfo; + + on = false; + time = 0; + LastScan = sc_None; + fontcolor = HiliteColor; + do + { + if (TimeCount >= time) + { + on ^= true; + VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor); + VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor); + if (on) + VWB_DrawTile8(item->x + 90 + 16,item->y,TileBase + 8); + VW_UpdateScreen(); + + time = TimeCount + (TickBase / 2); + } + + IN_ReadCursor(&cursorinfo); + while (cursorinfo.button0 || cursorinfo.button1) + { + IN_ReadCursor(&cursorinfo); + LastScan = sc_Escape; + } + + asm pushf + asm cli + if (LastScan == sc_LShift) + LastScan = sc_None; + asm popf + } while (!(scan = LastScan)); + + if (scan != sc_Escape) + { + for (j = 0,on = false;j < 11;j++) + { + if (j == i) + continue; + if (*(KeyMaps[j]) == scan) + { + on = true; + break; + } + } + if (on) + USL_CtlDialog("Key already used","Press a key",nil); + else + *(KeyMaps[i]) = scan; + } + IN_ClearKeysDown(); +} + +#pragma argsused +static boolean +USL_KeySCustom(UserCall call,UserItem far *item) +{ + if (call == uic_SetupCard) + Controls[0] = ctrl_Keyboard; + return(false); +} + +#pragma argsused +static boolean +USL_KeyCustom(UserCall call,UserItem far *item) +{ + boolean result; + word i; + + result = false; + i = (topcard == &keygroup)? (3 + (item - keyi)) : (item - keybi); + switch (call) + { + case uic_SetupCard: + Controls[0] = ctrl_Keyboard; + break; + case uic_Draw: + VWB_Bar(CtlPanelSX + 1,item->y, + CtlPanelEX - CtlPanelSX - 1,8,BackColor); // Clear out background + USL_DrawItemIcon(item); + fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor; + px = item->x + 8; + py = item->y + 1; + USL_DrawString(item->text); + VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor); + VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor); + px = item->x + 90 + 6; + py = item->y + 1; + USL_DrawString(IN_GetScanName(*KeyMaps[i])); + result = true; + break; + case uic_Hit: + USL_KeyCustom(uic_Draw,item); + USL_CKSetKey(item,i); + USL_DrawCtlPanel(); + result = true; + break; + } + return(result); +} + +static void +USL_CJDraw(char *s1,char *s2) +{ + word w,h; + + USL_MeasureString(s1,&w,&h); + px = CtlPanelSX + ((CtlPanelW - w) / 2); + py = CtlPanelEY - 34; + VWB_Bar(CtlPanelSX + 1,py,CtlPanelW - 2,h * 2,BackColor); + fontcolor = HiliteColor; + USL_DrawString(s1); + py += h; + USL_MeasureString(s2,&w,&h); + px = CtlPanelSX + ((CtlPanelW - w) / 2); + USL_DrawString(s2); +} + +static boolean +USL_CJGet(word joy,word button,word x,word y,word *xaxis,word *yaxis) +{ + boolean on; + longword time; + + while (IN_GetJoyButtonsDB(joy)) + if (LastScan == sc_Escape) + return(false); + + on = false; + time = 0; + while (!(IN_GetJoyButtonsDB(joy) & (1 << button))) + { + if (TimeCount >= time) + { + on ^= true; + time = TimeCount + (TickBase / 2); + VWB_DrawTile8(x,y,TileBase + on); + VW_UpdateScreen(); + } + + if (LastScan == sc_Escape) + return(false); + } + IN_GetJoyAbs(joy,xaxis,yaxis); + return(true); +} + +static boolean +USL_ConfigJoystick(word joy) +{ + word x,y, + minx,miny, + maxx,maxy; + + BottomS1 = BottomS2 = ""; + BottomS3 = "Esc to back out"; + USL_DrawCtlPanel(); + x = CtlPanelSX + 60; + y = CtlPanelSY + 19; + VWB_DrawPic(x,y,CP_JOYSTICKPIC); + + USL_CJDraw("Move Joystick to upper left","and press button #1"); + VWB_DrawTile8(x + 24,y + 8,TileBase + 6); + VWB_DrawTile8(x + 8,y + 8,TileBase + 1); + VWB_DrawTile8(x + 8,y + 24,TileBase + 0); + VW_UpdateScreen(); + if (!USL_CJGet(joy,0,x + 8,y + 8,&minx,&miny)) + return(false); + + USL_CJDraw("Move Joystick to lower right","and press button #2"); + VWB_DrawTile8(x + 24,y + 8,TileBase - 25); + VWB_DrawTile8(x + 40,y + 24,TileBase + 7); + VWB_DrawTile8(x + 8,y + 8,TileBase + 0); + VWB_DrawTile8(x + 8,y + 24,TileBase + 1); + VW_UpdateScreen(); + if (!USL_CJGet(joy,1,x + 8,y + 24,&maxx,&maxy)) + return(false); + + while (IN_GetJoyButtonsDB(joy)) + ; + + IN_SetupJoy(joy,minx,maxx,miny,maxy); + return(true); +} + +#pragma argsused +static boolean +USL_Joy1Custom(UserCall call,UserItem far *item) +{ + if (call == uic_SetupCard) + { + if (USL_ConfigJoystick(0)) + { + Controls[0] = ctrl_Joystick1; + USL_CtlDialog("USING JOYSTICK #1","PRESS ANY KEY",nil); + } + return(true); + } + else + return(false); +} + +#pragma argsused +static boolean +USL_Joy2Custom(UserCall call,UserItem far *item) +{ + if (call == uic_SetupCard) + { + if (USL_ConfigJoystick(1)) + { + Controls[0] = ctrl_Joystick2; + USL_CtlDialog("USING JOYSTICK #2","PRESS ANY KEY",nil); + } + return(true); + } + else + return(false); +} + +static void +USL_DrawFileIcon(UserItem far *item) +{ + word color; + + item->y = topcard->y + CtlPanelSY + 12; + item->y += (item - loadsavegamei) * 11; + + fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor; + color = fontcolor ^ BackColor; // Blech! + VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y,color); + VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y + 9,color); + VWB_Vlin(item->y,item->y + 9,item->x,color); + VWB_Vlin(item->y,item->y + 9,item->x + (CtlPanelW - 12),color); +} + +static void +USL_DoLoadGame(UserItem far *item) +{ + char *filename; + word n, + err; + int file; + SaveGame *game; + + if (!USL_ConfirmComm(uc_Loaded)) + return; + + n = item - loadsavegamei; + game = &Games[n]; + + USL_ShowLoadSave("Loading",game->name); + + err = 0; + filename = USL_GiveSaveName(n); + if ((file = open(filename,O_BINARY | O_RDONLY)) != -1) + { + if (read(file,game,sizeof(*game)) == sizeof(*game)) + { + if (USL_LoadGame) + if (!USL_LoadGame(file)) + USL_HandleError(err = errno); + } + else + USL_HandleError(err = errno); + close(file); + } + else + USL_HandleError(err = errno); + if (err) + { + abortgame = true; + Communication = uc_None; + CtlPanelDone = false; + } + else + loadedgame = true; + game->present = true; + + if (loadedgame) + Paused = true; + + USL_DrawCtlPanel(); +} + +static boolean +USL_LoadCustom(UserCall call,UserItem far *item) +{ + boolean result; + word i; + + result = false; + switch (call) + { + case uic_SetupCard: + for (i = 0;i < MaxSaveGames;i++) + { + if (Games[i].present) + loadsavegamei[i].flags &= ~ui_Disabled; + else + loadsavegamei[i].flags |= ui_Disabled; + } + break; + case uic_DrawIcon: + USL_DrawFileIcon(item); + result = true; + break; + case uic_Draw: + USL_DrawFileIcon(item); + VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor); + + i = item - loadsavegamei; + if (Games[i].present) + px = item->x + 2; + else + px = item->x + 60; + py = item->y + 2; + USL_DrawString(Games[i].present? Games[i].name : "Empty"); + result = true; + break; + case uic_Hit: + USL_DoLoadGame(item); + result = true; + break; + } + return(result); +} + +static void +USL_DoSaveGame(UserItem far *item) +{ + boolean ok; + char *filename; + word n,err; + int file; + SaveGame *game; + + BottomS1 = "Type name"; + BottomS2 = "Enter accepts"; + USL_DrawCtlPanel(); + + n = item - loadsavegamei; + game = &Games[n]; + fontcolor = HiliteColor; + VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor); + game->oldtest = &PrintX; + ok = US_LineInput(item->x + 2,item->y + 2, + game->name,game->present? game->name : nil, + true,MaxGameName, + CtlPanelW - 22); + if (!strlen(game->name)) + strcpy(game->name,"Untitled"); + if (ok) + { + USL_ShowLoadSave("Saving",game->name); + + filename = USL_GiveSaveName(n); + err = 0; + file = open(filename,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + if (file != -1) + { + if (write(file,game,sizeof(*game)) == sizeof(*game)) + { + if (USL_SaveGame) + ok = USL_SaveGame(file); + if (!ok) + USL_HandleError(err = errno); + } + else + USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno)); + close(file); + } + else + USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno)); + if (err) + { + remove(filename); + ok = false; + } + + } + + if (!game->present) + game->present = ok; + + if (ok) + GameIsDirty = false; + USL_SetupCard(); +} + +static boolean +USL_SaveCustom(UserCall call,UserItem far *item) +{ + word i; + + switch (call) + { + case uic_SetupCard: + for (i = 0;i < MaxSaveGames;i++) + loadsavegamei[i].flags &= ~ui_Disabled; + return(false); + case uic_Hit: + USL_DoSaveGame(item); + return(true); +// break; + } + return(USL_LoadCustom(call,item)); +} + +#define PaddleMinX (CtlPanelSX + 3) +#define PaddleMaxX (CtlPanelEX - 15) +#define BallMinX (CtlPanelSX + 2) +#define BallMinY (CtlPanelSY + 12 + 2) +#define BallMaxX (CtlPanelEX - 6) +#define BallMaxY (CtlPanelEY - 13) +#define CPaddleY (BallMinY + 4) +#define KPaddleY (BallMaxY - 2) +void +USL_DrawPongScore(word k,word c) +{ + fontcolor = HiliteColor; + PrintY = py = CtlPanelSY + 4; + px = CtlPanelSX + 6; + VWB_Bar(px,py,42,6,BackColor); + USL_DrawString("YOU:"); + PrintX = px; + US_PrintUnsigned(k); + px = CtlPanelSX + 108; + VWB_Bar(px,py,50,6,BackColor); + USL_DrawString("COMP:"); + PrintX = px; + US_PrintUnsigned(c); +} + +void +USL_PlayPong(void) +{ + boolean ball,killball,revdir,done,lastscore; + word cycle, + x,y, + kx,cx, + rx, + bx,by, + kscore,cscore, + speedup; + int bdx,bdy; + longword balltime,waittime; + CursorInfo cursorinfo; + + kx = cx = PaddleMinX + ((PaddleMaxX - PaddleMinX) / 2); + bx = by = bdx = bdy = 0; + kscore = cscore = 0; + USL_DrawPongScore(0,0); + cycle = 0; + revdir = false; + killball = true; + done = false; + lastscore = false; + do + { + waittime = TimeCount; + + IN_ReadCursor(&cursorinfo); + if (((cursorinfo.x < 0) || IN_KeyDown(sc_LeftArrow)) && (kx > PaddleMinX)) + kx -= 2; + else if (((cursorinfo.x > 0) || IN_KeyDown(sc_RightArrow)) && (kx < PaddleMaxX)) + kx += 2; + + if (killball) + { + ball = false; + balltime = TimeCount + TickBase; + speedup = 10; + killball = false; + } + + if (ball && (cycle++ % 3)) + { + x = (bx >> 2); + if (!(x & 1)) + x += (US_RndT() & 1); + + if ((cx + 6 < x) && (cx < PaddleMaxX)) + cx += 1; + else if ((cx + 6 > x) && (cx > PaddleMinX)) + cx -= 1; + } + + VWB_Bar(BallMinX,BallMinY - 1, + BallMaxX - BallMinX + 5,BallMaxY - BallMinY + 7, + BackColor); + VWB_DrawSprite(cx,CPaddleY,PADDLESPR); + VWB_DrawSprite(kx,KPaddleY,PADDLESPR); + if (ball) + { + if + ( + (((bx + bdx) >> 2) > BallMaxX) + || (((bx + bdx) >> 2) < BallMinX) + ) + { + SD_PlaySound(BALLBOUNCESND); + bdx = -bdx; + } + bx += bdx; + + if (((by + bdy) >> 2) > BallMaxY) + { + killball = true; + lastscore = false; + cscore++; + SD_PlaySound(COMPSCOREDSND); + USL_DrawPongScore(kscore,cscore); + if (cscore == 21) + { + USL_CtlDialog("You lost!","Press any key",nil); + done = true; + continue; + } + } + else if (((by + bdy) >> 2) < BallMinY) + { + killball = true; + lastscore = true; + kscore++; + SD_PlaySound(KEENSCOREDSND); + USL_DrawPongScore(kscore,cscore); + if (kscore == 21) + { + USL_CtlDialog("You won!","Press any key",nil); + done = true; + continue; + } + } + by += bdy; + + x = bx >> 2; + y = by >> 2; + if (!killball) + { + if + ( + (bdy < 0) + && ((y >= CPaddleY) && (y < CPaddleY + 3)) + && ((x >= (cx - 5)) && (x < (cx + 11))) + ) + { + rx = cx; + revdir = true; + SD_PlaySound(COMPPADDLESND); + } + else if + ( + (bdy > 0) + && ((y >= (KPaddleY - 3)) && (y < KPaddleY)) + && ((x >= (kx - 5)) && (x < (kx + 11))) + ) + { + if (((bdy >> 2) < 3) && !(--speedup)) + { + bdy++; + speedup = 10; + } + rx = kx; + revdir = true; + SD_PlaySound(KEENPADDLESND); + } + if (revdir) + { + bdy = -bdy; + bdx = ((x + 5 - rx) >> 1) - (1 << 2); + if (!bdx) + bdx--; + revdir = false; + } + } + VWB_DrawSprite(x,y,(x & 1)? BALL1PIXELTOTHERIGHTSPR : BALLSPR); + } + else if (TimeCount >= balltime) + { + ball = true; + bdx = 1 - (US_RndT() % 3); + bdy = 2; + if (lastscore) + bdy = -bdy; + bx = (BallMinX + ((BallMaxX - BallMinX) / 2)) << 2; + by = (BallMinY + ((BallMaxY - BallMinY) / 2)) << 2; + } + VW_UpdateScreen(); + while (waittime == TimeCount) + ; // DEBUG - do adaptiveness + } while ((LastScan != sc_Escape) && !done); + IN_ClearKeysDown(); +} + +#pragma argsused +static boolean +USL_PongCustom(UserCall call,struct UserItem far *item) +{ + if (call != uic_SetupCard) + return(false); + + VWB_DrawPic(0,0,CP_MENUSCREENPIC); + VWB_DrawPic(CtlPanelSX + 56,CtlPanelSY,CP_PADDLEWARPIC); + VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelSY + 12,HiliteColor ^ BackColor); + VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelEY - 7,HiliteColor ^ BackColor); + USL_PlayPong(); + + return(true); +} + +// Flag management stuff +static void +USL_ClearFlags(UserItemGroup far *node) +{ + UserItem far *i; + + if (!node->items) + return; + + for (i = node->items;i->type != uii_Bad;i++) + { + i->flags &= ~UISelectFlags; + if (i->child) + USL_ClearFlags((UserItemGroup far *)i->child); + } +} + +static int +USL_FindPushedItem(UserItemGroup far *group) +{ + word i; + UserItem far *item; + + for (item = group->items,i = 0;item->type != uii_Bad;item++,i++) + if (item->flags & ui_Pushed) + return(i); + return(-1); +} + +static void +USL_SelectItem(UserItemGroup far *group,word index,boolean draw) +{ + UserItem far *item; + + if (index != group->cursor) + { + item = &group->items[group->cursor]; + item->flags &= ~ui_Selected; + if (draw) + USL_DrawItem(item); + } + + group->cursor = index; + item = &group->items[group->cursor]; + group->items[group->cursor].flags |= ui_Selected; + if (draw) + USL_DrawItem(item); +} + +static void +USL_PushItem(UserItemGroup far *group,word index,boolean draw) +{ + word i; + UserItem far *item; + + USL_SelectItem(group,index,draw); + for (item = group->items,i = 0;item->type != uii_Bad;item++,i++) + { + if (item->type != uii_RadioButton) + continue; + + if (i == index) + { + item->flags |= ui_Pushed; + if (draw) + USL_DrawItem(item); + } + else if (item->flags & ui_Pushed) + { + item->flags &= ~ui_Pushed; + if (draw) + USL_DrawItem(item); + } + } +} + +static void +USL_NextItem(void) +{ + if (topcard->items[topcard->cursor + 1].type == uii_Bad) + return; + USL_SelectItem(topcard,topcard->cursor + 1,true); +} + +static void +USL_PrevItem(void) +{ + if (!topcard->cursor) + return; + USL_SelectItem(topcard,topcard->cursor - 1,true); +} + +static void +USL_SetupCard(void) +{ + BottomS1 = "Arrows move"; + BottomS2 = "Enter selects"; + BottomS3 = cstackptr? "ESC to back out" : "ESC to quit"; + + USL_SelectItem(topcard,topcard->cursor,false); + USL_DrawCtlPanel(); // Contents? +} + +static void +USL_DownLevel(UserItemGroup far *group) +{ + if (!group) + Quit("USL_DownLevel() - nil card"); + USL_PushCard(group); + if (group->custom && group->custom(uic_SetupCard,nil)) + USL_PopCard(); + USL_SetupCard(); +} + +static void +USL_UpLevel(void) +{ + if (!cstackptr) + { + USL_ConfirmComm(uc_Quit); + return; + } + + if (topcard->items) + topcard->items[topcard->cursor].flags &= ~ui_Selected; + USL_PopCard(); + USL_SetupCard(); +} + +static void +USL_DoItem(void) +{ + // DEBUG - finish this routine + UserItem far *item; + + item = &topcard->items[topcard->cursor]; + if (item->flags & ui_Disabled) + SD_PlaySound(NOWAYSND); + else + { + switch (item->type) + { + case uii_Button: + if (!(topcard->custom && topcard->custom(uic_Hit,item))) + USL_ConfirmComm(item->comm); + break; + case uii_RadioButton: + USL_PushItem(topcard,topcard->cursor,true); + break; + case uii_Folder: + USL_DownLevel(item->child); + break; + } + } +} + +static void +USL_SetControlValues(void) +{ + USL_PushItem(&soundgroup,SoundMode,false); + USL_PushItem(&musicgroup,MusicMode,false); + if (!AdLibPresent) + { + soundi[2].flags |= ui_Disabled; // AdLib sound effects + musici[1].flags |= ui_Disabled; // AdLib music + } + + if (!JoysPresent[0]) + configi[3].flags |= ui_Disabled; + if (!JoysPresent[1]) + configi[4].flags |= ui_Disabled; + + rooti[4].text = ingame? "RETURN TO GAME" : "RETURN TO DEMO"; + if (!ingame) + { + rooti[2].flags |= ui_Disabled; // Save Game + rooti[5].flags |= ui_Disabled; // End Game + } + rootgroup.cursor = ingame? 4 : 0; + USL_SetOptionsText(); + // DEBUG - write the rest of this +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the +// values of all the appropriate variables +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_SetUpCtlPanel(void) +{ + int i; + + // Cache in all of the stuff for the control panel + CA_UpLevel(); + for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++) + CA_MarkGrChunk(i); + for (i = PADDLE_LUMP_START;i <= PADDLE_LUMP_END;i++) + CA_MarkGrChunk(i); + CA_MarkGrChunk(STARTFONT+1); // Little font + CA_MarkGrChunk(CP_MENUMASKPICM); // Mask for dialogs + CA_CacheMarks("Control Panel"); + CA_LoadAllSounds(); + + // Do some other setup + fontnumber = 1; + US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString); + fontcolor = F_BLACK; + VW_Bar (0,0,320,200,3); // CAT3D patch + RF_FixOfs(); + VW_InitDoubleBuffer(); + + Communication = uc_None; + USL_ClearFlags(&rootgroup); + USL_SetControlValues(); + USL_SetupStack(); + USL_SetupCard(); + + if (ingame) + GameIsDirty = true; + + IN_ClearKeysDown(); +} + +static void +USL_HandleComm(UComm comm) +{ + switch (comm) + { + case uc_Loaded: + case uc_Return: + break; + case uc_Abort: + abortgame = true; + break; + case uc_Quit: + QuitToDos = true; + break; + case uc_SEasy: + restartgame = gd_Easy; + break; + case uc_SNormal: + restartgame = gd_Normal; + break; + case uc_SHard: + restartgame = gd_Hard; + break; + + default: + Quit("USL_HandleComm() - unknown"); + break; + } +} + +static void +USL_GetControlValues(void) +{ + int i; + + // DEBUG - write the rest of this + i = USL_FindPushedItem(&soundgroup); + if (i != SoundMode) + SD_SetSoundMode(i); + + i = USL_FindPushedItem(&musicgroup); + if (i != MusicMode) + SD_SetMusicMode(i); +} + +/////////////////////////////////////////////////////////////////////////// +// +// USL_TearDownCtlPanel() - Given the state of the control panel, sets the +// modes and values as appropriate +// +/////////////////////////////////////////////////////////////////////////// +static void +USL_TearDownCtlPanel(void) +{ + USL_GetControlValues(); + if (Communication) + USL_HandleComm(Communication); + + fontnumber = 0; // Normal font + fontcolor = F_BLACK; + if (restartgame && USL_ResetGame) + USL_ResetGame(); + else if (QuitToDos) + { + if (tedlevel) + TEDDeath(); + else + { + US_CenterWindow(20,3); + fontcolor = F_SECONDCOLOR; + US_PrintCentered("Quitting..."); + fontcolor = F_BLACK; + VW_UpdateScreen(); + Quit(nil); + } + } + + IN_ClearKeysDown(); + SD_WaitSoundDone(); + VW_Bar (0,0,320,200,3); // CAT3D patch + CA_DownLevel(); + CA_LoadAllSounds(); +} + +/////////////////////////////////////////////////////////////////////////// +// +// US_ControlPanel() - This is the main routine for the control panel +// +/////////////////////////////////////////////////////////////////////////// +#define MoveMin 40 +void +US_ControlPanel(void) +{ +extern void HelpScreens(void); + + boolean resetitem,on; + word i; + int ydelta; + longword flashtime; + UserItem far *item; + CursorInfo cursorinfo; + +#if 0 + // DEBUG!!! + { + USL_SetUpCtlPanel(); + Communication = uc_Loaded; + CtlPanelDone = true; + loadedgame = true; + USL_TearDownCtlPanel(); + return; + } +#endif + + if ((LastScan < sc_F1) || (LastScan > sc_F10)) + IN_ClearKeysDown(); + + USL_SetUpCtlPanel(); + USL_DrawCtlPanel(); + + ydelta = 0; + for (CtlPanelDone = false,resetitem = on = true;!CtlPanelDone;) + { + item = &(topcard->items[topcard->cursor]); + + if (resetitem) + { + flashtime = TimeCount + (TickBase / 2); + resetitem = false; + } + + if (TimeCount >= flashtime) + { + on ^= true; + resetitem = true; + if (!on) + item->flags &= ~ui_Selected; + USL_DrawItemIcon(item); + item->flags |= ui_Selected; + } + + VW_UpdateScreen(); + + if (LastScan) + { + switch (LastScan) + { + case sc_UpArrow: + USL_PrevItem(); + resetitem = true; + break; + case sc_DownArrow: + USL_NextItem(); + resetitem = true; + break; + case sc_Return: + USL_DoItem(); + resetitem = true; + break; + case sc_Escape: + USL_UpLevel(); + resetitem = true; + break; +#ifndef KEEN6 + case sc_F1: + HelpScreens(); + USL_DrawCtlPanel(); + resetitem = true; + break; +#endif + } + + if + ( + (!resetitem) + && ( + (LastScan == KbdDefs[0].button0) + || (LastScan == KbdDefs[0].button1) + ) + ) + { + USL_DoItem(); + resetitem = true; + } + + if (!resetitem) + { + for (item = topcard->items,i = 0;item->type != uii_Bad;item++,i++) + { + if (item->hotkey == LastScan) + { + USL_SelectItem(topcard,i,true); + resetitem = true; + break; + } + } + } + + IN_ClearKeysDown(); + } + else + { + IN_ReadCursor(&cursorinfo); + ydelta += cursorinfo.y; + if (cursorinfo.button0) + { + do + { + IN_ReadCursor(&cursorinfo); + } while (cursorinfo.button0); + USL_DoItem(); + resetitem = true; + } + else if (cursorinfo.button1) + { + do + { + IN_ReadCursor(&cursorinfo); + } while (cursorinfo.button1); + USL_UpLevel(); + resetitem = true; + } + else if (ydelta < -MoveMin) + { + ydelta += MoveMin; + USL_PrevItem(); + resetitem = true; + } + else if (ydelta > MoveMin) + { + ydelta -= MoveMin; + USL_NextItem(); + resetitem = true; + } + } + } + + USL_TearDownCtlPanel(); +} diff --git a/ID_US_A.ASM b/ID_US_A.ASM new file mode 100644 index 0000000..15e6f12 --- /dev/null +++ b/ID_US_A.ASM @@ -0,0 +1,117 @@ +; 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. + +IDEAL +MODEL MEDIUM,C + +; Assembly portion of the User Mgr. This is just John Carmack's table +; driven pseudo-random number generator, and we put it in the User Mgr +; because we couldn't figure out where it should go + + +;============================================================================ +; +; RANDOM ROUTINES +; +;============================================================================ + + FARDATA + +rndindex dw ? + +rndtable db 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 + db 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 + db 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 + db 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 + db 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 + db 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 + db 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 + db 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 + db 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 + db 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 + db 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 + db 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 + db 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 + db 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 + db 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 + db 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 + db 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 + db 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 + db 120, 163, 236, 249 + + + CODESEG + +LastRnd dw ? + +;================================================= +; +; void US_InitRndT (boolean randomize) +; Init table based RND generator +; if randomize is false, the counter is set to 0 +; +;================================================= + +PROC US_InitRndT randomize:word + uses si,di + public US_InitRndT + + mov ax,SEG rndtable + mov es,ax + mov ax,[randomize] + or ax,ax + jne @@timeit ;if randomize is true, really random + + mov dx,0 ;set to a definite value + jmp @@setit + +@@timeit: + mov ah,2ch + int 21h ;GetSystemTime + and dx,0ffh + +@@setit: + mov [es:rndindex],dx + ret + +ENDP + +;================================================= +; +; int US_RndT (void) +; Return a random # between 0-255 +; Exit : AX = value +; +;================================================= +PROC US_RndT + public US_RndT + + mov ax,SEG rndtable + mov es,ax + mov bx,[es:rndindex] + inc bx + and bx,0ffh + mov [es:rndindex],bx + mov al,[es:rndtable+BX] + xor ah,ah + + ret + +ENDP + +END + diff --git a/ID_VW.C b/ID_VW.C new file mode 100644 index 0000000..625f08d --- /dev/null +++ b/ID_VW.C @@ -0,0 +1,1541 @@ +/* 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_VW.C + +#include "ID_HEADS.H" + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define VIEWWIDTH 40 + +#define PIXTOBLOCK 4 // 16 pixels to an update block + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +cardtype videocard; // set by VW_Startup +grtype grmode; // CGAgr, EGAgr, VGAgr + +unsigned bufferofs; // hidden area to draw to before displaying +unsigned displayofs; // origin of the visable screen +unsigned panx,pany; // panning adjustments inside port in pixels +unsigned pansx,pansy; // panning adjustments inside port in screen + // block limited pixel values (ie 0/8 for ega x) +unsigned panadjust; // panx/pany adjusted by screen resolution + +unsigned screenseg; // normally 0xa000 / 0xb800 +unsigned linewidth; +unsigned ylookup[VIRTUALHEIGHT]; + +unsigned fontnumber; // 0 based font number for drawing + +boolean screenfaded; + +pictabletype _seg *pictable; +pictabletype _seg *picmtable; +spritetabletype _seg *spritetable; + +int bordercolor; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +void VWL_MeasureString (char far *string, word *width, word *height, + fontstruct _seg *font); +void VWL_DrawCursor (void); +void VWL_EraseCursor (void); +void VWL_DBSetup (void); +void VWL_UpdateScreenBlocks (void); + + +int bordercolor; +int cursorvisible; +int cursornumber,cursorwidth,cursorheight,cursorx,cursory; +memptr cursorsave; +unsigned cursorspot; + +//=========================================================================== + + +/* +======================= += += VW_Startup += +======================= +*/ + +static char *ParmStrings[] = {"HIDDENCARD",""}; + +void VW_Startup (void) +{ + int i; + + asm cld; + + videocard = 0; + + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],ParmStrings) == 0) + { + videocard = EGAcard; + break; + } + + if (!videocard) + videocard = VW_VideoID (); + +#if GRMODE == EGAGR + grmode = EGAGR; + if (videocard != EGAcard && videocard != VGAcard) +Quit ("Improper video card! If you really have an EGA/VGA card that I am not \n" + "detecting, use the -HIDDENCARD command line parameter!"); + EGAWRITEMODE(0); +#endif + +#if GRMODE == CGAGR + grmode = CGAGR; + if (videocard < CGAcard || videocard > VGAcard) +Quit ("Improper video card! If you really have a CGA card that I am not \n" + "detecting, use the -HIDDENCARD command line parameter!"); + MM_GetPtr (&(memptr)screenseg,0x10000l); // grab 64k for floating screen +#endif + + cursorvisible = 0; +} + +//=========================================================================== + +/* +======================= += += VW_Shutdown += +======================= +*/ + +void VW_Shutdown (void) +{ + VW_SetScreenMode (TEXTGR); +#if GRMODE == EGAGR + VW_SetLineWidth (80); +#endif +} + +//=========================================================================== + +/* +======================== += += VW_SetScreenMode += Call BIOS to set TEXT / CGAgr / EGAgr / VGAgr += +======================== +*/ + +void VW_SetScreenMode (int grmode) +{ + switch (grmode) + { + case TEXTGR: _AX = 3; + geninterrupt (0x10); + screenseg=0xb000; + break; + case CGAGR: _AX = 4; + geninterrupt (0x10); // screenseg is actually a main mem buffer + break; + case EGAGR: _AX = 0xd; + geninterrupt (0x10); + screenseg=0xa000; + break; +#ifdef VGAGAME + case VGAGR:{ + char extern VGAPAL; // deluxepaint vga pallet .OBJ file + void far *vgapal = &VGAPAL; + SetCool256 (); // custom 256 color mode + screenseg=0xa000; + _ES = FP_SEG(vgapal); + _DX = FP_OFF(vgapal); + _BX = 0; + _CX = 0x100; + _AX = 0x1012; + geninterrupt(0x10); // set the deluxepaint pallet + + break; +#endif + } + VW_SetLineWidth(SCREENWIDTH); +} + +/* +============================================================================= + + SCREEN FADES + +============================================================================= +*/ + +char colors[7][17]= +{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,0}, + {0,0,0,0,0,0,0,0,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0}, + {0,1,2,3,4,5,6,7,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0}, + {0,1,2,3,4,5,6,7,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0}, + {0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f}}; + + +void VW_ColorBorder (int color) +{ + _AH=0x10; + _AL=1; + _BH=color; + geninterrupt (0x10); + bordercolor = color; +} + +void VW_SetPalette(byte *palette) +{ + byte p; + word i; + + for (i = 0;i < 15;i++) + { + p = palette[i]; + colors[0][i] = 0; + colors[1][i] = (p > 0x10)? (p & 0x0f) : 0; + colors[2][i] = (p > 0x10)? p : 0; + colors[3][i] = p; + colors[4][i] = (p > 0x10)? 0x1f : p; + colors[5][i] = 0x1f; + } +} + +void VW_SetDefaultColors(void) +{ +#if GRMODE == EGAGR + colors[3][16] = bordercolor; + _ES=FP_SEG(&colors[3]); + _DX=FP_OFF(&colors[3]); + _AX=0x1002; + geninterrupt(0x10); + screenfaded = false; +#endif +} + + +void VW_FadeOut(void) +{ +#if GRMODE == EGAGR + int i; + + for (i=3;i>=0;i--) + { + colors[i][16] = bordercolor; + _ES=FP_SEG(&colors[i]); + _DX=FP_OFF(&colors[i]); + _AX=0x1002; + geninterrupt(0x10); + VW_WaitVBL(6); + } + screenfaded = true; +#endif +} + + +void VW_FadeIn(void) +{ +#if GRMODE == EGAGR + int i; + + for (i=0;i<4;i++) + { + colors[i][16] = bordercolor; + _ES=FP_SEG(&colors[i]); + _DX=FP_OFF(&colors[i]); + _AX=0x1002; + geninterrupt(0x10); + VW_WaitVBL(6); + } + screenfaded = false; +#endif +} + +void VW_FadeUp(void) +{ +#if GRMODE == EGAGR + int i; + + for (i=3;i<6;i++) + { + colors[i][16] = bordercolor; + _ES=FP_SEG(&colors[i]); + _DX=FP_OFF(&colors[i]); + _AX=0x1002; + geninterrupt(0x10); + VW_WaitVBL(6); + } + screenfaded = true; +#endif +} + +void VW_FadeDown(void) +{ +#if GRMODE == EGAGR + int i; + + for (i=5;i>2;i--) + { + colors[i][16] = bordercolor; + _ES=FP_SEG(&colors[i]); + _DX=FP_OFF(&colors[i]); + _AX=0x1002; + geninterrupt(0x10); + VW_WaitVBL(6); + } + screenfaded = false; +#endif +} + + +/* +======================== += += VW_SetAtrReg += += Sets an attribute (pallete / border) register += Does NOT vsync! += +======================== +*/ + +void VW_SetAtrReg (int reg, int value) +{ + asm cli + asm mov dx,STATUS_REGISTER_1 + asm in al,dx + asm mov dx,ATR_INDEX + + asm mov al,BYTE PTR [reg] + asm out dx,al + asm mov al,BYTE PTR [value] + asm out dx,al + asm mov dx,0x3da + asm in al,dx + asm mov dx,ATR_INDEX + asm mov al,0x20 + asm out dx,al + asm sti +} + + + +//=========================================================================== + +/* +==================== += += VW_SetLineWidth += += Must be an even number of bytes += +==================== +*/ + +void VW_SetLineWidth (int width) +{ + int i,offset; + +#if GRMODE == EGAGR +// +// set wide virtual screen +// +asm mov dx,CRTC_INDEX +asm mov al,CRTC_OFFSET +asm mov ah,[BYTE PTR width] +asm shr ah,1 +asm out dx,ax +#endif + +// +// set up lookup tables +// + linewidth = width; + + offset = 0; + + for (i=0;i0 + +/* +==================== += += VW_DrawPic += += X in bytes, y in pixels, chunknum is the #defined picnum += +==================== +*/ + +void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum) +{ + int picnum = chunknum - STARTPICS; + memptr source; + unsigned dest,width,height; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = pictable[picnum].width; + height = pictable[picnum].height; + + VW_MemToScreen(source,dest,width,height); +} + + +#endif + +#if NUMPICM>0 + +/* +==================== += += VW_DrawMPic += += X in bytes, y in pixels, chunknum is the #defined picnum += +==================== +*/ + +void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum) +{ + int picnum = chunknum - STARTPICM; + memptr source; + unsigned dest,width,height; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = picmtable[picnum].width; + height = picmtable[picnum].height; + + VW_MaskBlock(source,0,dest,width,height,width*height); +} + +void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum) +{ + int picnum = chunknum - STARTPICM; + memptr source; + unsigned dest,width,ofs,plane; + int height; + + source = grsegs[chunknum]; + width = picmtable[picnum].width; + height = picmtable[picnum].height; + plane = width*height; + + ofs = 0; + if (y<0) + { + ofs= -y*width; + height+=y; + y=0; + } + else if (y+height>216) + { + height-=(y-216); + } + dest = ylookup[y]+x+bufferofs; + if (height<1) + return; + + VW_MaskBlock(source,ofs,dest,width,height,plane); +} + + +#endif + +//=========================================================================== + +#if NUMSPRITES>0 + +/* +==================== += += VW_DrawSprite += += X and Y in pixels, it will match the closest shift possible += += To do: += Add vertical clipping! += Make the shifts act as center points, rather than break points += +==================== +*/ + +void VW_DrawSprite(int x, int y, unsigned chunknum) +{ + spritetabletype far *spr; + spritetype _seg *block; + unsigned dest,shift; + + spr = &spritetable[chunknum-STARTSPRITES]; + block = (spritetype _seg *)grsegs[chunknum]; + + y+=spr->orgy>>G_P_SHIFT; + x+=spr->orgx>>G_P_SHIFT; + +#if GRMODE == EGAGR + shift = (x&7)/2; +#endif +#if GRMODE == CGAGR + shift = 0; +#endif + + dest = bufferofs + ylookup[y]; + if (x>=0) + dest += x/SCREENXDIV; + else + dest += (x+1)/SCREENXDIV; + + VW_MaskBlock (block,block->sourceoffset[shift],dest, + block->width[shift],spr->height,block->planesize[shift]); +} + +#endif + + +/* +================== += += VW_Hlin += +================== +*/ + + +#if GRMODE == EGAGR + +unsigned char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1}; +unsigned char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color) +{ + unsigned dest,xlb,xhb,maskleft,maskright,mid; + + xlb=xl/8; + xhb=xh/8; + + EGAWRITEMODE(2); + EGAMAPMASK(15); + + maskleft = leftmask[xl&7]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + + asm mov dx,GC_INDEX + asm mov al,GC_BITMASK + asm mov ah,[BYTE PTR maskleft] + asm out dx,ax // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + goto done; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,GC_BITMASK +asm mov ah,[BYTE PTR maskleft] +asm out dx,ax // mask off pixels + +asm mov al,bh +asm mov bl,[es:di] // load latches +asm stosb + +// +// draw middle +// +asm mov ax,GC_BITMASK + 255*256 +asm out dx,ax // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,GC_BITMASK +asm mov ah,[BYTE PTR maskright] +asm out dx,ax // mask off pixels + +asm xchg bh,[es:di] // load latches and write pixels + +done: + EGABITMASK(255); + EGAWRITEMODE(0); +} +#endif + + +#if GRMODE == CGAGR + +unsigned char pixmask[4] = {0xc0,0x30,0x0c,0x03}; +unsigned char leftmask[4] = {0xff,0x3f,0x0f,0x03}; +unsigned char rightmask[4] = {0xc0,0xf0,0xfc,0xff}; +unsigned char colorbyte[4] = {0,0x55,0xaa,0xff}; + +// +// could be optimized for rep stosw +// +void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color) +{ + unsigned dest,xlb,xhb,mid; + byte maskleft,maskright; + + color = colorbyte[color]; // expand 2 color bits to 8 + + xlb=xl/4; + xhb=xh/4; + + maskleft = leftmask[xl&3]; + maskright = rightmask[xh&3]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; +asm mov es,[screenseg] + + if (xlb==xhb) + { + // + // entire line is in one byte + // + maskleft&=maskright; + + asm mov ah,[maskleft] + asm mov bl,[BYTE PTR color] + asm and bl,[maskleft] + asm not ah + + asm mov di,[dest] + + asm mov al,[es:di] + asm and al,ah // mask out pixels + asm or al,bl // or in color + asm mov [es:di],al + return; + } + +asm mov di,[dest] +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov ah,[maskleft] +asm mov bl,bh +asm and bl,[maskleft] +asm not ah +asm mov al,[es:di] +asm and al,ah // mask out pixels +asm or al,bl // or in color +asm stosb + +// +// draw middle +// +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov ah,[maskright] +asm mov bl,bh +asm and bl,[maskright] +asm not ah +asm mov al,[es:di] +asm and al,ah // mask out pixels +asm or al,bl // or in color +asm stosb +} +#endif + + +/* +================== += += VW_Bar += += Pixel addressable block fill routine += +================== +*/ + +#if GRMODE == CGAGR + +void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height, + unsigned color) +{ + unsigned xh = x+width-1; + + while (height--) + VW_Hlin (x,xh,y++,color); +} + +#endif + + +#if GRMODE == EGAGR + +void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height, + unsigned color) +{ + unsigned dest,xh,xlb,xhb,maskleft,maskright,mid; + + xh = x+width-1; + xlb=x/8; + xhb=xh/8; + + EGAWRITEMODE(2); + EGAMAPMASK(15); + + maskleft = leftmask[x&7]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + + asm mov dx,GC_INDEX + asm mov al,GC_BITMASK + asm mov ah,[BYTE PTR maskleft] + asm out dx,ax // mask off pixels + + asm mov ah,[BYTE PTR color] + asm mov dx,[linewidth] +yloop1: + asm mov al,ah + asm xchg al,[es:di] // load latches and write pixels + asm add di,dx // down to next line + asm dec [height] + asm jnz yloop1 + + goto done; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov bh,[BYTE PTR color] +asm mov dx,GC_INDEX +asm mov si,[linewidth] +asm sub si,[mid] // add to di at end of line to get to next scan +asm dec si + +// +// draw left side +// +yloop2: +asm mov al,GC_BITMASK +asm mov ah,[BYTE PTR maskleft] +asm out dx,ax // mask off pixels + +asm mov al,bh +asm mov bl,[es:di] // load latches +asm stosb + +// +// draw middle +// +asm mov ax,GC_BITMASK + 255*256 +asm out dx,ax // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,GC_BITMASK +asm mov ah,[BYTE PTR maskright] +asm out dx,ax // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels + +asm add di,si // move to start of next line +asm dec [height] +asm jnz yloop2 + +done: + EGABITMASK(255); + EGAWRITEMODE(0); +} + +#endif + +//========================================================================== + +/* +================== += += VW_MeasureString += +================== +*/ + +#if NUMFONT+NUMFONTM>0 +void +VWL_MeasureString (char far *string, word *width, word *height, fontstruct _seg *font) +{ + *height = font->height; + for (*width = 0;*string;string++) + *width += font->width[*((byte far *)string)]; // proportional width +} + +void VW_MeasurePropString (char far *string, word *width, word *height) +{ + VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONT+fontnumber]); +} + +void VW_MeasureMPropString (char far *string, word *width, word *height) +{ + VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONTM+fontnumber]); +} + + +#endif + + +/* +============================================================================= + + CGA stuff + +============================================================================= +*/ + +#if GRMODE == CGAGR + +#define CGACRTCWIDTH 40 + +/* +========================== += += VW_CGAFullUpdate += +========================== +*/ + +void VW_CGAFullUpdate (void) +{ + byte *update; + boolean halftile; + unsigned x,y,middlerows,middlecollumns; + + displayofs = bufferofs+panadjust; + +asm mov ax,0xb800 +asm mov es,ax + +asm mov si,[displayofs] +asm xor di,di + +asm mov bx,100 // pairs of scan lines to copy +asm mov dx,[linewidth] +asm sub dx,80 + +asm mov ds,[screenseg] +asm test si,1 +asm jz evenblock + +// +// odd source +// +asm mov ax,39 // words accross screen +copytwolineso: +asm movsb +asm mov cx,ax +asm rep movsw +asm movsb +asm add si,dx +asm add di,0x2000-80 // go to the interlaced bank +asm movsb +asm mov cx,ax +asm rep movsw +asm movsb +asm add si,dx +asm sub di,0x2000 // go to the non interlaced bank + +asm dec bx +asm jnz copytwolineso +asm jmp blitdone + +// +// even source +// +evenblock: +asm mov ax,40 // words accross screen +copytwolines: +asm mov cx,ax +asm rep movsw +asm add si,dx +asm add di,0x2000-80 // go to the interlaced bank +asm mov cx,ax +asm rep movsw +asm add si,dx +asm sub di,0x2000 // go to the non interlaced bank + +asm dec bx +asm jnz copytwolines + +blitdone: +asm mov ax,ss +asm mov ds,ax +asm mov es,ax + +asm xor ax,ax // clear out the update matrix +asm mov cx,UPDATEWIDE*UPDATEHIGH/2 + +asm mov di,[baseupdateptr] +asm rep stosw + + updateptr = baseupdateptr; + *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE; +} + + +#endif + +/* +============================================================================= + + CURSOR ROUTINES + +These only work in the context of the double buffered update routines + +============================================================================= +*/ + +/* +==================== += += VWL_DrawCursor += += Background saves, then draws the cursor at cursorspot += +==================== +*/ + +void VWL_DrawCursor (void) +{ + cursorspot = bufferofs + ylookup[cursory+pansy]+(cursorx+pansx)/SCREENXDIV; + VW_ScreenToMem(cursorspot,cursorsave,cursorwidth,cursorheight); + VWB_DrawSprite(cursorx,cursory,cursornumber); +} + + +//========================================================================== + + +/* +==================== += += VWL_EraseCursor += +==================== +*/ + +void VWL_EraseCursor (void) +{ + VW_MemToScreen(cursorsave,cursorspot,cursorwidth,cursorheight); + VW_MarkUpdateBlock ((cursorx+pansx)&SCREENXMASK,cursory+pansy, + ( (cursorx+pansx)&SCREENXMASK)+cursorwidth*SCREENXDIV-1, + cursory+pansy+cursorheight-1); +} + + +//========================================================================== + + +/* +==================== += += VW_ShowCursor += +==================== +*/ + +void VW_ShowCursor (void) +{ + cursorvisible++; +} + + +//========================================================================== + +/* +==================== += += VW_HideCursor += +==================== +*/ + +void VW_HideCursor (void) +{ + cursorvisible--; +} + +//========================================================================== + +/* +==================== += += VW_MoveCursor += +==================== +*/ +#define MAXCURSORX (319-24) +#define MAXCURSORY (199-24) + +void VW_MoveCursor (int x, int y) +{ + if (x>MAXCURSORX) + x=MAXCURSORX; + if (y>MAXCURSORY) + y=MAXCURSORY; // catacombs hack to keep cursor on screen + + cursorx = x; + cursory = y; +} + +//========================================================================== + +/* +==================== += += VW_SetCursor += += Load in a sprite to be used as a cursor, and allocate background save space += +==================== +*/ + +void VW_SetCursor (int spritenum) +{ + VW_FreeCursor (); + + cursornumber = spritenum; + + CA_CacheGrChunk (spritenum); + MM_SetLock (&grsegs[spritenum],true); + + cursorwidth = spritetable[spritenum-STARTSPRITES].width+1; + cursorheight = spritetable[spritenum-STARTSPRITES].height; + + MM_GetPtr (&cursorsave,cursorwidth*cursorheight*5); + MM_SetLock (&cursorsave,true); +} + + +/* +==================== += += VW_FreeCursor += += Frees the memory used by the cursor and its background save += +==================== +*/ + +void VW_FreeCursor (void) +{ + if (cursornumber) + { + MM_SetLock (&grsegs[cursornumber],false); + MM_SetPurge (&grsegs[cursornumber],3); + MM_SetLock (&cursorsave,false); + MM_FreePtr (&cursorsave); + cursornumber = 0; + } +} + + +/* +============================================================================= + + Double buffer management routines + +============================================================================= +*/ + +/* +====================== += += VW_InitDoubleBuffer += +====================== +*/ + +void VW_InitDoubleBuffer (void) +{ +#if GRMODE == EGAGR + VW_SetScreen (displayofs+panadjust,0); // no pel pan +#endif +} + + +/* +====================== += += VW_FixRefreshBuffer += += Copies the view page to the buffer page on page flipped refreshes to += avoid a one frame shear around pop up windows += +====================== +*/ + +void VW_FixRefreshBuffer (void) +{ +#if GRMODE == EGAGR + VW_ScreenToScreen (displayofs,bufferofs,PORTTILESWIDE*4*CHARWIDTH, + (PORTTILESHIGH-1)*16); +#endif +} + + +/* +====================== += += VW_QuitDoubleBuffer += +====================== +*/ + +void VW_QuitDoubleBuffer (void) +{ +} + + +/* +======================= += += VW_MarkUpdateBlock += += Takes a pixel bounded block and marks the tiles in bufferblocks += Returns 0 if the entire block is off the buffer screen += +======================= +*/ + +int VW_MarkUpdateBlock (int x1, int y1, int x2, int y2) +{ + int x,y,xt1,yt1,xt2,yt2,nextline; + byte *mark; + + xt1 = x1>>PIXTOBLOCK; + yt1 = y1>>PIXTOBLOCK; + + xt2 = x2>>PIXTOBLOCK; + yt2 = y2>>PIXTOBLOCK; + + if (xt1<0) + xt1=0; + else if (xt1>=UPDATEWIDE-1) + return 0; + + if (yt1<0) + yt1=0; + else if (yt1>UPDATEHIGH) + return 0; + + if (xt2<0) + return 0; + else if (xt2>=UPDATEWIDE-1) + xt2 = UPDATEWIDE-2; + + if (yt2<0) + return 0; + else if (yt2>=UPDATEHIGH) + yt2 = UPDATEHIGH-1; + + mark = updateptr + uwidthtable[yt1] + xt1; + nextline = UPDATEWIDE - (xt2-xt1) - 1; + + for (y=yt1;y<=yt2;y++) + { + for (x=xt1;x<=xt2;x++) + *mark++ = 1; // this tile will need to be updated + + mark += nextline; + } + + return 1; +} + + +/* +=========================== += += VW_UpdateScreen += += Updates any changed areas of the double buffer and displays the cursor += +=========================== +*/ + +void VW_UpdateScreen (void) +{ + if (cursorvisible>0) + VWL_DrawCursor(); + +#if GRMODE == EGAGR + VWL_UpdateScreenBlocks(); + +asm mov ax,ds +asm mov es,ax +asm mov di,[updateptr] // cat3d patch +asm xor ax,ax // clear out the update matrix +asm mov cx,UPDATEWIDE*UPDATEHIGH/2 +asm rep stosw + *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE; + +asm cli +asm mov cx,[displayofs] +asm add cx,[panadjust] +asm mov dx,CRTC_INDEX +asm mov al,0ch // start address high register +asm out dx,al +asm inc dx +asm mov al,ch +asm out dx,al +asm dec dx +asm mov al,0dh // start address low register +asm out dx,al +asm mov al,cl +asm inc dx +asm out dx,al +asm sti + +#endif +#if GRMODE == CGAGR + VW_CGAFullUpdate(); +#endif + + if (cursorvisible>0) + VWL_EraseCursor(); +} + + + +void VWB_DrawTile8 (int x, int y, int tile) +{ + x+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7)) + VW_DrawTile8 (x/SCREENXDIV,y,tile); +} + +void VWB_DrawTile8M (int x, int y, int tile) +{ + int xb; + + x+=pansx; + y+=pansy; + xb = x/SCREENXDIV; // use intermediate because VW_DT8M is macro + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7)) + VW_DrawTile8M (xb,y,tile); +} + +void VWB_DrawTile16 (int x, int y, int tile) +{ + x+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15)) + VW_DrawTile16 (x/SCREENXDIV,y,tile); +} + +void VWB_DrawTile16M (int x, int y, int tile) +{ + int xb; + + x+=pansx; + y+=pansy; + xb = x/SCREENXDIV; // use intermediate because VW_DT16M is macro + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15)) + VW_DrawTile16M (xb,y,tile); +} + +#if NUMPICS +void VWB_DrawPic (int x, int y, int chunknum) +{ +// mostly copied from drawpic + int picnum = chunknum - STARTPICS; + memptr source; + unsigned dest,width,height; + + x+=pansx; + y+=pansy; + x/= SCREENXDIV; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = pictable[picnum].width; + height = pictable[picnum].height; + + if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1)) + VW_MemToScreen(source,dest,width,height); +} +#endif + +#if NUMPICM>0 +void VWB_DrawMPic(int x, int y, int chunknum) +{ +// mostly copied from drawmpic + int picnum = chunknum - STARTPICM; + memptr source; + unsigned dest,width,height; + + x+=pansx; + y+=pansy; + x/=SCREENXDIV; + + source = grsegs[chunknum]; + dest = ylookup[y]+x+bufferofs; + width = picmtable[picnum].width; + height = picmtable[picnum].height; + + if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1)) + VW_MaskBlock(source,0,dest,width,height,width*height); +} +#endif + + +void VWB_Bar (int x, int y, int width, int height, int color) +{ + x+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x,y,x+width,y+height-1) ) + VW_Bar (x,y,width,height,color); +} + + +#if NUMFONT +void VWB_DrawPropString (char far *string) +{ + int x,y; + x = px+pansx; + y = py+pansy; + VW_DrawPropString (string); + VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1); +} +#endif + + +#if NUMFONTM +void VWB_DrawMPropString (char far *string) +{ + int x,y; + x = px+pansx; + y = py+pansy; + VW_DrawMPropString (string); + VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1); +} +#endif + +#if NUMSPRITES +void VWB_DrawSprite(int x, int y, int chunknum) +{ + spritetabletype far *spr; + spritetype _seg *block; + unsigned dest,shift,width,height; + + x+=pansx; + y+=pansy; + + spr = &spritetable[chunknum-STARTSPRITES]; + block = (spritetype _seg *)grsegs[chunknum]; + + y+=spr->orgy>>G_P_SHIFT; + x+=spr->orgx>>G_P_SHIFT; + + +#if GRMODE == EGAGR + shift = (x&7)/2; +#endif +#if GRMODE == CGAGR + shift = 0; +#endif + + dest = bufferofs + ylookup[y]; + if (x>=0) + dest += x/SCREENXDIV; + else + dest += (x+1)/SCREENXDIV; + + width = block->width[shift]; + height = spr->height; + + if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+width*SCREENXDIV-1 + ,y+height-1)) + VW_MaskBlock (block,block->sourceoffset[shift],dest, + width,height,block->planesize[shift]); +} +#endif + +void VWB_Plot (int x, int y, int color) +{ + x+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x,y,x,y)) + VW_Plot(x,y,color); +} + +void VWB_Hlin (int x1, int x2, int y, int color) +{ + x1+=pansx; + x2+=pansx; + y+=pansy; + if (VW_MarkUpdateBlock (x1,y,x2,y)) + VW_Hlin(x1,x2,y,color); +} + +void VWB_Vlin (int y1, int y2, int x, int color) +{ + x+=pansx; + y1+=pansy; + y2+=pansy; + if (VW_MarkUpdateBlock (x,y1,x,y2)) + VW_Vlin(y1,y2,x,color); +} + + +//=========================================================================== diff --git a/ID_VW.H b/ID_VW.H new file mode 100644 index 0000000..b3ae157 --- /dev/null +++ b/ID_VW.H @@ -0,0 +1,370 @@ +/* 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_VW.H + +#ifndef __TYPES__ +#include "ID_TYPES.H" +#endif + +#ifndef __ID_MM__ +#include "ID_MM.H" +#endif + +#ifndef __ID_GLOB__ +#include "ID_GLOB.H" +#endif + +#define __ID_VW__ + +//=========================================================================== + +#define G_P_SHIFT 4 // global >> ?? = pixels + +#if GRMODE == EGAGR +#define SCREENWIDTH 40 +#define CHARWIDTH 1 +#define TILEWIDTH 2 +#define GRPLANES 4 +#define BYTEPIXELS 8 +#endif + +#if GRMODE == CGAGR +#define SCREENWIDTH 128 +#define CHARWIDTH 2 +#define TILEWIDTH 4 +#define GRPLANES 1 +#define BYTEPIXELS 4 +#endif + +#define VIRTUALHEIGHT 300 +#define VIRTUALWIDTH 512 + + +#if GRMODE == CGAGR + +#define MAXSHIFTS 1 + +#define WHITE 3 // graphics mode independant colors +#define BLACK 0 +#define FIRSTCOLOR 1 +#define SECONDCOLOR 2 +#define F_WHITE 0 // for XOR font drawing +#define F_BLACK 3 +#define F_FIRSTCOLOR 2 +#define F_SECONDCOLOR 1 + +#endif + +#if GRMODE == EGAGR + +#define MAXSHIFTS 4 + +#define WHITE 15 // graphics mode independant colors +#define BLACK 0 +#define FIRSTCOLOR 1 +#define SECONDCOLOR 12 +#define F_WHITE 0 // for XOR font drawing +#define F_BLACK 15 +#define F_FIRSTCOLOR 14 +#define F_SECONDCOLOR 3 + +#endif + +#if GRMODE == EGAGR +#define SCREENXMASK (~7) +#define SCREENXPLUS (7) +#define SCREENXDIV (8) +#endif + +#if GRMODE == CGAGR +#define SCREENXMASK (~3) +#define SCREENXDIV (4) +#endif + +//=========================================================================== + + +#define SC_INDEX 0x3C4 +#define SC_RESET 0 +#define SC_CLOCK 1 +#define SC_MAPMASK 2 +#define SC_CHARMAP 3 +#define SC_MEMMODE 4 + +#define CRTC_INDEX 0x3D4 +#define CRTC_H_TOTAL 0 +#define CRTC_H_DISPEND 1 +#define CRTC_H_BLANK 2 +#define CRTC_H_ENDBLANK 3 +#define CRTC_H_RETRACE 4 +#define CRTC_H_ENDRETRACE 5 +#define CRTC_V_TOTAL 6 +#define CRTC_OVERFLOW 7 +#define CRTC_ROWSCAN 8 +#define CRTC_MAXSCANLINE 9 +#define CRTC_CURSORSTART 10 +#define CRTC_CURSOREND 11 +#define CRTC_STARTHIGH 12 +#define CRTC_STARTLOW 13 +#define CRTC_CURSORHIGH 14 +#define CRTC_CURSORLOW 15 +#define CRTC_V_RETRACE 16 +#define CRTC_V_ENDRETRACE 17 +#define CRTC_V_DISPEND 18 +#define CRTC_OFFSET 19 +#define CRTC_UNDERLINE 20 +#define CRTC_V_BLANK 21 +#define CRTC_V_ENDBLANK 22 +#define CRTC_MODE 23 +#define CRTC_LINECOMPARE 24 + + +#define GC_INDEX 0x3CE +#define GC_SETRESET 0 +#define GC_ENABLESETRESET 1 +#define GC_COLORCOMPARE 2 +#define GC_DATAROTATE 3 +#define GC_READMAP 4 +#define GC_MODE 5 +#define GC_MISCELLANEOUS 6 +#define GC_COLORDONTCARE 7 +#define GC_BITMASK 8 + +#define ATR_INDEX 0x3c0 +#define ATR_MODE 16 +#define ATR_OVERSCAN 17 +#define ATR_COLORPLANEENABLE 18 +#define ATR_PELPAN 19 +#define ATR_COLORSELECT 20 + +#define STATUS_REGISTER_1 0x3da + +//=========================================================================== + +typedef enum {NOcard,MDAcard,CGAcard,EGAcard,MCGAcard,VGAcard, + HGCcard=0x80,HGCPcard,HICcard} cardtype; + +typedef struct +{ + int width, + height, + orgx,orgy, + xl,yl,xh,yh, + shifts; +} spritetabletype; + +typedef struct +{ + unsigned sourceoffset[MAXSHIFTS]; + unsigned planesize[MAXSHIFTS]; + unsigned width[MAXSHIFTS]; + byte data[]; +} spritetype; // the memptr for each sprite points to this + +typedef struct +{ + int width,height; +} pictabletype; + + +typedef struct +{ + int height; + int location[256]; + char width[256]; +} fontstruct; + + +typedef enum {CGAgr,EGAgr,VGAgr} grtype; + +//=========================================================================== + +extern cardtype videocard; // set by VW_Startup +extern grtype grmode; // CGAgr, EGAgr, VGAgr + +extern unsigned bufferofs; // hidden port to draw to before displaying +extern unsigned displayofs; // origin of port on visable screen +extern unsigned panx,pany; // panning adjustments inside port in pixels +extern unsigned pansx,pansy; +extern unsigned panadjust; // panx/pany adjusted by screen resolution + +extern unsigned screenseg; // normally 0xa000 or buffer segment + +extern unsigned linewidth; +extern unsigned ylookup[VIRTUALHEIGHT]; + +extern boolean screenfaded; +extern char colors[7][17]; // pallets for fades + +extern pictabletype _seg *pictable; +extern pictabletype _seg *picmtable; +extern spritetabletype _seg *spritetable; + +extern unsigned fontnumber; // 0 based font number for drawing +extern int px,py; +extern byte pdrawmode,fontcolor; + +extern int bordercolor; + +// +// asm globals +// + +extern unsigned *shifttabletable[8]; +extern unsigned bufferwidth,bufferheight,screenspot; // used by font drawing stuff + + + +//=========================================================================== + + +void VW_Startup (void); +void VW_Shutdown (void); + +cardtype VW_VideoID (void); + +// +// EGA hardware routines +// + +#define EGAWRITEMODE(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_MODE+256*x;out dx,ax;sti;} +#define EGABITMASK(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_BITMASK+256*x;out dx,ax;sti;} +#define EGAMAPMASK(x) asm{cli;mov dx,SC_INDEX;mov ax,SC_MAPMASK+x*256;out dx,ax;sti;} +#define EGAREADMAP(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_READMAP+x*256;out dx,ax;sti;} + +void VW_SetLineWidth(int width); +void VW_SetSplitScreen(int width); +void VW_SetScreen (unsigned CRTC, unsigned pelpan); + +void VW_SetScreenMode (int grmode); +void VW_ClearVideo (int color); +void VW_WaitVBL (int number); + +void VW_ColorBorder (int color); +void VW_SetPalette(byte *palette); +void VW_SetDefaultColors(void); +void VW_FadeOut(void); +void VW_FadeIn(void); +void VW_FadeUp(void); +void VW_FadeDown(void); + +void VW_SetAtrReg (int reg, int value); + +// +// block primitives +// + +void VW_MaskBlock(memptr segm,unsigned ofs,unsigned dest, + unsigned wide,unsigned height,unsigned planesize); +void VW_MemToScreen(memptr source,unsigned dest,unsigned width,unsigned height); +void VW_ScreenToMem(unsigned source,memptr dest,unsigned width,unsigned height); +void VW_ScreenToScreen(unsigned source,unsigned dest,unsigned width,unsigned height); + + +// +// block addressable routines +// + +void VW_DrawTile8(unsigned x, unsigned y, unsigned tile); + +#if GRMODE == EGAGR + +#define VW_DrawTile8M(x,y,t) \ + VW_MaskBlock(grsegs[STARTTILE8M],(t)*40,bufferofs+ylookup[y]+(x),1,8,8) +#define VW_DrawTile16(x,y,t) \ + VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),2,16) +#define VW_DrawTile16M(x,y,t) \ + VW_MaskBlock(grsegs[STARTTILE16M],(t)*160,bufferofs+ylookup[y]+(x),2,16,32) + +#endif + +#if GRMODE == CGAGR + +#define VW_DrawTile8M(x,y,t) \ + VW_MaskBlock(grsegs[STARTTILE8M],(t)*32,bufferofs+ylookup[y]+(x),2,8,16) +#define VW_DrawTile16(x,y,t) \ + VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),4,16) +#define VW_DrawTile16M(x,y,t) \ + VW_MaskBlock(grsegs[STARTTILE16M],(t)*128,bufferofs+ylookup[y]+(x),4,16,64) + +#endif + +void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum); +void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum); +void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum); + +// +// pixel addressable routines +// +void VW_MeasurePropString (char far *string, word *width, word *height); +void VW_MeasureMPropString (char far *string, word *width, word *height); + +void VW_DrawPropString (char far *string); +void VW_DrawMPropString (char far *string); +void VW_DrawSprite(int x, int y, unsigned sprite); +void VW_Plot(unsigned x, unsigned y, unsigned color); +void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color); +void VW_Vlin(unsigned yl, unsigned yh, unsigned x, unsigned color); +void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height, + unsigned color); + +//=========================================================================== + +// +// Double buffer management routines +// + +void VW_InitDoubleBuffer (void); +void VW_FixRefreshBuffer (void); +int VW_MarkUpdateBlock (int x1, int y1, int x2, int y2); +void VW_UpdateScreen (void); +void VW_CGAFullUpdate (void); + +// +// cursor +// + +void VW_ShowCursor (void); +void VW_HideCursor (void); +void VW_MoveCursor (int x, int y); +void VW_SetCursor (int spritenum); +void VW_FreeCursor (void); + +// +// mode independant routines +// coordinates in pixels, rounded to best screen res +// regions marked in double buffer +// + +void VWB_DrawTile8 (int x, int y, int tile); +void VWB_DrawTile8M (int x, int y, int tile); +void VWB_DrawTile16 (int x, int y, int tile); +void VWB_DrawTile16M (int x, int y, int tile); +void VWB_DrawPic (int x, int y, int chunknum); +void VWB_DrawMPic(int x, int y, int chunknum); +void VWB_Bar (int x, int y, int width, int height, int color); + +void VWB_DrawPropString (char far *string); +void VWB_DrawMPropString (char far *string); +void VWB_DrawSprite (int x, int y, int chunknum); +void VWB_Plot (int x, int y, int color); +void VWB_Hlin (int x1, int x2, int y, int color); +void VWB_Vlin (int y1, int y2, int x, int color); + +//=========================================================================== diff --git a/ID_VW_A.ASM b/ID_VW_A.ASM new file mode 100644 index 0000000..3c5132e --- /dev/null +++ b/ID_VW_A.ASM @@ -0,0 +1,732 @@ +; 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_VW_A.ASM + +IDEAL +MODEL MEDIUM,C + +INCLUDE "ID_ASM.EQU" + +WAITFORVBL = 1 ; setting to 0 causes setscreen and waitvbl + ; to skip waiting for VBL (for timing things) + +;============================================================================ + +DATASEG + +EXTRN screenseg :WORD +EXTRN drawofs :WORD +EXTRN bufferofs :WORD +EXTRN displayofs :WORD +EXTRN drawofs :WORD +EXTRN panadjust :WORD +EXTRN ylookup :WORD +EXTRN linewidth :WORD +EXTRN grsegs :WORD +EXTRN updateptr :WORD +EXTRN blockstarts :WORD ;offsets from drawofs for each update block +EXTRN fontspace :WORD +EXTRN fontnumber :WORD + + +planemask db ? +planenum db ? +screendest dw ? +linedelta dw ? + +LABEL shiftdata0 WORD + dw 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 + dw 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 + dw 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41 + dw 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55 + dw 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69 + dw 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83 + dw 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 + dw 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111 + dw 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125 + dw 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139 + dw 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153 + dw 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167 + dw 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181 + dw 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195 + dw 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209 + dw 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223 + dw 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237 + dw 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251 + dw 252, 253, 254, 255 + +LABEL shiftdata1 WORD + dw 0,32768, 1,32769, 2,32770, 3,32771, 4,32772, 5,32773, 6,32774 + dw 7,32775, 8,32776, 9,32777, 10,32778, 11,32779, 12,32780, 13,32781 + dw 14,32782, 15,32783, 16,32784, 17,32785, 18,32786, 19,32787, 20,32788 + dw 21,32789, 22,32790, 23,32791, 24,32792, 25,32793, 26,32794, 27,32795 + dw 28,32796, 29,32797, 30,32798, 31,32799, 32,32800, 33,32801, 34,32802 + dw 35,32803, 36,32804, 37,32805, 38,32806, 39,32807, 40,32808, 41,32809 + dw 42,32810, 43,32811, 44,32812, 45,32813, 46,32814, 47,32815, 48,32816 + dw 49,32817, 50,32818, 51,32819, 52,32820, 53,32821, 54,32822, 55,32823 + dw 56,32824, 57,32825, 58,32826, 59,32827, 60,32828, 61,32829, 62,32830 + dw 63,32831, 64,32832, 65,32833, 66,32834, 67,32835, 68,32836, 69,32837 + dw 70,32838, 71,32839, 72,32840, 73,32841, 74,32842, 75,32843, 76,32844 + dw 77,32845, 78,32846, 79,32847, 80,32848, 81,32849, 82,32850, 83,32851 + dw 84,32852, 85,32853, 86,32854, 87,32855, 88,32856, 89,32857, 90,32858 + dw 91,32859, 92,32860, 93,32861, 94,32862, 95,32863, 96,32864, 97,32865 + dw 98,32866, 99,32867, 100,32868, 101,32869, 102,32870, 103,32871, 104,32872 + dw 105,32873, 106,32874, 107,32875, 108,32876, 109,32877, 110,32878, 111,32879 + dw 112,32880, 113,32881, 114,32882, 115,32883, 116,32884, 117,32885, 118,32886 + dw 119,32887, 120,32888, 121,32889, 122,32890, 123,32891, 124,32892, 125,32893 + dw 126,32894, 127,32895 + +LABEL shiftdata2 WORD + dw 0,16384,32768,49152, 1,16385,32769,49153, 2,16386,32770,49154, 3,16387 + dw 32771,49155, 4,16388,32772,49156, 5,16389,32773,49157, 6,16390,32774,49158 + dw 7,16391,32775,49159, 8,16392,32776,49160, 9,16393,32777,49161, 10,16394 + dw 32778,49162, 11,16395,32779,49163, 12,16396,32780,49164, 13,16397,32781,49165 + dw 14,16398,32782,49166, 15,16399,32783,49167, 16,16400,32784,49168, 17,16401 + dw 32785,49169, 18,16402,32786,49170, 19,16403,32787,49171, 20,16404,32788,49172 + dw 21,16405,32789,49173, 22,16406,32790,49174, 23,16407,32791,49175, 24,16408 + dw 32792,49176, 25,16409,32793,49177, 26,16410,32794,49178, 27,16411,32795,49179 + dw 28,16412,32796,49180, 29,16413,32797,49181, 30,16414,32798,49182, 31,16415 + dw 32799,49183, 32,16416,32800,49184, 33,16417,32801,49185, 34,16418,32802,49186 + dw 35,16419,32803,49187, 36,16420,32804,49188, 37,16421,32805,49189, 38,16422 + dw 32806,49190, 39,16423,32807,49191, 40,16424,32808,49192, 41,16425,32809,49193 + dw 42,16426,32810,49194, 43,16427,32811,49195, 44,16428,32812,49196, 45,16429 + dw 32813,49197, 46,16430,32814,49198, 47,16431,32815,49199, 48,16432,32816,49200 + dw 49,16433,32817,49201, 50,16434,32818,49202, 51,16435,32819,49203, 52,16436 + dw 32820,49204, 53,16437,32821,49205, 54,16438,32822,49206, 55,16439,32823,49207 + dw 56,16440,32824,49208, 57,16441,32825,49209, 58,16442,32826,49210, 59,16443 + dw 32827,49211, 60,16444,32828,49212, 61,16445,32829,49213, 62,16446,32830,49214 + dw 63,16447,32831,49215 + +LABEL shiftdata3 WORD + dw 0, 8192,16384,24576,32768,40960,49152,57344, 1, 8193,16385,24577,32769,40961 + dw 49153,57345, 2, 8194,16386,24578,32770,40962,49154,57346, 3, 8195,16387,24579 + dw 32771,40963,49155,57347, 4, 8196,16388,24580,32772,40964,49156,57348, 5, 8197 + dw 16389,24581,32773,40965,49157,57349, 6, 8198,16390,24582,32774,40966,49158,57350 + dw 7, 8199,16391,24583,32775,40967,49159,57351, 8, 8200,16392,24584,32776,40968 + dw 49160,57352, 9, 8201,16393,24585,32777,40969,49161,57353, 10, 8202,16394,24586 + dw 32778,40970,49162,57354, 11, 8203,16395,24587,32779,40971,49163,57355, 12, 8204 + dw 16396,24588,32780,40972,49164,57356, 13, 8205,16397,24589,32781,40973,49165,57357 + dw 14, 8206,16398,24590,32782,40974,49166,57358, 15, 8207,16399,24591,32783,40975 + dw 49167,57359, 16, 8208,16400,24592,32784,40976,49168,57360, 17, 8209,16401,24593 + dw 32785,40977,49169,57361, 18, 8210,16402,24594,32786,40978,49170,57362, 19, 8211 + dw 16403,24595,32787,40979,49171,57363, 20, 8212,16404,24596,32788,40980,49172,57364 + dw 21, 8213,16405,24597,32789,40981,49173,57365, 22, 8214,16406,24598,32790,40982 + dw 49174,57366, 23, 8215,16407,24599,32791,40983,49175,57367, 24, 8216,16408,24600 + dw 32792,40984,49176,57368, 25, 8217,16409,24601,32793,40985,49177,57369, 26, 8218 + dw 16410,24602,32794,40986,49178,57370, 27, 8219,16411,24603,32795,40987,49179,57371 + dw 28, 8220,16412,24604,32796,40988,49180,57372, 29, 8221,16413,24605,32797,40989 + dw 49181,57373, 30, 8222,16414,24606,32798,40990,49182,57374, 31, 8223,16415,24607 + dw 32799,40991,49183,57375 + +LABEL shiftdata4 WORD + dw 0, 4096, 8192,12288,16384,20480,24576,28672,32768,36864,40960,45056,49152,53248 + dw 57344,61440, 1, 4097, 8193,12289,16385,20481,24577,28673,32769,36865,40961,45057 + dw 49153,53249,57345,61441, 2, 4098, 8194,12290,16386,20482,24578,28674,32770,36866 + dw 40962,45058,49154,53250,57346,61442, 3, 4099, 8195,12291,16387,20483,24579,28675 + dw 32771,36867,40963,45059,49155,53251,57347,61443, 4, 4100, 8196,12292,16388,20484 + dw 24580,28676,32772,36868,40964,45060,49156,53252,57348,61444, 5, 4101, 8197,12293 + dw 16389,20485,24581,28677,32773,36869,40965,45061,49157,53253,57349,61445, 6, 4102 + dw 8198,12294,16390,20486,24582,28678,32774,36870,40966,45062,49158,53254,57350,61446 + dw 7, 4103, 8199,12295,16391,20487,24583,28679,32775,36871,40967,45063,49159,53255 + dw 57351,61447, 8, 4104, 8200,12296,16392,20488,24584,28680,32776,36872,40968,45064 + dw 49160,53256,57352,61448, 9, 4105, 8201,12297,16393,20489,24585,28681,32777,36873 + dw 40969,45065,49161,53257,57353,61449, 10, 4106, 8202,12298,16394,20490,24586,28682 + dw 32778,36874,40970,45066,49162,53258,57354,61450, 11, 4107, 8203,12299,16395,20491 + dw 24587,28683,32779,36875,40971,45067,49163,53259,57355,61451, 12, 4108, 8204,12300 + dw 16396,20492,24588,28684,32780,36876,40972,45068,49164,53260,57356,61452, 13, 4109 + dw 8205,12301,16397,20493,24589,28685,32781,36877,40973,45069,49165,53261,57357,61453 + dw 14, 4110, 8206,12302,16398,20494,24590,28686,32782,36878,40974,45070,49166,53262 + dw 57358,61454, 15, 4111, 8207,12303,16399,20495,24591,28687,32783,36879,40975,45071 + dw 49167,53263,57359,61455 + +LABEL shiftdata5 WORD + dw 0, 2048, 4096, 6144, 8192,10240,12288,14336,16384,18432,20480,22528,24576,26624 + dw 28672,30720,32768,34816,36864,38912,40960,43008,45056,47104,49152,51200,53248,55296 + dw 57344,59392,61440,63488, 1, 2049, 4097, 6145, 8193,10241,12289,14337,16385,18433 + dw 20481,22529,24577,26625,28673,30721,32769,34817,36865,38913,40961,43009,45057,47105 + dw 49153,51201,53249,55297,57345,59393,61441,63489, 2, 2050, 4098, 6146, 8194,10242 + dw 12290,14338,16386,18434,20482,22530,24578,26626,28674,30722,32770,34818,36866,38914 + dw 40962,43010,45058,47106,49154,51202,53250,55298,57346,59394,61442,63490, 3, 2051 + dw 4099, 6147, 8195,10243,12291,14339,16387,18435,20483,22531,24579,26627,28675,30723 + dw 32771,34819,36867,38915,40963,43011,45059,47107,49155,51203,53251,55299,57347,59395 + dw 61443,63491, 4, 2052, 4100, 6148, 8196,10244,12292,14340,16388,18436,20484,22532 + dw 24580,26628,28676,30724,32772,34820,36868,38916,40964,43012,45060,47108,49156,51204 + dw 53252,55300,57348,59396,61444,63492, 5, 2053, 4101, 6149, 8197,10245,12293,14341 + dw 16389,18437,20485,22533,24581,26629,28677,30725,32773,34821,36869,38917,40965,43013 + dw 45061,47109,49157,51205,53253,55301,57349,59397,61445,63493, 6, 2054, 4102, 6150 + dw 8198,10246,12294,14342,16390,18438,20486,22534,24582,26630,28678,30726,32774,34822 + dw 36870,38918,40966,43014,45062,47110,49158,51206,53254,55302,57350,59398,61446,63494 + dw 7, 2055, 4103, 6151, 8199,10247,12295,14343,16391,18439,20487,22535,24583,26631 + dw 28679,30727,32775,34823,36871,38919,40967,43015,45063,47111,49159,51207,53255,55303 + dw 57351,59399,61447,63495 + +LABEL shiftdata6 WORD + dw 0, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216,10240,11264,12288,13312 + dw 14336,15360,16384,17408,18432,19456,20480,21504,22528,23552,24576,25600,26624,27648 + dw 28672,29696,30720,31744,32768,33792,34816,35840,36864,37888,38912,39936,40960,41984 + dw 43008,44032,45056,46080,47104,48128,49152,50176,51200,52224,53248,54272,55296,56320 + dw 57344,58368,59392,60416,61440,62464,63488,64512, 1, 1025, 2049, 3073, 4097, 5121 + dw 6145, 7169, 8193, 9217,10241,11265,12289,13313,14337,15361,16385,17409,18433,19457 + dw 20481,21505,22529,23553,24577,25601,26625,27649,28673,29697,30721,31745,32769,33793 + dw 34817,35841,36865,37889,38913,39937,40961,41985,43009,44033,45057,46081,47105,48129 + dw 49153,50177,51201,52225,53249,54273,55297,56321,57345,58369,59393,60417,61441,62465 + dw 63489,64513, 2, 1026, 2050, 3074, 4098, 5122, 6146, 7170, 8194, 9218,10242,11266 + dw 12290,13314,14338,15362,16386,17410,18434,19458,20482,21506,22530,23554,24578,25602 + dw 26626,27650,28674,29698,30722,31746,32770,33794,34818,35842,36866,37890,38914,39938 + dw 40962,41986,43010,44034,45058,46082,47106,48130,49154,50178,51202,52226,53250,54274 + dw 55298,56322,57346,58370,59394,60418,61442,62466,63490,64514, 3, 1027, 2051, 3075 + dw 4099, 5123, 6147, 7171, 8195, 9219,10243,11267,12291,13315,14339,15363,16387,17411 + dw 18435,19459,20483,21507,22531,23555,24579,25603,26627,27651,28675,29699,30723,31747 + dw 32771,33795,34819,35843,36867,37891,38915,39939,40963,41987,43011,44035,45059,46083 + dw 47107,48131,49155,50179,51203,52227,53251,54275,55299,56323,57347,58371,59395,60419 + dw 61443,62467,63491,64515 + +LABEL shiftdata7 WORD + dw 0, 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144, 6656 + dw 7168, 7680, 8192, 8704, 9216, 9728,10240,10752,11264,11776,12288,12800,13312,13824 + dw 14336,14848,15360,15872,16384,16896,17408,17920,18432,18944,19456,19968,20480,20992 + dw 21504,22016,22528,23040,23552,24064,24576,25088,25600,26112,26624,27136,27648,28160 + dw 28672,29184,29696,30208,30720,31232,31744,32256,32768,33280,33792,34304,34816,35328 + dw 35840,36352,36864,37376,37888,38400,38912,39424,39936,40448,40960,41472,41984,42496 + dw 43008,43520,44032,44544,45056,45568,46080,46592,47104,47616,48128,48640,49152,49664 + dw 50176,50688,51200,51712,52224,52736,53248,53760,54272,54784,55296,55808,56320,56832 + dw 57344,57856,58368,58880,59392,59904,60416,60928,61440,61952,62464,62976,63488,64000 + dw 64512,65024, 1, 513, 1025, 1537, 2049, 2561, 3073, 3585, 4097, 4609, 5121, 5633 + dw 6145, 6657, 7169, 7681, 8193, 8705, 9217, 9729,10241,10753,11265,11777,12289,12801 + dw 13313,13825,14337,14849,15361,15873,16385,16897,17409,17921,18433,18945,19457,19969 + dw 20481,20993,21505,22017,22529,23041,23553,24065,24577,25089,25601,26113,26625,27137 + dw 27649,28161,28673,29185,29697,30209,30721,31233,31745,32257,32769,33281,33793,34305 + dw 34817,35329,35841,36353,36865,37377,37889,38401,38913,39425,39937,40449,40961,41473 + dw 41985,42497,43009,43521,44033,44545,45057,45569,46081,46593,47105,47617,48129,48641 + dw 49153,49665,50177,50689,51201,51713,52225,52737,53249,53761,54273,54785,55297,55809 + dw 56321,56833,57345,57857,58369,58881,59393,59905,60417,60929,61441,61953,62465,62977 + dw 63489,64001,64513,65025 + +shifttabletable dw shiftdata0,shiftdata1,shiftdata2,shiftdata3 + dw shiftdata4,shiftdata5,shiftdata6,shiftdata7 + +PUBLIC shifttabletable + + +;============================================================================ + +CODESEG + +IFE GRMODE-CGAGR +INCLUDE "ID_VW_AC.ASM" +ENDIF + +IFE GRMODE-EGAGR +INCLUDE "ID_VW_AE.ASM" +ENDIF + +IFE GRMODE-VGAGR +INCLUDE "ID_VW_AV.ASM" +ENDIF + +;============================================================================ +; +; MISC VIDEO ROUTINES +; +;============================================================================ + +;======== +; +; VW_WaitVBL (int number) +; +;======== + +PROC VW_WaitVBL number:WORD +PUBLIC VW_WaitVBL + +if WAITFORVBL ; skip wait if profiling + + mov dx,STATUS_REGISTER_1 + + mov cx,[number] + +waitvbl1: + in al,dx + test al,00001000b ;look for vbl + jnz waitvbl1 + +waitvbl2: + in al,dx + test al,00001000b ;look for vbl + jz waitvbl2 + + loop waitvbl1 + +endif + + ret + +ENDP + + +;=========================================================================== + + + MASM +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +; +; Name: VW_VideoID +; +; Function: Detects the presence of various video subsystems +; +; int VideoID; +; +; Subsystem ID values: +; 0 = (none) +; 1 = MDA +; 2 = CGA +; 3 = EGA +; 4 = MCGA +; 5 = VGA +; 80h = HGC +; 81h = HGC+ +; 82h = Hercules InColor +; +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +; +; Equates +; +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +VIDstruct STRUC ; corresponds to C data structure + +Video0Type DB ? ; first subsystem type +Display0Type DB ? ; display attached to first subsystem + +Video1Type DB ? ; second subsystem type +Display1Type DB ? ; display attached to second subsystem + +VIDstruct ENDS + + +Device0 EQU word ptr Video0Type[di] +Device1 EQU word ptr Video1Type[di] + + +MDA EQU 1 ; subsystem types +CGA EQU 2 +EGA EQU 3 +MCGA EQU 4 +VGA EQU 5 +HGC EQU 80h +HGCPlus EQU 81h +InColor EQU 82h + +MDADisplay EQU 1 ; display types +CGADisplay EQU 2 +EGAColorDisplay EQU 3 +PS2MonoDisplay EQU 4 +PS2ColorDisplay EQU 5 + +TRUE EQU 1 +FALSE EQU 0 + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +; +; Program +; +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +Results VIDstruct <> ;results go here! + +EGADisplays DB CGADisplay ; 0000b, 0001b (EGA switch values) + DB EGAColorDisplay ; 0010b, 0011b + DB MDADisplay ; 0100b, 0101b + DB CGADisplay ; 0110b, 0111b + DB EGAColorDisplay ; 1000b, 1001b + DB MDADisplay ; 1010b, 1011b + +DCCtable DB 0,0 ; translate table for INT 10h func 1Ah + DB MDA,MDADisplay + DB CGA,CGADisplay + DB 0,0 + DB EGA,EGAColorDisplay + DB EGA,MDADisplay + DB 0,0 + DB VGA,PS2MonoDisplay + DB VGA,PS2ColorDisplay + DB 0,0 + DB MCGA,EGAColorDisplay + DB MCGA,PS2MonoDisplay + DB MCGA,PS2ColorDisplay + +TestSequence DB TRUE ; this list of flags and addresses + DW FindPS2 ; determines the order in which this + ; program looks for the various +EGAflag DB ? ; subsystems + DW FindEGA + +CGAflag DB ? + DW FindCGA + +Monoflag DB ? + DW FindMono + +NumberOfTests EQU ($-TestSequence)/3 + + +PUBLIC VW_VideoID +VW_VideoID PROC + + push bp ; preserve caller registers + mov bp,sp + push ds + push si + push di + + push cs + pop ds + ASSUME DS:@Code + +; initialize the data structure that will contain the results + + lea di,Results ; DS:DI -> start of data structure + + mov Device0,0 ; zero these variables + mov Device1,0 + +; look for the various subsystems using the subroutines whose addresses are +; tabulated in TestSequence; each subroutine sets flags in TestSequence +; to indicate whether subsequent subroutines need to be called + + mov byte ptr CGAflag,TRUE + mov byte ptr EGAflag,TRUE + mov byte ptr Monoflag,TRUE + + mov cx,NumberOfTests + mov si,offset TestSequence + +@@L01: lodsb ; AL := flag + test al,al + lodsw ; AX := subroutine address + jz @@L02 ; skip subroutine if flag is false + + push si + push cx + call ax ; call subroutine to detect subsystem + pop cx + pop si + +@@L02: loop @@L01 + +; determine which subsystem is active + + call FindActive + + mov al,Results.Video0Type + mov ah,0 ; was: Results.Display0Type + + pop di ; restore caller registers and return + pop si + pop ds + mov sp,bp + pop bp + ret + +VW_VideoID ENDP + + +; +; FindPS2 +; +; This subroutine uses INT 10H function 1Ah to determine the video BIOS +; Display Combination Code (DCC) for each video subsystem present. +; + +FindPS2 PROC near + + mov ax,1A00h + int 10h ; call video BIOS for info + + cmp al,1Ah + jne @@L13 ; exit if function not supported (i.e., + ; no MCGA or VGA in system) + +; convert BIOS DCCs into specific subsystems & displays + + mov cx,bx + xor bh,bh ; BX := DCC for active subsystem + + or ch,ch + jz @@L11 ; jump if only one subsystem present + + mov bl,ch ; BX := inactive DCC + add bx,bx + mov ax,[bx+offset DCCtable] + + mov Device1,ax + + mov bl,cl + xor bh,bh ; BX := active DCC + +@@L11: add bx,bx + mov ax,[bx+offset DCCtable] + + mov Device0,ax + +; reset flags for subsystems that have been ruled out + + mov byte ptr CGAflag,FALSE + mov byte ptr EGAflag,FALSE + mov byte ptr Monoflag,FALSE + + lea bx,Video0Type[di] ; if the BIOS reported an MDA ... + cmp byte ptr [bx],MDA + je @@L12 + + lea bx,Video1Type[di] + cmp byte ptr [bx],MDA + jne @@L13 + +@@L12: mov word ptr [bx],0 ; ... Hercules can't be ruled out + mov byte ptr Monoflag,TRUE + +@@L13: ret + +FindPS2 ENDP + + +; +; FindEGA +; +; Look for an EGA. This is done by making a call to an EGA BIOS function +; which doesn't exist in the default (MDA, CGA) BIOS. + +FindEGA PROC near ; Caller: AH = flags + ; Returns: AH = flags + ; Video0Type and + ; Display0Type updated + + mov bl,10h ; BL := 10h (return EGA info) + mov ah,12h ; AH := INT 10H function number + int 10h ; call EGA BIOS for info + ; if EGA BIOS is present, + ; BL <> 10H + ; CL = switch setting + cmp bl,10h + je @@L22 ; jump if EGA BIOS not present + + mov al,cl + shr al,1 ; AL := switches/2 + mov bx,offset EGADisplays + xlat ; determine display type from switches + mov ah,al ; AH := display type + mov al,EGA ; AL := subystem type + call FoundDevice + + cmp ah,MDADisplay + je @@L21 ; jump if EGA has a monochrome display + + mov CGAflag,FALSE ; no CGA if EGA has color display + jmp short @@L22 + +@@L21: mov Monoflag,FALSE ; EGA has a mono display, so MDA and + ; Hercules are ruled out +@@L22: ret + +FindEGA ENDP + +; +; FindCGA +; +; This is done by looking for the CGA's 6845 CRTC at I/O port 3D4H. +; +FindCGA PROC near ; Returns: VIDstruct updated + + mov dx,3D4h ; DX := CRTC address port + call Find6845 + jc @@L31 ; jump if not present + + mov al,CGA + mov ah,CGADisplay + call FoundDevice + +@@L31: ret + +FindCGA ENDP + +; +; FindMono +; +; This is done by looking for the MDA's 6845 CRTC at I/O port 3B4H. If +; a 6845 is found, the subroutine distinguishes between an MDA +; and a Hercules adapter by monitoring bit 7 of the CRT Status byte. +; This bit changes on Hercules adapters but does not change on an MDA. +; +; The various Hercules adapters are identified by bits 4 through 6 of +; the CRT Status value: +; +; 000b = HGC +; 001b = HGC+ +; 101b = InColor card +; + +FindMono PROC near ; Returns: VIDstruct updated + + mov dx,3B4h ; DX := CRTC address port + call Find6845 + jc @@L44 ; jump if not present + + mov dl,0BAh ; DX := 3BAh (status port) + in al,dx + and al,80h + mov ah,al ; AH := bit 7 (vertical sync on HGC) + + mov cx,8000h ; do this 32768 times +@@L41: in al,dx + and al,80h ; isolate bit 7 + cmp ah,al + loope @@L41 ; wait for bit 7 to change + jne @@L42 ; if bit 7 changed, it's a Hercules + + mov al,MDA ; if bit 7 didn't change, it's an MDA + mov ah,MDADisplay + call FoundDevice + jmp short @@L44 + +@@L42: in al,dx + mov dl,al ; DL := value from status port + and dl,01110000b ; mask bits 4 thru 6 + + mov ah,MDADisplay ; assume it's a monochrome display + + mov al,HGCPlus ; look for an HGC+ + cmp dl,00010000b + je @@L43 ; jump if it's an HGC+ + + mov al,HGC ; look for an InColor card or HGC + cmp dl,01010000b + jne @@L43 ; jump if it's not an InColor card + + mov al,InColor ; it's an InColor card + mov ah,EGAColorDisplay + +@@L43: call FoundDevice + +@@L44: ret + +FindMono ENDP + +; +; Find6845 +; +; This routine detects the presence of the CRTC on a MDA, CGA or HGC. +; The technique is to write and read register 0Fh of the chip (cursor +; low). If the same value is read as written, assume the chip is +; present at the specified port addr. +; + +Find6845 PROC near ; Caller: DX = port addr + ; Returns: cf set if not present + mov al,0Fh + out dx,al ; select 6845 reg 0Fh (Cursor Low) + inc dx + in al,dx ; AL := current Cursor Low value + mov ah,al ; preserve in AH + mov al,66h ; AL := arbitrary value + out dx,al ; try to write to 6845 + + mov cx,100h +@@L51: loop @@L51 ; wait for 6845 to respond + + in al,dx + xchg ah,al ; AH := returned value + ; AL := original value + out dx,al ; restore original value + + cmp ah,66h ; test whether 6845 responded + je @@L52 ; jump if it did (cf is reset) + + stc ; set carry flag if no 6845 present + +@@L52: ret + +Find6845 ENDP + + +; +; FindActive +; +; This subroutine stores the currently active device as Device0. The +; current video mode determines which subsystem is active. +; + +FindActive PROC near + + cmp word ptr Device1,0 + je @@L63 ; exit if only one subsystem + + cmp Video0Type[di],4 ; exit if MCGA or VGA present + jge @@L63 ; (INT 10H function 1AH + cmp Video1Type[di],4 ; already did the work) + jge @@L63 + + mov ah,0Fh + int 10h ; AL := current BIOS video mode + + and al,7 + cmp al,7 ; jump if monochrome + je @@L61 ; (mode 7 or 0Fh) + + cmp Display0Type[di],MDADisplay + jne @@L63 ; exit if Display0 is color + jmp short @@L62 + +@@L61: cmp Display0Type[di],MDADisplay + je @@L63 ; exit if Display0 is monochrome + +@@L62: mov ax,Device0 ; make Device0 currently active + xchg ax,Device1 + mov Device0,ax + +@@L63: ret + +FindActive ENDP + + +; +; FoundDevice +; +; This routine updates the list of subsystems. +; + +FoundDevice PROC near ; Caller: AH = display # + ; AL = subsystem # + ; Destroys: BX + lea bx,Video0Type[di] + cmp byte ptr [bx],0 + je @@L71 ; jump if 1st subsystem + + lea bx,Video1Type[di] ; must be 2nd subsystem + +@@L71: mov [bx],ax ; update list entry + ret + +FoundDevice ENDP + +IDEAL + + + +END diff --git a/ID_VW_AC.ASM b/ID_VW_AC.ASM new file mode 100644 index 0000000..f96072e --- /dev/null +++ b/ID_VW_AC.ASM @@ -0,0 +1,1485 @@ +; 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. + +;================================= +; +; CGA view manager routines +; +;================================= + +;============================================================================ +; +; All of these routines draw into a floating virtual screen segment in main +; memory. bufferofs points to the origin of the drawing page in screenseg. +; The routines that write out words must take into account buffer wrapping +; and not write a word at 0xffff (which causes an exception on 386s). +; +; The direction flag should be clear +; +;============================================================================ + +DATASEG + +plotpixels db 0c0h,030h,0ch,03h +colorbyte db 000000b,01010101b,10101010b,11111111b +colorword dw 0,5555h,0aaaah,0ffffh + +CODESEG + +;============================================================================ +; +; VW_Plot (int x,y,color) +; +;============================================================================ + + +PROC VW_Plot x:WORD, y:WORD, color:WORD +PUBLIC VW_Plot +USES SI,DI + + mov es,[screenseg] + + mov di,[bufferofs] + mov bx,[y] + shl bx,1 + add di,[ylookup+bx] + mov bx,[x] + mov ax,bx + shr ax,1 + shr ax,1 + add di,ax ; di = byte on screen + + and bx,3 + mov ah,[plotpixels+bx] + mov bx,[color] + mov cl,[colorbyte+bx] + and cl,ah + not ah + + mov al,[es:di] + and al,ah ; mask off other pixels + or al,cl + stosb + + ret + +ENDP + + +;============================================================================ +; +; VW_Vlin (int yl,yh,x,color) +; +;============================================================================ + +PROC VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD +PUBLIC VW_Vlin +USES SI,DI + + mov es,[screenseg] + + mov di,[bufferofs] + mov bx,[yl] + shl bx,1 + add di,[ylookup+bx] + mov bx,[x] + mov ax,bx + shr ax,1 + shr ax,1 + add di,ax ; di = byte on screen + + and bx,3 + mov ah,[plotpixels+bx] + mov bx,[color] + mov bl,[colorbyte+bx] + and bl,ah + not ah + + mov cx,[yh] + sub cx,[yl] + inc cx ;number of pixels to plot + + mov dx,[linewidth] + +@@plot: + mov al,[es:di] + and al,ah ; mask off other pixels + or al,bl + mov [es:di],al + add di,dx + loop @@plot + + ret + + ret + +ENDP + + +;============================================================================ + + +;=================== +; +; VW_DrawTile8 +; +; xcoord in bytes (8 pixels), ycoord in pixels +; All Tile8s are in one grseg, so an offset is calculated inside it +; +; DONE +; +;=================== + +PROC VW_DrawTile8 xcoord:WORD, ycoord:WORD, tile:WORD +PUBLIC VW_DrawTile8 +USES SI,DI + + mov es,[screenseg] + + mov di,[bufferofs] + add di,[xcoord] + mov bx,[ycoord] + shl bx,1 + add di,[ylookup+bx] + + mov bx,[linewidth] + sub bx,2 + + mov si,[tile] + shl si,1 + shl si,1 + shl si,1 + shl si,1 + + mov ds,[grsegs+STARTTILE8*2] ; segment for all tile8s + +; +; start drawing +; + +REPT 7 + movsb ;no word moves because of segment wrapping + movsb + add di,bx +ENDM + movsb + movsb + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VW_MaskBlock +; +; Draws a masked block shape to the screen. bufferofs is NOT accounted for. +; The mask comes first, then the data. Seperate unwound routines are used +; to speed drawing. +; +; Mask blocks will allways be an even width because of the way IGRAB works +; +; DONE +; +;============================================================================ + +DATASEG + +UNWOUNDMASKS = 18 + + +maskroutines dw mask0,mask0,mask2E,mask2O,mask4E,mask4O + dw mask6E,mask6O,mask8E,mask8O,mask10E,mask10O + dw mask12E,mask12O,mask14E,mask14O,mask16E,mask16O + dw mask18E,mask18O + + +routinetouse dw ? + +CODESEG + +PROC VW_MaskBlock segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD +PUBLIC VW_MaskBlock +USES SI,DI + + mov es,[screenseg] + + mov di,[wide] + mov dx,[linewidth] + sub dx,di ;dx = delta to start of next line + + mov bx,[planesize] ; si+bx = data location + + cmp di,UNWOUNDMASKS + jbe @@unwoundroutine + +;============== +; +; General purpose masked block drawing. This could be optimised into +; four routines to use words, but few play loop sprites should be this big! +; +;============== + + mov [ss:linedelta],dx + mov ds,[segm] + mov si,[ofs] + mov di,[dest] + mov dx,[height] ;scan lines to draw + +@@lineloopgen: + mov cx,[wide] +@@byteloop: + mov al,[es:di] + and al,[si] + or al,[bx+si] + inc si + stosb + loop @@byteloop + + add di,[ss:linedelta] + dec dx + jnz @@lineloopgen + +mask0: + mov ax,ss + mov ds,ax + ret ;width of 0 = no drawing + + +;================= +; +; use the unwound routines +; +;================= + +@@unwoundroutine: + shr di,1 ;we only have even width unwound routines + mov cx,[dest] + shr cx,1 + rcl di,1 ;shift a 1 in if destination is odd + shl di,1 + mov ax,[maskroutines+di] ;call the right routine + + mov ds,[segm] + mov si,[ofs] + mov di,[dest] + mov cx,[height] ;scan lines to draw + + jmp ax ;draw it + +;================= +; +; Horizontally unwound routines to draw certain masked blocks faster +; +;================= + +MACRO MASKBYTE + mov al,[es:di] + and al,[si] + or al,[bx+si] + inc si + stosb +ENDM + +MACRO MASKWORD + mov ax,[es:di] + and ax,[si] + or ax,[bx+si] + inc si + inc si + stosw +ENDM + +MACRO SPRITELOOP addr + add di,dx + loop addr + mov ax,ss + mov ds,ax + ret +ENDM + + +EVEN +mask2E: + MASKWORD + SPRITELOOP mask2E + +EVEN +mask2O: + MASKBYTE + MASKBYTE + SPRITELOOP mask2O + +EVEN +mask4E: + MASKWORD + MASKWORD + SPRITELOOP mask4E + +EVEN +mask4O: + MASKBYTE + MASKWORD + MASKBYTE + SPRITELOOP mask4O + +EVEN +mask6E: + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask6E + +EVEN +mask6O: + MASKBYTE + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask6O + +EVEN +mask8E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask8E + +EVEN +mask8O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask8O + +EVEN +mask10E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask10E + +EVEN +mask10O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask10O + +EVEN +mask12E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask12E + +EVEN +mask12O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask12O + +EVEN +mask14E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask14E + +EVEN +mask14O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask14O + +EVEN +mask16E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask16E + +EVEN +mask16O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask16O + +EVEN +mask18E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask18E + +EVEN +mask18O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask18O + + +ENDP + + +;============================================================================ +; +; VW_ScreenToScreen +; +; Basic block copy routine. Copies one block of screen memory to another, +; bufferofs is NOT accounted for. +; +; DONE +; +;============================================================================ + +PROC VW_ScreenToScreen source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_ScreenToScreen +USES SI,DI + + mov bx,[linewidth] + sub bx,[wide] + + mov ax,[screenseg] + mov es,ax + mov ds,ax + + mov si,[source] + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + mov ax,[wide] +; +; if the width, source, and dest are all even, use word moves +; This is allways the case in the CGA refresh +; + test ax,1 + jnz @@bytelineloop + test si,1 + jnz @@bytelineloop + test di,1 + jnz @@bytelineloop + + shr ax,1 +@@wordlineloop: + mov cx,ax + rep movsw + add si,bx + add di,bx + + dec dx + jnz @@wordlineloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +@@bytelineloop: + mov cx,ax + rep movsb + add si,bx + add di,bx + + dec dx + jnz @@bytelineloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VW_MemToScreen +; +; Basic block drawing routine. Takes a block shape at segment pointer source +; of width by height data, and draws it to dest in the virtual screen, +; based on linewidth. bufferofs is NOT accounted for. +; There are four drawing routines to provide the best optimized code while +; accounting for odd segment wrappings due to the floating screens. +; +; DONE +; +;============================================================================ + +DATASEG + +memtoscreentable dw eventoeven,eventoodd,oddtoeven,oddtoodd + +CODESEG + + +PROC VW_MemToScreen source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_MemToScreen +USES SI,DI + + mov es,[screenseg] + + mov bx,[linewidth] + sub bx,[wide] + + mov ds,[source] + + xor si,si ;block is segment aligned + + xor di,di + shr [wide],1 ;change wide to words, and see if carry is set + rcl di,1 ;1 if wide is odd + mov ax,[dest] + shr ax,1 + rcl di,1 ;shift a 1 in if destination is odd + shl di,1 ;to index into a word width table + mov dx,[height] ;scan lines to draw + mov ax,[wide] + jmp [ss:memtoscreentable+di] ;call the right routine + +;============== +; +; Copy an even width block to an even destination address +; +;============== + +eventoeven: + mov di,[dest] ;start at same place in all planes +EVEN +@@lineloopEE: + mov cx,ax + rep movsw + add di,bx + dec dx + jnz @@lineloopEE + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an odd width block to an even video address +; +;============== + +oddtoeven: + mov di,[dest] ;start at same place in all planes +EVEN +@@lineloopOE: + mov cx,ax + rep movsw + movsb ;copy the last byte + add di,bx + dec dx + jnz @@lineloopOE + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an even width block to an odd video address +; +;============== + +eventoodd: + mov di,[dest] ;start at same place in all planes + dec ax ;one word has to be handled seperately +EVEN +@@lineloopEO: + movsb + mov cx,ax + rep movsw + movsb + add di,bx + dec dx + jnz @@lineloopEO + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an odd width block to an odd video address +; +;============== + +oddtoodd: + mov di,[dest] ;start at same place in all planes +EVEN +@@lineloopOO: + movsb + mov cx,ax + rep movsw + add di,bx + dec dx + jnz @@lineloopOO + + mov ax,ss + mov ds,ax ;restore turbo's data segment + ret + + +ENDP + +;=========================================================================== +; +; VW_ScreenToMem +; +; Copies a block of video memory to main memory, in order from planes 0-3. +; This could be optimized along the lines of VW_MemToScreen to take advantage +; of word copies, but this is an infrequently called routine. +; +; DONE +; +;=========================================================================== + +PROC VW_ScreenToMem source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_ScreenToMem +USES SI,DI + + mov es,[dest] + + mov bx,[linewidth] + sub bx,[wide] + + mov ds,[screenseg] + + xor di,di + + mov si,[source] + mov dx,[height] ;scan lines to draw + +@@lineloop: + mov cx,[wide] + rep movsb + + add si,bx + + dec dx + jnz @@lineloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;=========================================================================== +; +; MISC CGA ROUTINES +; +;=========================================================================== + +;============== +; +; VW_SetScreen +; +; DONE +; +;============== + +PROC VW_SetScreen crtc:WORD +PUBLIC VW_SetScreen + +; +; for some reason, my XT's EGA card doesn't like word outs to the CRTC +; index... +; + cli + + mov cx,[crtc] + mov dx,CRTC_INDEX + mov al,0ch ;start address high register + out dx,al + inc dx + mov al,ch + out dx,al + dec dx + mov al,0dh ;start address low register + out dx,al + mov al,cl + inc dx + out dx,al + + sti + + ret + +ENDP + + +if NUMFONT+NUMFONTM + +;=========================================================================== +; +; GENERAL FONT DRAWING ROUTINES +; +;=========================================================================== + +DATASEG + +px dw ? ; proportional character drawing coordinates +py dw ? +pdrawmode db 11000b ; 8 = OR, 24 = XOR, put in GC_DATAROTATE +fontcolor db 15 ;0-15 mapmask value + +PUBLIC px,py,pdrawmode,fontcolor + +; +; offsets in font structure +; +pcharheight = 0 ;lines high +charloc = 2 ;pointers to every character +charwidth = 514 ;every character's width in pixels + + +propchar dw ? ; the character number to shift +stringptr dw ?,? + +fontcolormask dw ? ; font color expands into this + +BUFFWIDTH = 100 +BUFFHEIGHT = 32 ; must be twice as high as font for masked fonts + +databuffer db BUFFWIDTH*BUFFHEIGHT dup (?) + +bufferwidth dw ? ; bytes with valid info / line +bufferheight dw ? ; number of lines currently used + +bufferbyte dw ? +bufferbit dw ? +PUBLIC bufferwidth,bufferheight,bufferbyte,bufferbit + +screenspot dw ? ; where the buffer is going + +bufferextra dw ? ; add at end of a line copy +screenextra dw ? + +CODESEG + +;====================== +; +; Macros to table shift a byte of font +; +;====================== + +MACRO SHIFTNOXOR + mov al,[es:bx] ; source + xor ah,ah + shl ax,1 + mov si,ax + mov ax,[bp+si] ; table shift into two bytes + or [di],al ; or with first byte + inc di + mov [di],ah ; replace next byte + inc bx ; next source byte +ENDM + +MACRO SHIFTWITHXOR + mov al,[es:bx] ; source + xor ah,ah + shl ax,1 + mov si,ax + mov ax,[bp+si] ; table shift into two bytes + not ax + and [di],al ; and with first byte + inc di + mov [di],ah ; replace next byte + inc bx ; next source byte +ENDM + + +;======================= +; +; VWL_XORBuffer +; +; Pass buffer start in SI (somewhere in databuffer) +; Draws the buffer to the screen buffer +; +;======================== + +PROC VWL_XORBuffer NEAR +USES BP + mov bl,[fontcolor] + xor bh,bh + shl bx,1 + mov ax,[colorword+bx] + mov [fontcolormask],ax + + mov es,[screenseg] + mov di,[screenspot] + + mov bx,[bufferwidth] ;calculate offsets for end of each line + mov [bufferwidth],bx + + or bx,bx + jnz @@isthere + ret ;nothing to draw + +@@isthere: + test bx,1 + jnz @@odd + jmp @@even +; +; clear the last byte so word draws can be used +; +@@odd: + mov al,0 +line = 0 +REPT BUFFHEIGHT + mov [BYTE databuffer+BUFFWIDTH*line+bx],al +line = line+1 +ENDM + + inc bx +@@even: + mov ax,[linewidth] + sub ax,bx + mov [screenextra],ax + mov ax,BUFFWIDTH + sub ax,bx + mov [bufferextra],ax + mov dx,bx + shr dx,1 ;word to copy + + mov bx,[bufferheight] ;lines to copy + mov bp,[fontcolormask] +@@lineloop: + mov cx,dx +@@wordloop: + lodsw ;get a word from the buffer + and ax,bp + xor [es:di],ax ;draw it + add di,2 + loop @@wordloop + + add si,[bufferextra] + add di,[screenextra] + + dec bx + jnz @@lineloop + + ret +ENDP + + +DATASEG + +;============================================================================ +; +; NON MASKED FONT DRAWING ROUTINES +; +;============================================================================ + +if numfont + +DATASEG + +shiftdrawtable dw 0,shift1wide,shift2wide,shift3wide,shift4wide + dw shift5wide,shift6wide + +CODESEG + +;================== +; +; ShiftPropChar +; +; Call with BX = character number (0-255) +; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts +; them to the new position +; +;================== + +PROC ShiftPropChar NEAR + + mov es,[grsegs+STARTFONT*2] ;segment of font to use + +; +; find character location, width, and height +; + mov si,[es:charwidth+bx] + and si,0ffh ;SI hold width in pixels + shl bx,1 + mov bx,[es:charloc+bx] ;BX holds pointer to character data + +; +; look up which shift table to use, based on bufferbit +; + mov di,[bufferbit] + shl di,1 + mov bp,[shifttabletable+di] ;BP holds pointer to shift table + + mov di,OFFSET databuffer + add di,[bufferbyte] ;DI holds pointer to buffer + + mov cx,[bufferbit] + add cx,si ;add twice because pixel == two bits + add cx,si ;new bit position + mov ax,cx + and ax,7 + mov [bufferbit],ax ;new bit position + mov ax,cx + shr ax,1 + shr ax,1 + shr ax,1 + add [bufferbyte],ax ;new byte position + + add si,3 + shr si,1 + shr si,1 ;bytes the character is wide + shl si,1 ;*2 to look up in shiftdrawtable + + mov cx,[es:pcharheight] + mov dx,BUFFWIDTH + jmp [ss:shiftdrawtable+si] ;procedure to draw this width + +; +; one byte character +; +shift1wide: + dec dx +EVEN +@@loop1: + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop1 + + ret + +; +; two byte character +; +shift2wide: + dec dx + dec dx +EVEN +@@loop2: + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop2 + + ret + +; +; three byte character +; +shift3wide: + sub dx,3 +EVEN +@@loop3: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop3 + + ret + + +; +; four byte character +; +shift4wide: + sub dx,4 +EVEN +@@loop4: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop4 + + ret + + +; +; five byte character +; +shift5wide: + sub dx,5 +EVEN +@@loop5: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop5 + + ret + +; +; six byte character +; +shift6wide: + sub dx,6 +EVEN +@@loop6: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + + loop @@loop6 + + ret + + + +ENDP + +;============================================================================ + +;================== +; +; VW_DrawPropString +; +; Draws a C string of characters at px/py and advances px +; +;================== + +CODESEG + +PROC VW_DrawPropString string:DWORD +PUBLIC VW_DrawPropString +USES SI,DI + +; +; proportional spaceing, which clears the buffer ahead of it, so only +; clear the first collumn +; + mov al,0 +line = 0 +REPT BUFFHEIGHT + mov [BYTE databuffer+BUFFWIDTH*line],al +line = line+1 +ENDM + +; +; shift the characters into the buffer +; +@@shiftchars: + mov ax,[px] + and ax,3 + shl ax,1 ;one pixel == two bits + mov [bufferbit],ax + mov [bufferbyte],0 + + mov ax,[WORD string] + mov [stringptr],ax + mov ax,[WORD string+2] + mov [stringptr+2],ax + +@@shiftone: + mov es,[stringptr+2] + mov bx,[stringptr] + inc [stringptr] + mov bx,[es:bx] + xor bh,bh + or bl,bl + jz @@allshifted + call ShiftPropChar + jmp @@shiftone + +@@allshifted: +; +; calculate position to draw buffer on screen +; + mov bx,[py] + shl bx,1 + mov di,[ylookup+bx] + add di,[bufferofs] + add di,[panadjust] + + mov ax,[px] + shr ax,1 + shr ax,1 ;x location in bytes + add di,ax + mov [screenspot],di + +; +; advance px +; + mov ax,[bufferbyte] + shl ax,1 + shl ax,1 + mov bx,[bufferbit] + shr bx,1 ;two bits == one pixel + or ax,bx + add [px],ax + +; +; draw it +; + mov ax,[bufferbyte] + test [bufferbit],7 + jz @@go + inc ax ;so the partial byte also gets drawn +@@go: + mov [bufferwidth],ax + mov es,[grsegs+STARTFONT*2] + mov ax,[es:pcharheight] + mov [bufferheight],ax + + mov si,OFFSET databuffer + call VWL_XORBuffer + + ret + +ENDP + +endif ;numfont + +;============================================================================ +; +; MASKED FONT DRAWING ROUTINES +; +;============================================================================ + +if numfontm + +DATASEG + +mshiftdrawtable dw 0,mshift1wide,mshift2wide,mshift3wide + + +CODESEG + +;================== +; +; ShiftMPropChar +; +; Call with BX = character number (0-255) +; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts +; them to the new position +; +;================== + +PROC ShiftMPropChar NEAR + + mov es,[grsegs+STARTFONTM*2] ;segment of font to use + +; +; find character location, width, and height +; + mov si,[es:charwidth+bx] + and si,0ffh ;SI hold width in pixels + shl bx,1 + mov bx,[es:charloc+bx] ;BX holds pointer to character data + +; +; look up which shift table to use, based on bufferbit +; + mov di,[bufferbit] + shl di,1 + mov bp,[shifttabletable+di] ;BP holds pointer to shift table + + mov di,OFFSET databuffer + add di,[bufferbyte] ;DI holds pointer to buffer + +; +; advance position by character width +; + mov cx,[bufferbit] + add cx,si ;new bit position + mov ax,cx + and ax,7 + mov [bufferbit],ax ;new bit position + mov ax,cx + shr ax,1 + shr ax,1 + shr ax,1 + add [bufferbyte],ax ;new byte position + + add si,7 + shr si,1 + shr si,1 + shr si,1 ;bytes the character is wide + shl si,1 ;*2 to look up in shiftdrawtable + + mov cx,[es:pcharheight] + mov dx,BUFFWIDTH + jmp [ss:mshiftdrawtable+si] ;procedure to draw this width + +; +; one byte character +; +mshift1wide: + dec dx + +EVEN +@@loop1m: + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop1m + + mov cx,[es:pcharheight] + +EVEN +@@loop1: + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop1 + + ret + +; +; two byte character +; +mshift2wide: + dec dx + dec dx +EVEN +@@loop2m: + SHIFTWITHXOR + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop2m + + mov cx,[es:pcharheight] + +EVEN +@@loop2: + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop2 + + ret + +; +; three byte character +; +mshift3wide: + sub dx,3 +EVEN +@@loop3m: + SHIFTWITHXOR + SHIFTWITHXOR + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop3m + + mov cx,[es:pcharheight] + +EVEN +@@loop3: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop3 + + ret + + +ENDP + +;============================================================================ + +;================== +; +; VW_DrawMPropString +; +; Draws a C string of characters at px/py and advances px +; +;================== + + + +PROC VW_DrawMPropString string:DWORD +PUBLIC VW_DrawMPropString +USES SI,DI + +; +; clear out the first byte of the buffer, the rest will automatically be +; cleared as characters are drawn into it +; + mov es,[grsegs+STARTFONTM*2] + mov dx,[es:pcharheight] + mov di,OFFSET databuffer + mov ax,ds + mov es,ax + mov bx,BUFFWIDTH-1 + + mov cx,dx + mov al,0ffh +@@maskfill: + stosb ; fill the mask part with $ff + add di,bx + loop @@maskfill + + mov cx,dx + xor al,al +@@datafill: + stosb ; fill the data part with $0 + add di,bx + loop @@datafill + +; +; shift the characters into the buffer +; + mov ax,[px] + and ax,7 + mov [bufferbit],ax + mov [bufferbyte],0 + + mov ax,[WORD string] + mov [stringptr],ax + mov ax,[WORD string+2] + mov [stringptr+2],ax + +@@shiftone: + mov es,[stringptr+2] + mov bx,[stringptr] + inc [stringptr] + mov bx,[es:bx] + xor bh,bh + or bl,bl + jz @@allshifted + call ShiftMPropChar + jmp @@shiftone + +@@allshifted: +; +; calculate position to draw buffer on screen +; + mov bx,[py] + shl bx,1 + mov di,[ylookup+bx] + add di,[bufferofs] + + mov ax,[px] + shr ax,1 + shr ax,1 + shr ax,1 ;x location in bytes + add di,ax + mov [screenspot],di + +; +; advance px +; + mov ax,[bufferbyte] + shl ax,1 + shl ax,1 + shl ax,1 + or ax,[bufferbit] + add [px],ax + +; +; draw it +; + mov ax,[bufferbyte] + test [bufferbit],7 + jz @@go + inc ax ;so the partial byte also gets drawn +@@go: + mov [bufferwidth],ax + mov es,[grsegs+STARTFONTM*2] + mov ax,[es:pcharheight] + mov [bufferheight],ax + + mov si,OFFSET databuffer + call BufferToScreen ; cut out mask + ; or in data + call BufferToScreen ; SI is still in the right position in buffer + + ret + +ENDP + +endif ; if numfontm + +endif ; if fonts diff --git a/ID_VW_AE.ASM b/ID_VW_AE.ASM new file mode 100644 index 0000000..13a7434 --- /dev/null +++ b/ID_VW_AE.ASM @@ -0,0 +1,1752 @@ +; 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. + +;================================= +; +; EGA view manager routines +; +;================================= + +;============================================================================ +; +; All EGA drawing routines that write out words need to have alternate forms +; for starting on even and odd addresses, because writing a word at segment +; offset 0xffff causes an exception! To work around this, write a single +; byte out to make the address even, so it wraps cleanly at the end. +; +; All of these routines assume read/write mode 0, and will allways return +; in that state. +; The direction flag should be clear +; readmap/writemask is left in an undefined state +; +;============================================================================ + + +;============================================================================ +; +; VW_Plot (int x,y,color) +; +;============================================================================ + +DATASEG + +plotpixels db 128,64,32,16,8,4,2,1 + +CODESEG + +PROC VW_Plot x:WORD, y:WORD, color:WORD +PUBLIC VW_Plot +USES SI,DI + + mov es,[screenseg] + + mov dx,SC_INDEX + mov ax,SC_MAPMASK+15*256 + WORDOUT + + mov dx,GC_INDEX + mov ax,GC_MODE+2*256 ;write mode 2 + WORDOUT + + mov di,[bufferofs] + mov bx,[y] + shl bx,1 + add di,[ylookup+bx] + mov bx,[x] + mov ax,bx + shr ax,1 + shr ax,1 + shr ax,1 + add di,ax ; di = byte on screen + + and bx,7 + mov ah,[plotpixels+bx] + mov al,GC_BITMASK ;mask off other pixels + WORDOUT + + mov bl,[BYTE color] + xchg bl,[es:di] ; load latches and write pixel + + mov dx,GC_INDEX + mov ah,0ffh ;no bit mask + WORDOUT + mov ax,GC_MODE+0*256 ;write mode 0 + WORDOUT + + ret + +ENDP + + +;============================================================================ +; +; VW_Vlin (int yl,yh,x,color) +; +;============================================================================ + +PROC VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD +PUBLIC VW_Vlin +USES SI,DI + + mov es,[screenseg] + + mov dx,SC_INDEX + mov ax,SC_MAPMASK+15*256 + WORDOUT + + mov dx,GC_INDEX + mov ax,GC_MODE+2*256 ;write mode 2 + WORDOUT + + mov di,[bufferofs] + mov bx,[yl] + shl bx,1 + add di,[ylookup+bx] + mov bx,[x] + mov ax,bx + shr ax,1 + shr ax,1 + shr ax,1 + add di,ax ; di = byte on screen + + and bx,7 + mov ah,[plotpixels+bx] + mov al,GC_BITMASK ;mask off other pixels + WORDOUT + + mov cx,[yh] + sub cx,[yl] + inc cx ;number of pixels to plot + + mov bh,[BYTE color] + mov dx,[linewidth] + +@@plot: + mov bl,bh + xchg bl,[es:di] ; load latches and write pixel + add di,dx + + loop @@plot + + mov dx,GC_INDEX + mov ah,0ffh ;no bit mask + WORDOUT + mov ax,GC_MODE+0*256 ;write mode 0 + WORDOUT + + ret + +ENDP + + +;============================================================================ + + +;=================== +; +; VW_DrawTile8 +; +; xcoord in bytes (8 pixels), ycoord in pixels +; All Tile8s are in one grseg, so an offset is calculated inside it +; +;=================== + +PROC VW_DrawTile8 xcoord:WORD, ycoord:WORD, tile:WORD +PUBLIC VW_DrawTile8 +USES SI,DI + + mov es,[screenseg] + + mov di,[bufferofs] + add di,[xcoord] + mov bx,[ycoord] + shl bx,1 + add di,[ylookup+bx] + mov [ss:screendest],di ;screen destination + + mov bx,[linewidth] + dec bx + + mov si,[tile] + shl si,1 + shl si,1 + shl si,1 + shl si,1 + shl si,1 + + mov ds,[grsegs+STARTTILE8*2] ; segment for all tile8s + + mov cx,4 ;planes to draw + mov ah,0001b ;map mask + + mov dx,SC_INDEX + mov al,SC_MAPMASK + +; +; start drawing +; + +@@planeloop: + WORDOUT + shl ah,1 ;shift plane mask over for next plane + + mov di,[ss:screendest] ;start at same place in all planes + +REPT 7 + movsb + add di,bx +ENDM + movsb + + loop @@planeloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VW_MaskBlock +; +; Draws a masked block shape to the screen. bufferofs is NOT accounted for. +; The mask comes first, then four planes of data. +; +;============================================================================ + +DATASEG + +UNWOUNDMASKS = 10 + + +maskroutines dw mask0,mask0,mask1E,mask1E,mask2E,mask2O,mask3E,mask3O + dw mask4E,mask4O,mask5E,mask5O,mask6E,mask6O + dw mask7E,mask7O,mask8E,mask8O,mask9E,mask9O + dw mask10E,mask10O + + +routinetouse dw ? + +CODESEG + +PROC VW_MaskBlock segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD +PUBLIC VW_MaskBlock +USES SI,DI + + mov es,[screenseg] + + mov [BYTE planemask],1 + mov [BYTE planenum],0 + + mov di,[wide] + mov dx,[linewidth] + sub dx,[wide] + mov [linedelta],dx ;amount to add after drawing each line + + mov bx,[planesize] ; si+bx = data location + + cmp di,UNWOUNDMASKS + jbe @@unwoundroutine + mov [routinetouse],OFFSET generalmask + jmp NEAR @@startloop + +;================= +; +; use the unwound routines +; +;================= + +@@unwoundroutine: + mov cx,[dest] + shr cx,1 + rcl di,1 ;shift a 1 in if destination is odd + shl di,1 ;to index into a word width table + mov ax,[maskroutines+di] ;call the right routine + mov [routinetouse],ax + +@@startloop: + mov ds,[segm] + +@@drawplane: + mov dx,SC_INDEX + mov al,SC_MAPMASK + mov ah,[ss:planemask] + WORDOUT + mov dx,GC_INDEX + mov al,GC_READMAP + mov ah,[ss:planenum] + WORDOUT + + mov si,[ofs] ;start back at the top of the mask + mov di,[dest] ;start at same place in all planes + mov cx,[height] ;scan lines to draw + mov dx,[ss:linedelta] + + jmp [ss:routinetouse] ;draw one plane +planereturn: ;routine jmps back here + + add bx,[ss:planesize] ;start of mask = start of next plane + + inc [ss:planenum] + shl [ss:planemask],1 ;shift plane mask over for next plane + cmp [ss:planemask],10000b ;done all four planes? + jne @@drawplane + +mask0: + mov ax,ss + mov ds,ax + ret ;width of 0 = no drawing + +;============== +; +; General purpose masked block drawing. This could be optimised into +; four routines to use words, but few play loop sprites should be this big! +; +;============== + +generalmask: + mov dx,cx + +@@lineloopgen: + mov cx,[wide] +@@byteloop: + mov al,[es:di] + and al,[si] + or al,[bx+si] + inc si + stosb + loop @@byteloop + + add di,[ss:linedelta] + dec dx + jnz @@lineloopgen + jmp planereturn + +;================= +; +; Horizontally unwound routines to draw certain masked blocks faster +; +;================= + +MACRO MASKBYTE + mov al,[es:di] + and al,[si] + or al,[bx+si] + inc si + stosb +ENDM + +MACRO MASKWORD + mov ax,[es:di] + and ax,[si] + or ax,[bx+si] + inc si + inc si + stosw +ENDM + +MACRO SPRITELOOP addr + add di,dx + loop addr + jmp planereturn +ENDM + + +EVEN +mask1E: + MASKBYTE + SPRITELOOP mask1E + +EVEN +mask2E: + MASKWORD + SPRITELOOP mask2E + +EVEN +mask2O: + MASKBYTE + MASKBYTE + SPRITELOOP mask2O + +EVEN +mask3E: + MASKWORD + MASKBYTE + SPRITELOOP mask3E + +EVEN +mask3O: + MASKBYTE + MASKWORD + SPRITELOOP mask3O + +EVEN +mask4E: + MASKWORD + MASKWORD + SPRITELOOP mask4E + +EVEN +mask4O: + MASKBYTE + MASKWORD + MASKBYTE + SPRITELOOP mask4O + +EVEN +mask5E: + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask5E + +EVEN +mask5O: + MASKBYTE + MASKWORD + MASKWORD + SPRITELOOP mask5O + +EVEN +mask6E: + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask6E + +EVEN +mask6O: + MASKBYTE + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask6O + +EVEN +mask7E: + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask7E + +EVEN +mask7O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask7O + +EVEN +mask8E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask8E + +EVEN +mask8O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask8O + +EVEN +mask9E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask9E + +EVEN +mask9O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask9O + +EVEN +mask10E: + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKWORD + SPRITELOOP mask10E + +EVEN +mask10O: + MASKBYTE + MASKWORD + MASKWORD + MASKWORD + MASKWORD + MASKBYTE + SPRITELOOP mask10O + + +ENDP + + +;============================================================================ +; +; VW_ScreenToScreen +; +; Basic block copy routine. Copies one block of screen memory to another, +; using write mode 1 (sets it and returns with write mode 0). bufferofs is +; NOT accounted for. +; +;============================================================================ + +PROC VW_ScreenToScreen source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_ScreenToScreen +USES SI,DI + + pushf + cli + + mov dx,SC_INDEX + mov ax,SC_MAPMASK+15*256 + WORDOUT + mov dx,GC_INDEX + mov ax,GC_MODE+1*256 + WORDOUT + + popf + + mov bx,[linewidth] + sub bx,[wide] + + mov ax,[screenseg] + mov es,ax + mov ds,ax + + mov si,[source] + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + mov ax,[wide] + +@@lineloop: + mov cx,ax + rep movsb + add si,bx + add di,bx + + dec dx + jnz @@lineloop + + mov dx,GC_INDEX + mov ax,GC_MODE+0*256 + WORDOUT + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VW_MemToScreen +; +; Basic block drawing routine. Takes a block shape at segment pointer source +; with four planes of width by height data, and draws it to dest in the +; virtual screen, based on linewidth. bufferofs is NOT accounted for. +; There are four drawing routines to provide the best optimized code while +; accounting for odd segment wrappings due to the floating screens. +; +;============================================================================ + +DATASEG + +memtoscreentable dw eventoeven,eventoodd,oddtoeven,oddtoodd + +CODESEG + + +PROC VW_MemToScreen source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_MemToScreen +USES SI,DI + + mov es,[screenseg] + + mov bx,[linewidth] + sub bx,[wide] + + mov ds,[source] + + + xor si,si ;block is segment aligned + + xor di,di + shr [wide],1 ;change wide to words, and see if carry is set + rcl di,1 ;1 if wide is odd + mov ax,[dest] + shr ax,1 + rcl di,1 ;shift a 1 in if destination is odd + shl di,1 ;to index into a word width table + mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0 + jmp [ss:memtoscreentable+di] ;call the right routine + +;============== +; +; Copy an even width block to an even video address +; +;============== + +eventoeven: + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloopEE: + mov cx,[wide] + rep movsw + + add di,bx + + dec dx + jnz @@lineloopEE + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne eventoeven + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an odd width block to an even video address +; +;============== + +oddtoeven: + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloopOE: + mov cx,[wide] + rep movsw + movsb ;copy the last byte + + add di,bx + + dec dx + jnz @@lineloopOE + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne oddtoeven + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an even width block to an odd video address +; +;============== + +eventoodd: + dec [wide] ;one word has to be handled seperately +EOplaneloop: + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloopEO: + movsb + mov cx,[wide] + rep movsw + movsb + + add di,bx + + dec dx + jnz @@lineloopEO + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne EOplaneloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +;============== +; +; Copy an odd width block to an odd video address +; +;============== + +oddtoodd: + mov dx,SC_INDEX + WORDOUT + + mov di,[dest] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloopOO: + movsb + mov cx,[wide] + rep movsw + + add di,bx + + dec dx + jnz @@lineloopOO + + shl ah,1 ;shift plane mask over for next plane + cmp ah,10000b ;done all four planes? + jne oddtoodd + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + + +ENDP + +;=========================================================================== +; +; VW_ScreenToMem +; +; Copies a block of video memory to main memory, in order from planes 0-3. +; This could be optimized along the lines of VW_MemToScreen to take advantage +; of word copies, but this is an infrequently called routine. +; +;=========================================================================== + +PROC VW_ScreenToMem source:WORD, dest:WORD, wide:WORD, height:WORD +PUBLIC VW_ScreenToMem +USES SI,DI + + mov es,[dest] + + mov bx,[linewidth] + sub bx,[wide] + + mov ds,[screenseg] + + mov ax,GC_READMAP ;read map for plane 0 + + xor di,di + +@@planeloop: + mov dx,GC_INDEX + WORDOUT + + mov si,[source] ;start at same place in all planes + mov dx,[height] ;scan lines to draw + +@@lineloop: + mov cx,[wide] + rep movsb + + add si,bx + + dec dx + jnz @@lineloop + + inc ah + cmp ah,4 ;done all four planes? + jne @@planeloop + + mov ax,ss + mov ds,ax ;restore turbo's data segment + + ret + +ENDP + + +;============================================================================ +; +; VWL_UpdateScreenBlocks +; +; Scans through the update matrix and copies any areas that have changed +; to the visable screen, then zeros the update array +; +;============================================================================ + + + +; AX 0/1 for scasb, temp for segment register transfers +; BX width for block copies +; CX REP counter +; DX line width deltas +; SI source for copies +; DI scas dest / movsb dest +; BP pointer to end of bufferblocks + +PROC VWL_UpdateScreenBlocks +PUBLIC VWL_UpdateScreenBlocks +USES SI,DI,BP + + jmp SHORT @@realstart +@@done: +; +; all tiles have been scanned +; + mov dx,GC_INDEX ; restore write mode 0 + mov ax,GC_MODE+0*256 + WORDOUT + + xor ax,ax ; clear out the update matrix + mov cx,UPDATEWIDE*UPDATEHIGH/2 + + mov di,[updateptr] + rep stosw + + ret + +@@realstart: + mov dx,SC_INDEX + mov ax,SC_MAPMASK+15*256 + WORDOUT + mov dx,GC_INDEX + mov ax,GC_MODE+1*256 + WORDOUT + + mov di,[updateptr] ; start of floating update screen + mov bp,di + add bp,UPDATEWIDE*UPDATEHIGH+1 ; when di = bp, all tiles have been scanned + + push di + mov cx,-1 ; definately scan the entire thing + +; +; scan for a 1 in the update list, meaning a tile needs to be copied +; from the master screen to the current screen +; +@@findtile: + pop di ; place to continue scaning from + mov ax,ss + mov es,ax ; search in the data segment + mov ds,ax + mov al,1 + repne scasb + cmp di,bp + jae @@done + + cmp [BYTE di],al + jne @@singletile + jmp @@tileblock + +;============ +; +; copy a single tile +; +;============ +@@singletile: + inc di ; we know the next tile is nothing + push di ; save off the spot being scanned + sub di,[updateptr] + shl di,1 + mov di,[blockstarts-4+di] ; start of tile location on screen + mov si,di + add si,[bufferofs] + add di,[displayofs] + + mov dx,[linewidth] + sub dx,2 + mov ax,[screenseg] + mov ds,ax + mov es,ax + +REPT 15 + movsb + movsb + add si,dx + add di,dx +ENDM + movsb + movsb + + jmp @@findtile + +;============ +; +; more than one tile in a row needs to be updated, so do it as a group +; +;============ +EVEN +@@tileblock: + mov dx,di ; hold starting position + 1 in dx + inc di ; we know the next tile also gets updated + repe scasb ; see how many more in a row + push di ; save off the spot being scanned + + mov bx,di + sub bx,dx ; number of tiles in a row + shl bx,1 ; number of bytes / row + + mov di,dx ; lookup position of start tile + sub di,[updateptr] + shl di,1 + mov di,[blockstarts-2+di] ; start of tile location + mov si,di + add si,[bufferofs] + add di,[displayofs] + + mov dx,[linewidth] + sub dx,bx ; offset to next line on screen + + mov ax,[screenseg] + mov ds,ax + mov es,ax + +REPT 15 + mov cx,bx + rep movsb + add si,dx + add di,dx +ENDM + mov cx,bx + rep movsb + + dec cx ; was 0 from last rep movsb, now $ffff for scasb + jmp @@findtile + +ENDP + + +;=========================================================================== +; +; MISC EGA ROUTINES +; +;=========================================================================== + +;============== +; +; VW_SetScreen +; +;============== + +PROC VW_SetScreen crtc:WORD, pel:WORD +PUBLIC VW_SetScreen + +if waitforvbl + + mov dx,STATUS_REGISTER_1 + +; +; wait util the CRTC just starts scaning a diplayed line to set the CRTC start +; + cli + +@@waitnodisplay: + in al,dx + test al,1 + jz @@waitnodisplay + +; the display is now disabled (in a HBL / VBL) + +@@waitdisplay: + in al,dx + test al,1 ;1 = display is disabled (HBL / VBL) + jnz @@waitdisplay + +; the display was just enabled, so a full scan line is available for CRTC set + +endif + +; +; set CRTC start +; +; for some reason, my XT's EGA card doesn't like word outs to the CRTC +; index... +; + mov cx,[crtc] + mov dx,CRTC_INDEX + mov al,0ch ;start address high register + out dx,al + inc dx + mov al,ch + out dx,al + dec dx + mov al,0dh ;start address low register + out dx,al + mov al,cl + inc dx + out dx,al + +if waitforvbl + +; +; wait for a vertical retrace to set pel panning +; + mov dx,STATUS_REGISTER_1 +@@waitvbl: + sti ;service interrupts + jmp $+2 + cli + in al,dx + test al,00001000b ;look for vertical retrace + jz @@waitvbl + +endif + +; +; set horizontal panning +; + + mov dx,ATR_INDEX + mov al,ATR_PELPAN or 20h + out dx,al + jmp $+2 + mov al,[BYTE pel] ;pel pan value + out dx,al + + sti + + ret + +ENDP + + +if NUMFONT+NUMFONTM + +;=========================================================================== +; +; GENERAL FONT DRAWING ROUTINES +; +;=========================================================================== + +DATASEG + +px dw ? ; proportional character drawing coordinates +py dw ? +pdrawmode db 11000b ; 8 = OR, 24 = XOR, put in GC_DATAROTATE +fontcolor db 15 ;0-15 mapmask value + +PUBLIC px,py,pdrawmode,fontcolor + +; +; offsets in font structure +; +pcharheight = 0 ;lines high +charloc = 2 ;pointers to every character +charwidth = 514 ;every character's width in pixels + + +propchar dw ? ; the character number to shift +stringptr dw ?,? + + +BUFFWIDTH = 50 +BUFFHEIGHT = 32 ; must be twice as high as font for masked fonts + +databuffer db BUFFWIDTH*BUFFHEIGHT dup (?) + +bufferwidth dw ? ; bytes with valid info / line +bufferheight dw ? ; number of lines currently used + +bufferbyte dw ? +bufferbit dw ? + +screenspot dw ? ; where the buffer is going + +bufferextra dw ? ; add at end of a line copy +screenextra dw ? + +PUBLIC bufferwidth,bufferheight,screenspot + +CODESEG + +;====================== +; +; Macros to table shift a byte of font +; +;====================== + +MACRO SHIFTNOXOR + mov al,[es:bx] ; source + xor ah,ah + shl ax,1 + mov si,ax + mov ax,[bp+si] ; table shift into two bytes + or [di],al ; or with first byte + inc di + mov [di],ah ; replace next byte + inc bx ; next source byte +ENDM + +MACRO SHIFTWITHXOR + mov al,[es:bx] ; source + xor ah,ah + shl ax,1 + mov si,ax + mov ax,[bp+si] ; table shift into two bytes + not ax + and [di],al ; and with first byte + inc di + mov [di],ah ; replace next byte + inc bx ; next source byte +ENDM + + +;======================= +; +; BufferToScreen +; +; Pass buffer start in SI (somewhere in databuffer) +; Draws the buffer to the EGA screen in the current write mode +; +;======================== + +PROC BufferToScreen NEAR + + mov es,[screenseg] + mov di,[screenspot] + + mov bx,[bufferwidth] ;calculate offsets for end of each line + or bx,bx + jnz @@isthere + ret ;nothing to draw + +@@isthere: + mov ax,[linewidth] + sub ax,bx + mov [screenextra],ax + mov ax,BUFFWIDTH + sub ax,bx + mov [bufferextra],ax + + mov bx,[bufferheight] ;lines to copy +@@lineloop: + mov cx,[bufferwidth] ;bytes to copy +@@byteloop: + lodsb ;get a byte from the buffer + xchg [es:di],al ;load latches and store back to screen + inc di + + loop @@byteloop + + add si,[bufferextra] + add di,[screenextra] + + dec bx + jnz @@lineloop + + ret +ENDP + + +;============================================================================ +; +; NON MASKED FONT DRAWING ROUTINES +; +;============================================================================ + +if numfont + +DATASEG + +shiftdrawtable dw 0,shift1wide,shift2wide,shift3wide,shift4wide + dw shift5wide + +CODESEG + +;================== +; +; ShiftPropChar +; +; Call with BX = character number (0-255) +; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts +; them to the new position +; +;================== + +PROC ShiftPropChar NEAR + + mov si,[fontnumber] + shl si,1 + mov es,[grsegs+STARTFONT*2+si] ;segment of font to use + +; +; find character location, width, and height +; + mov si,[es:charwidth+bx] + and si,0ffh ;SI hold width in pixels + shl bx,1 + mov bx,[es:charloc+bx] ;BX holds pointer to character data + +; +; look up which shift table to use, based on bufferbit +; + mov di,[bufferbit] + shl di,1 + mov bp,[shifttabletable+di] ;BP holds pointer to shift table + + mov di,OFFSET databuffer + add di,[bufferbyte] ;DI holds pointer to buffer + +; +; advance position by character width +; + mov cx,[bufferbit] + add cx,si ;new bit position + mov ax,cx + and ax,7 + mov [bufferbit],ax ;new bit position + mov ax,cx + shr ax,1 + shr ax,1 + shr ax,1 + add [bufferbyte],ax ;new byte position + + add si,7 + shr si,1 + shr si,1 + shr si,1 ;bytes the character is wide + shl si,1 ;*2 to look up in shiftdrawtable + + mov cx,[es:pcharheight] + mov dx,BUFFWIDTH + jmp [ss:shiftdrawtable+si] ;procedure to draw this width + +; +; one byte character +; +shift1wide: + dec dx +EVEN +@@loop1: + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop1 + ret + +; +; two byte character +; +shift2wide: + dec dx + dec dx +EVEN +@@loop2: + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop2 + ret + +; +; three byte character +; +shift3wide: + sub dx,3 +EVEN +@@loop3: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop3 + ret + +; +; four byte character +; +shift4wide: + sub dx,4 +EVEN +@@loop4: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop4 + ret + +; +; five byte character +; +shift5wide: + sub dx,5 +EVEN +@@loop5: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop5 + ret + + + +ENDP + +;============================================================================ + +;================== +; +; VW_DrawPropString +; +; Draws a C string of characters at px/py and advances px +; +; Assumes write mode 0 +; +;================== + +CODESEG + +PROC VW_DrawPropString string:DWORD +PUBLIC VW_DrawPropString +USES SI,DI + +; +; proportional spaceing, which clears the buffer ahead of it, so only +; clear the first collumn +; + mov al,0 +line = 0 +REPT BUFFHEIGHT + mov [BYTE databuffer+BUFFWIDTH*line],al +line = line+1 +ENDM + +; +; shift the characters into the buffer +; +@@shiftchars: + mov ax,[px] + and ax,7 + mov [bufferbit],ax + mov [bufferbyte],0 + + mov ax,[WORD string] + mov [stringptr],ax + mov ax,[WORD string+2] + mov [stringptr+2],ax + +@@shiftone: + mov es,[stringptr+2] + mov bx,[stringptr] + inc [stringptr] + mov bx,[es:bx] + xor bh,bh + or bl,bl + jz @@allshifted + call ShiftPropChar + jmp @@shiftone + +@@allshifted: +; +; calculate position to draw buffer on screen +; + mov bx,[py] + shl bx,1 + mov di,[ylookup+bx] + add di,[bufferofs] + add di,[panadjust] + + mov ax,[px] + shr ax,1 + shr ax,1 + shr ax,1 ;x location in bytes + add di,ax + mov [screenspot],di + +; +; advance px +; + mov ax,[bufferbyte] + shl ax,1 + shl ax,1 + shl ax,1 + or ax,[bufferbit] + add [px],ax + +; +; draw it +; + +; set xor/or mode + mov dx,GC_INDEX + mov al,GC_DATAROTATE + mov ah,[pdrawmode] + WORDOUT + +; set mapmask to color + mov dx,SC_INDEX + mov al,SC_MAPMASK + mov ah,[fontcolor] + WORDOUT + + mov ax,[bufferbyte] + test [bufferbit],7 + jz @@go + inc ax ;so the partial byte also gets drawn +@@go: + mov [bufferwidth],ax + mov si,[fontnumber] + shl si,1 + mov es,[grsegs+STARTFONT*2+si] + mov ax,[es:pcharheight] + mov [bufferheight],ax + + mov si,OFFSET databuffer + call BufferToScreen + +; set copy mode + mov dx,GC_INDEX + mov ax,GC_DATAROTATE + WORDOUT + +; set mapmask to all + mov dx,SC_INDEX + mov ax,SC_MAPMASK + 15*256 + WORDOUT + + + ret + +ENDP + +endif ;numfont + +;============================================================================ +; +; MASKED FONT DRAWING ROUTINES +; +;============================================================================ + +if numfontm + +DATASEG + +mshiftdrawtable dw 0,mshift1wide,mshift2wide,mshift3wide + + +CODESEG + +;================== +; +; ShiftMPropChar +; +; Call with BX = character number (0-255) +; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts +; them to the new position +; +;================== + +PROC ShiftMPropChar NEAR + + mov si,[fontnumber] + shl si,1 + mov es,[grsegs+STARTFONTM*2+si] ;segment of font to use + +; +; find character location, width, and height +; + mov si,[es:charwidth+bx] + and si,0ffh ;SI hold width in pixels + shl bx,1 + mov bx,[es:charloc+bx] ;BX holds pointer to character data + +; +; look up which shift table to use, based on bufferbit +; + mov di,[bufferbit] + shl di,1 + mov bp,[shifttabletable+di] ;BP holds pointer to shift table + + mov di,OFFSET databuffer + add di,[bufferbyte] ;DI holds pointer to buffer + + mov cx,[bufferbit] + add cx,si ;new bit position + mov ax,cx + and ax,7 + mov [bufferbit],ax ;new bit position + mov ax,cx + shr ax,1 + shr ax,1 + shr ax,1 + add [bufferbyte],ax ;new byte position + + add si,7 + shr si,1 + shr si,1 + shr si,1 ;bytes the character is wide + shl si,1 ;*2 to look up in shiftdrawtable + + mov cx,[es:pcharheight] + mov dx,BUFFWIDTH + jmp [ss:mshiftdrawtable+si] ;procedure to draw this width + +; +; one byte character +; +mshift1wide: + dec dx + +EVEN +@@loop1m: + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop1m + + mov cx,[es:pcharheight] + +EVEN +@@loop1: + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop1 + + ret + +; +; two byte character +; +mshift2wide: + dec dx + dec dx +EVEN +@@loop2m: + SHIFTWITHXOR + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop2m + + mov cx,[es:pcharheight] + +EVEN +@@loop2: + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop2 + + ret + +; +; three byte character +; +mshift3wide: + sub dx,3 +EVEN +@@loop3m: + SHIFTWITHXOR + SHIFTWITHXOR + SHIFTWITHXOR + add di,dx ; next line in buffer + + loop @@loop3m + + mov cx,[es:pcharheight] + +EVEN +@@loop3: + SHIFTNOXOR + SHIFTNOXOR + SHIFTNOXOR + add di,dx ; next line in buffer + loop @@loop3 + + ret + + +ENDP + +;============================================================================ + +;================== +; +; VW_DrawMPropString +; +; Draws a C string of characters at px/py and advances px +; +; Assumes write mode 0 +; +;================== + + + +PROC VW_DrawMPropString string:DWORD +PUBLIC VW_DrawMPropString +USES SI,DI + +; +; clear out the first byte of the buffer, the rest will automatically be +; cleared as characters are drawn into it +; + mov si,[fontnumber] + shl si,1 + mov es,[grsegs+STARTFONTM*2+si] + mov dx,[es:pcharheight] + mov di,OFFSET databuffer + mov ax,ds + mov es,ax + mov bx,BUFFWIDTH-1 + + mov cx,dx + mov al,0ffh +@@maskfill: + stosb ; fill the mask part with $ff + add di,bx + loop @@maskfill + + mov cx,dx + xor al,al +@@datafill: + stosb ; fill the data part with $0 + add di,bx + loop @@datafill + +; +; shift the characters into the buffer +; + mov ax,[px] + and ax,7 + mov [bufferbit],ax + mov [bufferbyte],0 + + mov ax,[WORD string] + mov [stringptr],ax + mov ax,[WORD string+2] + mov [stringptr+2],ax + +@@shiftone: + mov es,[stringptr+2] + mov bx,[stringptr] + inc [stringptr] + mov bx,[es:bx] + xor bh,bh + or bl,bl + jz @@allshifted + call ShiftMPropChar + jmp @@shiftone + +@@allshifted: +; +; calculate position to draw buffer on screen +; + mov bx,[py] + shl bx,1 + mov di,[ylookup+bx] + add di,[bufferofs] + add di,[panadjust] + + mov ax,[px] + shr ax,1 + shr ax,1 + shr ax,1 ;x location in bytes + add di,ax + mov [screenspot],di + +; +; advance px +; + mov ax,[bufferbyte] + shl ax,1 + shl ax,1 + shl ax,1 + or ax,[bufferbit] + add [px],ax + +; +; draw it +; + mov ax,[bufferbyte] + test [bufferbit],7 + jz @@go + inc ax ;so the partial byte also gets drawn +@@go: + mov [bufferwidth],ax + mov es,[grsegs+STARTFONTM*2] + mov ax,[es:pcharheight] + mov [bufferheight],ax + +; set AND mode to punch out the mask + mov dx,GC_INDEX + mov ax,GC_DATAROTATE + 8*256 + WORDOUT + +; set mapmask to all + mov dx,SC_INDEX + mov ax,SC_MAPMASK + 15*256 + WORDOUT + + mov si,OFFSET databuffer + call BufferToScreen + +; set OR mode to fill in the color + mov dx,GC_INDEX + mov ax,GC_DATAROTATE + 16*256 + WORDOUT + +; set mapmask to color + mov dx,SC_INDEX + mov al,SC_MAPMASK + mov ah,[fontcolor] + WORDOUT + + call BufferToScreen ; SI is still in the right position in buffer + +; set copy mode + mov dx,GC_INDEX + mov ax,GC_DATAROTATE + WORDOUT + +; set mapmask to all + mov dx,SC_INDEX + mov ax,SC_MAPMASK + 15*256 + WORDOUT + + + ret + +ENDP + +endif ; if numfontm + +endif ; if fonts diff --git a/INTROSCN.OBJ b/INTROSCN.OBJ new file mode 100644 index 0000000000000000000000000000000000000000..4f49b24cb143c1a054b021787e4bb5a61fb20f71 GIT binary patch literal 4160 zcmds4O>YuW6n&%A*4S9qCQYAlVO@x6Yhqkdz)CBH6s($fbEPn)q!b7YSX^~!T)8qX zyL90~U75B&Ko|W5n9Sb)VB&c*Lxzu`3Z;!PH}B2+n)}Y&ckX$xcU4SAS5s^8WN0;j zB3@4kA!6xmJ-)as>vMs*pYQI9OW~!p_~XPFhl8o0PgP=!Q<0}BUnm}qOgsp#SuW3o#&eeHG)DoNM}3PVW1nI1k#r57RWC#( zDpP|hl&2hRk%6&U%BY>N-+kBuT1l+h#9S7us60PfNzGO@7l2VzMaeI9@EiK z@iYDBWhxI(;@?ntaAe7V&m+W&?oDkdr#;>dNu{!##-IVMgYa39yackvP^&h`-bR)X zEm5$o-#%xz;b*$3DQ{uCZJ=bVItxEhq)!(Kb zu5CuHHa)^{OpqCb@oZ$KM;KV$!!?M55(#Aq+;i#{D}y>|B%Tg=XeW-MPD^HDNZ%d2 zU*QI1%R?d{H%ic!AR0R!lF>;+C0q}&vH~nGt!J;u*!OD#4XMJfQJ5($O!j)T+1 z*NNNiC9yhfz3&R&F11Y33PiDCd91?U;4)+H5(K_Skgg2}Q#+EA@kM~8>>$(~VQ!(@ zg}2e*Ysb^w{xd!mZ*>CyB0l?TQjkA&&IK#}e-}Sb;o95(cu^CR9%=fgpVAkC;*H1no`k~RydU!fGuoEfkzfiWF3 yRn*daf|gn% literal 0 HcmV?d00001 diff --git a/JABHACK.ASM b/JABHACK.ASM new file mode 100644 index 0000000..6aaacd6 --- /dev/null +++ b/JABHACK.ASM @@ -0,0 +1,115 @@ +; 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. + +; JABHACK.ASM + +.386C +IDEAL +MODEL MEDIUM + +EXTRN LDIV@:far + +;============================================================================ + +DATASEG + +EXTRN _intaddr:word + +;============================================================================ + +CODESEG + +; Hacked up Juan Jimenez's code a bit to just return 386/not 386 +PROC _CheckIs386 +PUBLIC _CheckIs386 + + pushf ; Save flag registers, we use them here + xor ax,ax ; Clear AX and... + push ax ; ...push it onto the stack + popf ; Pop 0 into flag registers (all bits to 0), + pushf ; attempting to set bits 12-15 of flags to 0's + pop ax ; Recover the save flags + and ax,08000h ; If bits 12-15 of flags are set to + cmp ax,08000h ; zero then it's 8088/86 or 80188/186 + jz not386 + + mov ax,07000h ; Try to set flag bits 12-14 to 1's + push ax ; Push the test value onto the stack + popf ; Pop it into the flag register + pushf ; Push it back onto the stack + pop ax ; Pop it into AX for check + and ax,07000h ; if bits 12-14 are cleared then + jz not386 ; the chip is an 80286 + + mov ax,1 ; We now assume it's a 80386 or better + popf + retf + +not386: + xor ax,ax + popf + retf + + ENDP + + +PROC _jabhack2 +PUBLIC _jabhack2 + + jmp @@skip + +@@where: + int 060h + retf + +@@skip: + push es + + mov ax,seg LDIV@ + mov es,ax + mov ax,[WORD PTR @@where] + mov [WORD FAR es:LDIV@],ax + mov ax,[WORD PTR @@where+2] + mov [WORD FAR es:LDIV@+2],ax + + mov ax,offset @@jabdiv + mov [_intaddr],ax + mov ax,seg @@jabdiv + mov [_intaddr+2],ax + + pop es + retf + +@@jabdiv: + add sp,4 ;Nuke IRET address, but leave flags + push bp + mov bp,sp ;Save BP, and set it equal to stack + cli + + mov eax,[bp+8] + cdq + idiv [DWORD PTR bp+12] + mov edx,eax + shr edx,16 + + pop bp ;Restore BP + popf ;Restore flags (from INT) + retf 8 ;Return to original caller + + ENDP + + END diff --git a/MAPSC3D.H b/MAPSC3D.H new file mode 100644 index 0000000..fc6bbbc --- /dev/null +++ b/MAPSC3D.H @@ -0,0 +1,55 @@ +/* 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. + */ + +/////////////////////////////////////// +// +// TED5 Map Header for C3D +// +/////////////////////////////////////// + +// +// Map Names +// +typedef enum { + APPROACH_MAP, // 0 + ENTRANCE_MAP, // 1 + GROUND_FLOOR_MAP, // 2 + SECOND_FLOOR_MAP, // 3 + THIRD_FLOOR_MAP, // 4 + TOWER_1_MAP, // 5 + TOWER_2_MAP, // 6 + SECRET_HALLS_MAP, // 7 + ACCESS_FLOOR_MAP, // 8 + DUNGEON_MAP, // 9 + LOWER_DUNGEON_MAP, // 10 + CATACOMB_MAP, // 11 + LOWER_REACHES_MAP, // 12 + WARRENS_MAP, // 13 + HIDDEN_CAVERNS_MAP, // 14 + FENSOFINSANITY_MAP, // 15 + CHAOSCORRIDORS_MAP, // 16 + LABYRINTH_MAP, // 17 + HALLS_OF_BLOOD_MAP, // 18 + NEMESISSLAIR_MAP, // 19 + LASTMAP + } mapnames; + +// +// TILEINFO offsets +// +#define ANIM 402 diff --git a/NOTES.TXT b/NOTES.TXT new file mode 100644 index 0000000..edba410 --- /dev/null +++ b/NOTES.TXT @@ -0,0 +1,42 @@ +589: No mention of Id software has been removed. I thought this was +cleared up last project. I am proud of CAT3D, and I would have a problem +with the removal of credit to the development team. + +591: The version number is on the intro screen + +592: The new piracy screen is in + +660: The map view is a debugging tool, not a feature. + +682,694,695: The hang is fixed. Whenever a monster had no viable move it +went into a loop (the trolls were all added at the last minute). Good +catch. + +697: The graphic screw up was fixed by adding two blocks to the map + +Everything else: The single lines are a result of roundoff error between +two transformations. When a wall segment disapears it is a result of a +back trace hitting a side that should by invisable. The objects showing up +through walls is a result of the z buffer being kept in pixels (a poor +design decision). Not mentioned by you: The thick bar shown when you look +almost straight down a long wall is a result of the tangent table maxing out +in 16 bits, causing the texture mapping to think it can optimize the wall +drawing. The fish eye view when you turn right next to a wall is a result of +not having enough compiled scaler routines (memory constraints) to scale +thinks closer than the view plane. These problems are understood, but the +corrective measures are not really feasable. I think CAT3D still stands well +even with these warts. Over the past several days I have developed a ray +tracing/intersecting 3-D refresh to replace the image based system from +CAT3D, and it has addressed all of these problems. I would put it in CAT3D +except for: it uses 286 specific code in a few places, but no big deal +to replace. It is designed to work with a demand paged graphics caching +system, still no big deal. I am unsure of its memory requirements, +potentially a terminal problem. I haven't tested it with scaled sprites +yet, this would require one or two additional days. + +If gamer's edge ever gets an ok to do a hard drive installable game you will +probably see the new system. + + + John Carmack + diff --git a/README.md b/README.md new file mode 100644 index 0000000..307d3b2 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +Catacomb 3-D: The Descent +========================= + +This repository contains the source code for Catacomb 3-D (also known as +Catacombs 3 or Catacomb 3-D: A New Dimension). The source code is designed for +Borland C++ 2.0, but compiled fine with Borland C++ 3.1 at the time of this +release. + +It is released under the GNU GPLv2. Please see COPYING for license details. + +This release does not affect the licensing for the game data files. You will +need to legally acquire the game data in order to use the exe built from this +source code.