aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Mandel <richard@tech-transfer.com>2014-06-05 19:53:35 -0400
committerRichard Mandel <richard@tech-transfer.com>2014-06-05 19:53:35 -0400
commitaa97f4995dc7dbea3ecc1794a2d4d4354839cad4 (patch)
tree474a81ada5d700a87cc80536cff6d8d535fbd77d
downloadcatacombapocalypse-aa97f4995dc7dbea3ecc1794a2d4d4354839cad4.tar.gz
catacombapocalypse-aa97f4995dc7dbea3ecc1794a2d4d4354839cad4.tar.bz2
catacombapocalypse-aa97f4995dc7dbea3ecc1794a2d4d4354839cad4.zip
Catacomb 3D open source release.
-rw-r--r--AUDIOC3D.H86
-rw-r--r--C3DADICT.OBJbin0 -> 1151 bytes
-rw-r--r--C3DAHEAD.OBJbin0 -> 501 bytes
-rw-r--r--C3DEDICT.OBJbin0 -> 1149 bytes
-rw-r--r--C3DEHEAD.OBJbin0 -> 1579 bytes
-rw-r--r--C3DMHEAD.OBJbin0 -> 746 bytes
-rw-r--r--C3_ACT1.C1259
-rw-r--r--C3_ASM.ASM197
-rw-r--r--C3_DEBUG.C606
-rw-r--r--C3_DEF.H533
-rw-r--r--C3_DRAW.C1592
-rw-r--r--C3_GAME.C1199
-rw-r--r--C3_MAIN.C756
-rw-r--r--C3_PLAY.C584
-rw-r--r--C3_SCALE.C690
-rw-r--r--C3_SCA_A.ASM153
-rw-r--r--C3_STATE.C546
-rw-r--r--C3_TRACE.C872
-rw-r--r--C3_WIZ.C2046
-rw-r--r--CAT3D.PRJbin0 -> 11498 bytes
-rw-r--r--COPYING339
-rw-r--r--GFXE_C3D.EQU327
-rw-r--r--GFXE_C3D.H353
-rw-r--r--ID_ASM.EQU114
-rw-r--r--ID_CA.C2133
-rw-r--r--ID_CA.H124
-rw-r--r--ID_HEADS.H124
-rw-r--r--ID_IN.C1112
-rw-r--r--ID_IN.H208
-rw-r--r--ID_MM.C1130
-rw-r--r--ID_MM.H108
-rw-r--r--ID_RF.C2845
-rw-r--r--ID_RF.H153
-rw-r--r--ID_RF_A.ASM690
-rw-r--r--ID_SD.C1295
-rw-r--r--ID_SD.H205
-rw-r--r--ID_STRS.H128
-rw-r--r--ID_US.C3691
-rw-r--r--ID_US.H147
-rw-r--r--ID_US_1.C1285
-rw-r--r--ID_US_2.C1818
-rw-r--r--ID_US_A.ASM117
-rw-r--r--ID_VW.C1541
-rw-r--r--ID_VW.H370
-rw-r--r--ID_VW_A.ASM732
-rw-r--r--ID_VW_AC.ASM1485
-rw-r--r--ID_VW_AE.ASM1752
-rw-r--r--INTROSCN.OBJbin0 -> 4160 bytes
-rw-r--r--JABHACK.ASM115
-rw-r--r--MAPSC3D.H55
-rw-r--r--NOTES.TXT42
-rw-r--r--README.md13
52 files changed, 35670 insertions, 0 deletions
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 0000000..c2f9fda
--- /dev/null
+++ b/C3DADICT.OBJ
Binary files differ
diff --git a/C3DAHEAD.OBJ b/C3DAHEAD.OBJ
new file mode 100644
index 0000000..8906d30
--- /dev/null
+++ b/C3DAHEAD.OBJ
Binary files differ
diff --git a/C3DEDICT.OBJ b/C3DEDICT.OBJ
new file mode 100644
index 0000000..991491a
--- /dev/null
+++ b/C3DEDICT.OBJ
Binary files differ
diff --git a/C3DEHEAD.OBJ b/C3DEHEAD.OBJ
new file mode 100644
index 0000000..df8c6b2
--- /dev/null
+++ b/C3DEHEAD.OBJ
Binary files differ
diff --git a/C3DMHEAD.OBJ b/C3DMHEAD.OBJ
new file mode 100644
index 0000000..1607914
--- /dev/null
+++ b/C3DMHEAD.OBJ
Binary files differ
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)<NUMEXPWALLS)
+ ExplodeWall (ob->tilex-1,ob->tiley);
+ other = tilemap[ob->tilex+1][ob->tiley];
+ if ((unsigned)(other-EXPWALLSTART)<NUMEXPWALLS)
+ ExplodeWall (ob->tilex+1,ob->tiley);
+ other = tilemap[ob->tilex][ob->tiley-1];
+ if ((unsigned)(other-EXPWALLSTART)<NUMEXPWALLS)
+ ExplodeWall (ob->tilex,ob->tiley-1);
+ other = tilemap[ob->tilex][ob->tiley+1];
+ if ((unsigned)(other-EXPWALLSTART)<NUMEXPWALLS)
+ ExplodeWall (ob->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<<TILESHIFT)+TILEGLOBAL/2;
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+ move -= ob->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<<TILESHIFT)+TILEGLOBAL/2;
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+ move -= ob->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<<TILESHIFT)+TILEGLOBAL/2;
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+ move -= ob->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<NUMCHUNKS;i++)
+ {
+ if (grsegs[i])
+ {
+ strcpy (scratch,"Chunk:");
+ itoa (i,str,10);
+ strcat (scratch,str);
+ strcat (scratch,"\n");
+ write (debughandle,scratch,strlen(scratch));
+ }
+ }
+ CA_CloseDebug ();
+#endif
+
+ US_CPrint ("Memory Usage");
+ US_CPrint ("------------");
+ US_Print ("Total :");
+ US_PrintUnsigned (mminfo.mainmem/1024);
+ US_Print ("k\nFree :");
+ US_PrintUnsigned (MM_UnusedMemory()/1024);
+ US_Print ("k\nWith purge:");
+ US_PrintUnsigned (MM_TotalFree()/1024);
+ US_Print ("k\n");
+ VW_UpdateScreen();
+ IN_Ack ();
+}
+
+//===========================================================================
+
+/*
+================
+=
+= PicturePause
+=
+================
+*/
+
+void PicturePause (void)
+{
+ int y;
+ unsigned source;
+
+ source = displayofs+panadjust;
+
+ VW_ColorBorder (15);
+ VW_SetLineWidth (40);
+ VW_SetScreen (0,0);
+
+ if (source<0x10000l-200*64)
+ {
+ //
+ // copy top line first
+ //
+ for (y=0;y<200;y++)
+ VW_ScreenToScreen (source+y*64,y*40,40,1);
+ }
+ else
+ {
+ //
+ // copy bottom line first
+ //
+ for (y=199;y>=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<endy;y++)
+ for (x=maporgx;x<endx;x++)
+ {
+ sx = (x-maporgx)*2;
+ sy = (y-maporgy)*16;
+
+ switch (viewtype)
+ {
+ case mapview:
+ tile = *(mapsegs[0]+farmapylookup[y]+x);
+ break;
+
+ case tilemapview:
+ tile = tilemap[x][y];
+ break;
+
+ case actoratview:
+ tile = (unsigned)actorat[x][y];
+ break;
+
+ case visview:
+ tile = spotvis[x][y];
+ break;
+
+ }
+
+ if (tile<NUMTILE16)
+ LatchDrawTile(sx,sy,tile);
+ else
+ {
+ LatchDrawChar(sx,sy,NUMBERCHARS+((tile&0xf000)>>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 && maporgx<mapwidth-VIEWTILEX)
+ maporgx++;
+ if (c.yaxis == -1 && maporgy>0)
+ maporgy--;
+ if (c.yaxis == 1 && maporgy<mapheight-VIEWTILEY)
+ maporgy++;
+
+ if (c.button0 && !button0held)
+ {
+ button0held = true;
+ viewtype++;
+ if (viewtype>visview)
+ 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 <MATH.H>
+#include <VALUES.H>
+
+//#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 (xh<xl)
+ Quit("DrawLine: xh<xl");
+ if (y<VIEWY)
+ Quit("DrawLine: y<VIEWY");
+ if (y>VIEWYH)
+ 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 (xh<xl)
+ Quit("DrawLine: xh<xl");
+ if (y<VIEWY)
+ Quit("DrawLine: y<VIEWY");
+ if (y>VIEWYH)
+ 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<<TILESHIFT;
+ tracey = (long)oty<<TILESHIFT;
+ if (tracexstep>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;i<VIEWWIDTH/2;i++)
+ {
+ // start 1/2 pixel over, so viewangle bisects two middle pixels
+ x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;
+ tang = (float)x/(FOCALLENGTH+MINDIST);
+ angle = atan(tang);
+ intang = angle*radtoint;
+ pixelangle[VIEWWIDTH/2-1-i] = intang;
+ pixelangle[VIEWWIDTH/2+i] = -intang;
+ }
+
+//
+// calculate fine tangents
+// 1 sign bit, 5 units (clipped to), 10 fracs
+//
+#define MININT (-MAXINT)
+
+ for (i=0;i<FINEANGLES/4;i++)
+ {
+ intang = tan(i/radtoint)*(1l<<10);
+
+ //
+ // if the tangent is not reprentable in this many bits, bound the
+ // units part ONLY
+ //
+ if (intang>MAXINT)
+ intang = 0x8f00 | (intang & 0xff);
+ else if (intang<MININT)
+ intang = 0xff00 | (intang & 0xff);
+
+ finetangent[i] = intang;
+// finetangent[FINEANGLES/2+i] = intang;
+// finetangent[FINEANGLES/2-i-1] = -intang;
+ finetangent[FINEANGLES-i-1] = -intang;
+ }
+
+//
+// calculate scale value so one tile at mindist allmost fills the view horizontally
+//
+ scale = GLOBAL1/VIEWWIDTH;
+ scale *= focallength;
+ scale /= (focallength+mindist);
+
+//
+// costable overlays sintable with a quarter phase shift
+// ANGLES is assumed to be divisable by four
+//
+// The low word of the value is the fraction, the high bit is the sign bit,
+// bits 16-30 should be 0
+//
+
+ angle = 0;
+ anglestep = PI/2/ANGLEQUAD;
+ for (i=0;i<=ANGLEQUAD;i++)
+ {
+ value=GLOBAL1*sin(angle);
+ sintable[i]=
+ sintable[i+ANGLES]=
+ sintable[ANGLES/2-i] = value;
+ sintable[ANGLES-i]=
+ sintable[ANGLES/2+i] = value | 0x80000000l;
+ angle += anglestep;
+ }
+
+//
+// figure trace angles for first and last pixel on screen
+//
+ angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);
+ angle *= ANGLES/(PI*2);
+
+ intang = (int)angle+1;
+ firstangle = intang;
+ lastangle = -intang;
+
+ prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);
+
+//
+// misc stuff
+//
+ walls[0].x2 = VIEWX-1;
+ walls[0].height2 = 32000;
+}
+
+
+//==========================================================================
+
+/*
+=====================
+=
+= ClearScreen
+=
+=====================
+*/
+
+void ClearScreen (void)
+{
+ //
+ // clear the screen
+ //
+asm mov dx,GC_INDEX
+asm mov ax,GC_MODE + 256*2 // read mode 0, write mode 2
+asm out dx,ax
+asm mov ax,GC_BITMASK + 255*256
+asm out dx,ax
+
+asm mov dx,40-VIEWWIDTH/8
+asm mov bl,VIEWWIDTH/16
+asm mov bh,CENTERY+1
+
+asm xor ax,ax
+asm mov es,[screenseg]
+asm mov di,[bufferofs]
+
+toploop:
+asm mov cl,bl
+asm rep stosw
+asm stosb
+asm add di,dx
+asm dec bh
+asm jnz toploop
+
+asm mov bh,CENTERY+1
+asm mov ax,0x0808
+
+bottomloop:
+asm mov cl,bl
+asm rep stosw
+asm stosb
+asm add di,dx
+asm dec bh
+asm jnz bottomloop
+
+
+asm mov dx,GC_INDEX
+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
+=
+= Clips and draws all the walls traced this refresh
+=
+=====================
+*/
+
+void DrawWallList (void)
+{
+ int i,leftx,newleft,rightclip;
+ walltype *wall, *check;
+
+asm mov ax,ds
+asm mov es,ax
+asm mov di,OFFSET wallwidth
+asm xor ax,ax
+asm mov cx,VIEWWIDTH/2
+asm rep stosw
+
+ ClearScreen ();
+
+ rightwall->x1 = VIEWXH+1;
+ rightwall->height1 = 32000;
+ (rightwall+1)->x1 = 32000;
+
+ leftx = -1;
+
+ for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)
+ {
+ if (leftx >= 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; i<numvisable; i++)
+ {
+ least = 32000;
+ for (j=0;j<numvisable;j++)
+ {
+ height = depthsort[j]->viewheight;
+ 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 (TimeCount<oldtimecount+DEMOTICS*2)
+ ;
+ lasttimecount = oldtimecount + DEMOTICS;
+ TimeCount = lasttimecount + DEMOTICS;
+ tics = DEMOTICS;
+ }
+ else
+ {
+//
+// non demo, so report actual time
+//
+ newtime = TimeCount;
+ tics = newtime-lasttimecount;
+ lasttimecount = newtime;
+
+#ifdef FILEPROFILE
+ strcpy (scratch,"\tTics:");
+ itoa (tics,str,10);
+ strcat (scratch,str);
+ strcat (scratch,"\n");
+ write (profilehandle,scratch,strlen(scratch));
+#endif
+
+ if (tics>MAXTICS)
+ {
+ 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;y<mapheight;y++)
+ for (x=0;x<mapwidth;x++)
+ {
+ tile = *start++;
+ if (!tile)
+ continue;
+
+ switch (tile)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ lumpneeded[PLAYERLUMP] = true;
+ SpawnPlayer(x,y,NORTH+tile-1);
+ break;
+
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ lumpneeded[tile-5+BOLTLUMP] = true;
+ SpawnBonus(x,y,tile-5);
+ break;
+
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ lumpneeded[SCROLLLUMP] = true;
+ SpawnBonus(x,y,B_SCROLL1+tile-12);
+ break;
+
+ case 20: // goal
+ lumpneeded[GOALLUMP] = true;
+ SpawnBonus(x,y,B_GOAL);
+ break;
+
+ case 21: // chest
+ lumpneeded[CHESTLUMP] = true;
+ SpawnBonus(x,y,B_CHEST);
+ break;
+
+ case 24:
+ lumpneeded[WARPLUMP] = true;
+ SpawnWarp (x,y,0);
+ break;
+//------
+ case 41:
+ if (gamestate.difficulty <gd_Hard)
+ break;
+ case 36:
+ if (gamestate.difficulty <gd_Normal)
+ break;
+ case 22:
+ lumpneeded[TROLLLUMP] = true;
+ SpawnTroll (x,y);
+ break;
+
+ case 42:
+ if (gamestate.difficulty <gd_Hard)
+ break;
+ case 37:
+ if (gamestate.difficulty <gd_Normal)
+ break;
+ case 23:
+ lumpneeded[ORCLUMP] = true;
+ SpawnOrc (x,y);
+ break;
+
+ case 43:
+ if (gamestate.difficulty <gd_Hard)
+ break;
+ case 38:
+ if (gamestate.difficulty <gd_Normal)
+ break;
+ case 25:
+ lumpneeded[BATLUMP] = true;
+ SpawnBat (x,y);
+ break;
+
+ case 44:
+ if (gamestate.difficulty <gd_Hard)
+ break;
+ case 39:
+ if (gamestate.difficulty <gd_Normal)
+ break;
+ case 26:
+ lumpneeded[DEMONLUMP] = true;
+ SpawnDemon (x,y);
+ break;
+
+ case 45:
+ if (gamestate.difficulty <gd_Hard)
+ break;
+ case 40:
+ if (gamestate.difficulty <gd_Normal)
+ break;
+ case 27:
+ lumpneeded[MAGELUMP] = true;
+ SpawnMage (x,y);
+ break;
+
+ case 28:
+ lumpneeded[GRELLUMP] = true;
+ SpawnNemesis (x,y);
+ break;
+
+ case 29:
+ SpawnBounce (x,y,0);
+ break;
+
+ case 30:
+ SpawnBounce (x,y,1);
+ break;
+
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ lumpneeded[WARPLUMP] = true;
+ SpawnWarp (x,y,tile-30);
+ break;
+ }
+ }
+
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= ScanText
+=
+==================
+*/
+
+void ScanText (void)
+{
+ int i;
+ char far *text;
+
+ text = (char _seg *)grsegs[LEVEL1TEXT+mapon];
+
+ textstarts[0] = 0;
+
+ for (i=1;i<=26;i++)
+ {
+ while (*text != '\n')
+ {
+ if (*text == '\r')
+ *text = 0;
+ text++;
+ }
+ text++;
+ textstarts[i] = FP_OFF(text);
+ }
+
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= DrawEnterScreen
+=
+==================
+*/
+
+static char *levelnames[] =
+ {
+ "The Approach",
+ "Nemesis's Keep",
+ "Ground Floor",
+ "Second Floor",
+ "Third Floor",
+ "Tower One",
+ "Tower Two",
+ "Secret Halls",
+ "Access Floor",
+ "The Dungeon",
+ "Lower Dungeon",
+ "Catacomb",
+ "Lower Reaches",
+ "The Warrens",
+ "Hidden Caverns",
+ "The Fens of Insanity",
+ "Chaos Corridors",
+ "The Labyrinth",
+ "Halls of Blood",
+ "Nemesis's Lair"
+ };
+void DrawEnterScreen (void)
+{
+ int x,y;
+
+ VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,9); // Medium blue
+
+ x = (VIEWWIDTH - (18 * 8)) / 2 -3;
+ y = (VIEWHEIGHT - (5 * 8)) / 2;
+ VW_DrawPic(x / 8,y,ENTERPLAQUEPIC);
+
+ WindowX = x;
+ WindowW = 18 * 8;
+ PrintY = (VIEWHEIGHT/2) + 3;
+ US_CPrint (levelnames[gamestate.mapon]);
+}
+
+//==========================================================================
+
+boolean tileneeded[NUMFLOORS];
+
+
+/*
+==================
+=
+= CacheScaleds
+=
+==================
+*/
+
+void CacheScaleds (void)
+{
+ int i,j;
+ unsigned source,dest;
+
+ FreeUpMemory ();
+ CA_CacheGrChunk(LEVEL1TEXT+mapon);
+ ScanText ();
+
+//
+// make sure we are displaying screenpage 0
+//
+ if (screenpage)
+ {
+ source = screenloc[screenpage];
+ dest = screenloc[0];
+ VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);
+ screenpage = 0;
+ VW_SetScreen (dest,0);
+ displayofs = dest;
+ }
+
+//
+// cache wall pictures
+//
+ for (i=1;i<NUMFLOORS;i++)
+ if (tileneeded[i])
+ {
+ SetupScaleWall (walllight1[i]);
+ SetupScaleWall (walllight2[i]);
+ SetupScaleWall (walldark1[i]);
+ SetupScaleWall (walldark2[i]);
+ }
+
+//
+// cache the actor pictures
+//
+ for (i=0;i<NUMLUMPS;i++)
+ if (lumpneeded[i])
+ for (j=lumpstart[i];j<=lumpend[i];j++)
+ SetupScalePic(j);
+
+ source = screenloc[0];
+ for (i=1;i<=2;i++)
+ {
+ dest = screenloc[i];
+ VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);
+ }
+
+ screenpage = 1;
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= SetupGameLevel
+=
+==================
+*/
+
+void SetupGameLevel (void)
+{
+ int x,y,i;
+ unsigned far *map,tile,spot;
+
+ memset (tileneeded,0,sizeof(tileneeded));
+//
+// randomize if not a demo
+//
+ if (DemoMode)
+ {
+ US_InitRndT(false);
+ gamestate.difficulty = gd_Normal;
+ }
+ else
+ US_InitRndT(true);
+
+//
+// load the level
+//
+ CA_CacheMap (gamestate.mapon);
+
+ mapwidth = mapheaderseg[mapon]->width;
+ mapheight = mapheaderseg[mapon]->height;
+
+//
+// make a lookup table for the maps left edge
+//
+ spot = 0;
+ for (y=0;y<mapheight;y++)
+ {
+ farmapylookup[y] = spot;
+ spot += mapwidth;
+ }
+
+//
+// copy the wall data to a data segment array
+//
+ memset (tilemap,0,sizeof(tilemap));
+ memset (actorat,0,sizeof(actorat));
+ map = mapsegs[0];
+ for (y=0;y<mapheight;y++)
+ for (x=0;x<mapwidth;x++)
+ {
+ tile = *map++;
+ if (tile<NUMFLOORS)
+ {
+ tileneeded[tile] = true;
+ tilemap[x][y] = tile;
+ if (tile>=EXPWALLSTART && tile<EXPWALLSTART+NUMEXPWALLS)
+ {
+ tileneeded[WALLEXP] = tileneeded[WALLEXP+1]
+ = tileneeded[WALLEXP+2] = true;
+ }
+
+ if (tile>0)
+ (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;i<NUMTILE8;i++)
+ {
+ for (p=0;p<4;p++)
+ {
+ m = 1<<p;
+ asm mov dx,SC_INDEX
+ asm mov al,SC_MAPMASK
+ asm mov ah,[BYTE PTR m]
+ asm out dx,ax
+ for (j=0;j<8;j++)
+ *(dest+j)=*src++;
+ }
+ dest+=8;
+ }
+
+//
+// tile 16s
+//
+ src = (byte _seg *)grsegs[STARTTILE16];
+
+ for (i=0;i<NUMTILE16;i++)
+ {
+ CA_CacheGrChunk (STARTTILE16+i);
+ src = (byte _seg *)grsegs[STARTTILE16+i];
+ if (src)
+ {
+ tileoffsets[i] = FP_OFF(dest);
+ for (p=0;p<4;p++)
+ {
+ m = 1<<p;
+ asm mov dx,SC_INDEX
+ asm mov al,SC_MAPMASK
+ asm mov ah,[BYTE PTR m]
+ asm out dx,ax
+ for (j=0;j<32;j++)
+ *(dest+j)=*src++;
+ }
+ dest+=32;
+ MM_FreePtr (&grsegs[STARTTILE16+i]);
+ UNMARKGRCHUNK(STARTTILE16+i);
+ }
+ else
+ tileoffsets[i] = 0;
+ }
+
+
+//
+// pics
+//
+ destoff = FP_OFF(dest);
+ for (i=FIRSTLATCHPIC+1;i<FIRSTSCALEPIC;i++)
+ {
+ latchpics[i-FIRSTLATCHPIC] = destoff;
+ CA_CacheGrChunk (i);
+ j = pictable[i-STARTPICS].width * pictable[i-STARTPICS].height;
+ VW_MemToScreen (grsegs[i],destoff,j,1);
+ destoff+=j;
+ MM_FreePtr (&grsegs[i]);
+ UNMARKGRCHUNK(i);
+ }
+
+ EGAMAPMASK(15);
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= FizzleFade
+=
+===================
+*/
+
+#define PIXPERFRAME 1600
+
+void FizzleFade (unsigned source, unsigned dest,
+ unsigned width,unsigned height, boolean abortable)
+{
+ unsigned drawofs,pagedelta;
+ unsigned char maskb[8] = {1,2,4,8,16,32,64,128};
+ unsigned x,y,p,frame;
+ long rndval;
+
+ pagedelta = dest-source;
+ VW_SetScreen (dest,0);
+ rndval = 1;
+ y = 0;
+
+asm mov es,[screenseg]
+asm mov dx,SC_INDEX
+asm mov al,SC_MAPMASK
+asm out dx,al
+
+ TimeCount=frame=0;
+ do // while (1)
+ {
+ if (abortable)
+ {
+ IN_ReadControl(0,&c);
+ if (c.button0 || c.button1 || Keyboard[sc_Space]
+ || Keyboard[sc_Enter])
+ {
+ VW_ScreenToScreen (source,dest,width/8,height);
+ return;
+ }
+ }
+
+ for (p=0;p<PIXPERFRAME;p++)
+ {
+ //
+ // seperate random value into x/y pair
+ //
+ asm mov ax,[WORD PTR rndval]
+ asm mov dx,[WORD PTR rndval+2]
+ asm mov bx,ax
+ asm dec bl
+ asm mov [BYTE PTR y],bl // low 8 bits - 1 = y xoordinate
+ asm mov bx,ax
+ asm mov cx,dx
+ asm shr cx,1
+ asm rcr bx,1
+ asm shr bx,1
+ asm shr bx,1
+ asm shr bx,1
+ asm shr bx,1
+ asm shr bx,1
+ asm shr bx,1
+ asm shr bx,1
+ asm mov [x],bx // next 9 bits = x xoordinate
+ //
+ // advance to next random element
+ //
+ asm shr dx,1
+ asm rcr ax,1
+ asm jnc noxor
+ asm xor dx,0x0001
+ asm xor ax,0x2000
+noxor:
+ asm mov [WORD PTR rndval],ax
+ asm mov [WORD PTR rndval+2],dx
+
+ if (x>width || 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 (TimeCount<frame) // don't go too fast
+ ;
+ } while (1);
+
+
+}
+
+//==========================================================================
+
+/*
+===================
+=
+= FizzleOut
+=
+===================
+*/
+
+void FizzleOut (int showlevel)
+{
+ unsigned page1,page2;
+//
+// fizzle fade screen to grey
+//
+ bufferofs = screenloc[(screenpage+1)%3];
+ if (showlevel)
+ DrawEnterScreen ();
+ FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);
+}
+
+//==========================================================================
+
+/*
+====================
+=
+= FreeUpMemory
+=
+====================
+*/
+
+void FreeUpMemory (void)
+{
+ int i;
+
+ for (i=0;i<NUMSCALEPICS;i++)
+ if (shapedirectory[i])
+ MM_SetPurge (&(memptr)shapedirectory[i],3);
+
+ for (i=0;i<NUMSCALEWALLS;i++)
+ if (walldirectory[i])
+ MM_SetPurge (&(memptr)walldirectory[i],3);
+}
+
+//==========================================================================
+
+/*
+==================
+=
+= DrawHighScores
+=
+==================
+*/
+
+void DrawHighScores(void)
+{
+ char buffer[16],*str;
+ word i,j,
+ w,h,
+ x,y;
+ HighScore *s;
+
+
+ CA_CacheGrChunk (HIGHSCORESPIC);
+ VWB_DrawPic (0,0,HIGHSCORESPIC);
+ MM_SetPurge (&grsegs[HIGHSCORESPIC],3);
+ UNMARKGRCHUNK(HIGHSCORESPIC);
+
+ for (i = 0,s = Scores;i < MaxScores;i++,s++)
+ {
+ PrintY = 68 + (16 * i);
+
+ //
+ // name
+ //
+ PrintX = 60;
+ US_Print(s->name);
+
+ //
+ // 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;y<mapheight;y++)
+ for (x=0;x<mapwidth;x++)
+ {
+ tile = *map++;
+ if (tile<NUMFLOORS)
+ {
+ tilemap[x][y] = tile;
+ if (tile>0)
+ (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<MAPSIZE;i++)
+ nearmapylookup[i] = &tilemap[0][0]+MAPSIZE*i;
+
+ for (i=0;i<PORTTILESHIGH;i++)
+ uwidthtable[i] = UPDATEWIDE*i;
+
+ blockstart = &blockstarts[0];
+ for (y=0;y<UPDATEHIGH;y++)
+ for (x=0;x<UPDATEWIDE;x++)
+ *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
+
+ BuildTables (); // 3-d tables
+
+ SetupScaling ();
+
+#ifndef PROFILE
+// US_FinishTextScreen();
+#endif
+
+//
+// reclaim the memory from the linked in text screen
+//
+ segstart = FP_SEG(&introscn);
+ seglength = 4000/16;
+ if (FP_OFF(&introscn))
+ {
+ segstart++;
+ seglength--;
+ }
+
+ MML_UseSpace (segstart,seglength);
+
+ VW_SetScreenMode (GRMODE);
+ VW_ColorBorder (3);
+ VW_ClearVideo (BLACK);
+
+//
+// initialize variables
+//
+ updateptr = &update[0];
+ *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;
+ bufferofs = 0;
+ displayofs = 0;
+ VW_SetLineWidth(SCREENWIDTH);
+}
+
+//===========================================================================
+
+void clrscr (void); // can't include CONIO.H because of name conflicts...
+
+/*
+==========================
+=
+= Quit
+=
+==========================
+*/
+
+void Quit (char *error)
+{
+ unsigned finscreen;
+
+#if 0
+ if (!error)
+ {
+ CA_SetAllPurge ();
+ CA_CacheGrChunk (PIRACY);
+ finscreen = (unsigned)grsegs[PIRACY];
+ }
+#endif
+
+ ShutdownId ();
+ if (error && *error)
+ {
+ puts(error);
+ exit(1);
+ }
+
+#if 0
+ if (!NoWait)
+ {
+ movedata (finscreen,0,0xb800,0,4000);
+ bioskey (0);
+ clrscr();
+ }
+#endif
+
+ exit(0);
+}
+
+//===========================================================================
+
+/*
+==================
+=
+= TEDDeath
+=
+==================
+*/
+
+void TEDDeath(void)
+{
+ ShutdownId();
+ execlp("TED5.EXE","TED5.EXE","/LAUNCH",NULL);
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= DemoLoop
+=
+=====================
+*/
+
+static char *ParmStrings[] = {"easy","normal","hard",""};
+
+void DemoLoop (void)
+{
+ int i,level;
+
+//
+// check for launch from ted
+//
+ if (tedlevel)
+ {
+ NewGame();
+ gamestate.mapon = tedlevelnum;
+ restartgame = gd_Normal;
+ for (i = 1;i < _argc;i++)
+ {
+ if ( (level = US_CheckParm(_argv[i],ParmStrings)) == -1)
+ continue;
+
+ restartgame = gd_Easy+level;
+ break;
+ }
+ GameLoop();
+ TEDDeath();
+ }
+
+
+//
+// main game cycle
+//
+ displayofs = bufferofs = 0;
+ VW_Bar (0,0,320,200,0);
+
+ while (1)
+ {
+ CA_CacheGrChunk (TITLEPIC);
+ bufferofs = SCREEN2START;
+ displayofs = SCREEN1START;
+ VWB_DrawPic (0,0,TITLEPIC);
+ MM_SetPurge (&grsegs[TITLEPIC],3);
+ UNMARKGRCHUNK(TITLEPIC);
+ FizzleFade (bufferofs,displayofs,320,200,true);
+
+ if (!IN_UserInput(TickBase*3,false))
+ {
+ CA_CacheGrChunk (CREDITSPIC);
+ VWB_DrawPic (0,0,CREDITSPIC);
+ MM_SetPurge (&grsegs[CREDITSPIC],3);
+ UNMARKGRCHUNK(CREDITSPIC);
+ FizzleFade (bufferofs,displayofs,320,200,true);
+
+ }
+
+ if (!IN_UserInput(TickBase*3,false))
+ {
+highscores:
+ DrawHighScores ();
+ FizzleFade (bufferofs,displayofs,320,200,true);
+ IN_UserInput(TickBase*3,false);
+ }
+
+ if (IN_IsUserInput())
+ {
+ US_ControlPanel ();
+
+ if (restartgame || loadedgame)
+ {
+ GameLoop ();
+ goto highscores;
+ }
+ }
+
+ }
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScalePic
+=
+==========================
+*/
+
+void SetupScalePic (unsigned picnum)
+{
+ unsigned scnum;
+
+ scnum = picnum-FIRSTSCALEPIC;
+
+ if (shapedirectory[scnum])
+ {
+ MM_SetPurge (&(memptr)shapedirectory[scnum],0);
+ return; // allready in memory
+ }
+
+ CA_CacheGrChunk (picnum);
+ DeplanePic (picnum);
+ shapesize[scnum] = BuildCompShape (&shapedirectory[scnum]);
+ grneeded[picnum]&= ~ca_levelbit;
+ MM_FreePtr (&grsegs[picnum]);
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScaleWall
+=
+==========================
+*/
+
+void SetupScaleWall (unsigned picnum)
+{
+ int x,y;
+ unsigned scnum;
+ byte far *dest;
+
+ scnum = picnum-FIRSTWALLPIC;
+
+ if (walldirectory[scnum])
+ {
+ MM_SetPurge (&walldirectory[scnum],0);
+ return; // allready in memory
+ }
+
+ CA_CacheGrChunk (picnum);
+ DeplanePic (picnum);
+ MM_GetPtr(&walldirectory[scnum],64*64);
+ dest = (byte far *)walldirectory[scnum];
+ for (x=0;x<64;x++)
+ for (y=0;y<64;y++)
+ *dest++ = spotvis[y][x];
+ grneeded[picnum]&= ~ca_levelbit;
+ MM_FreePtr (&grsegs[picnum]);
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= SetupScaling
+=
+==========================
+*/
+
+void SetupScaling (void)
+{
+ int i,x,y;
+ byte far *dest;
+
+//
+// build the compiled scalers
+//
+ for (i=1;i<=VIEWWIDTH/2;i++)
+ BuildCompScale (i*2,&scaledirectory[i]);
+}
+
+//===========================================================================
+
+int showscorebox;
+
+void RF_FixOfs (void)
+{
+}
+
+void HelpScreens (void)
+{
+}
+
+
+/*
+==================
+=
+= CheckMemory
+=
+==================
+*/
+
+#define MINMEMORY 335000l
+
+void CheckMemory(void)
+{
+ unsigned finscreen;
+
+ if (mminfo.nearheap+mminfo.farheap+mminfo.EMSmem+mminfo.XMSmem
+ >= 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 [/<switch>]\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.
+
+<backwardly linked free list>
+
+#############################################################################
+*/
+
+
+/*
+=========================
+=
+= 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;i<MAXACTORS;i++)
+ {
+ objlist[i].prev = &objlist[i+1];
+ objlist[i].next = NULL;
+ }
+
+ objlist[MAXACTORS-1].prev = NULL;
+
+ objfreelist = &objlist[0];
+ lastobj = NULL;
+
+ objectcount = 0;
+
+//
+// give the player and score the first free spots
+//
+ GetNewObj (false);
+ player = new;
+}
+
+//===========================================================================
+
+/*
+=========================
+=
+= GetNewObj
+=
+= Sets the global variable new to point to a free spot in objlist.
+= The free spot is inserted at the end of the liked list
+=
+= When the object list is full, the caller can either have it bomb out ot
+= return a dummy object pointer that will never get used
+=
+=========================
+*/
+
+void GetNewObj (boolean usedummy)
+{
+ if (!objfreelist)
+ {
+ if (usedummy)
+ {
+ new = &dummyobj;
+ return;
+ }
+ Quit ("GetNewObj: No free spots in objlist!");
+ }
+
+ new = objfreelist;
+ objfreelist = new->prev;
+ 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;y<height;y++)
+ {
+ dest = &spotvis[y][0];
+ for (x=0;x<width;x++)
+ {
+ by0 = *plane0++;
+ by1 = *plane1++;
+ by2 = *plane2++;
+ by3 = *plane3++;
+
+ for (b=0;b<8;b++)
+ {
+ shift=8-b;
+
+ color = 0;
+ asm mov cl,[BYTE PTR shift]
+ asm mov al,[BYTE PTR by3]
+ asm rcr al,cl;
+ asm rcl [BYTE PTR color],1;
+
+ asm mov cl,[BYTE PTR shift]
+ asm mov al,[BYTE PTR by2]
+ asm rcr al,cl;
+ asm rcl [BYTE PTR color],1;
+
+ asm mov cl,[BYTE PTR shift]
+ asm mov al,[BYTE PTR by1]
+ asm rcr al,cl;
+ asm rcl [BYTE PTR color],1;
+
+ asm mov cl,[BYTE PTR shift]
+ asm mov al,[BYTE PTR by0]
+ asm rcr al,cl;
+ asm rcl [BYTE PTR color],1;
+
+ *dest++ = color;
+ } // B
+ } // X
+ } // Y
+}
+
+
+
+
+/*
+========================
+=
+= BuildCompScale
+=
+= Builds a compiled scaler object that will scale a 64 tall object to
+= the given height (centered vertically on the screen)
+=
+= height should be even
+=
+= Call with
+= ---------
+= DS:SI Source for scale
+= ES:DI Dest for scale
+=
+= Calling the compiled scaler only destroys AL
+=
+========================
+*/
+
+unsigned BuildCompScale (int height, memptr *finalspot)
+{
+ t_compscale _seg *work;
+ byte far *code;
+
+ int i;
+ long fix,step;
+ unsigned src,totalscaled,totalsize;
+ int startpix,endpix,toppix;
+
+
+ MM_GetPtr (&(memptr)work,20000);
+
+ step = ((long)height<<16) / 64;
+ code = &work->code[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<endpix;startpix++)
+ {
+ if (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<<TILESHIFT)+TILEGLOBAL/2;
+ new->y = ((long)y<<TILESHIFT)+TILEGLOBAL/2;
+ CalcBounds(new);
+ new->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<<TILESHIFT)+TILEGLOBAL/2;
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;
+ move -= ob->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.y<focal.y) \
+ voffset = 0; \
+ else if (tile.y==focal.y) \
+ voffset = 3; \
+ else \
+ voffset = 6; \
+ if (tile.x==focal.x) \
+ voffset ++; \
+ else if (tile.x>focal.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.x<focal.x)
+ {
+ if (tile.y<focal.y)
+ wall = SOUTH;
+ else
+ wall = EAST;
+ }
+ else if (tile.x==focal.x)
+ {
+ if (tile.y<focal.y)
+ wall = SOUTH;
+ else
+ wall = NORTH;
+ }
+ else
+ {
+ if (tile.y<=focal.y)
+ wall = WEST;
+ else
+ wall = NORTH;
+ }
+
+ //
+ // rotate the X value to see if it is behind the view plane
+ //
+ if (TransformX (((long)tile.x<<16)+point1x[wall],
+ ((long)tile.y<<16)+point1y[wall]) < FOCALLENGTH)
+ {
+ tile.x = otx;
+ tile.y = oty;
+ return 0;
+ }
+ }
+
+//
+// if the old wall is still behind a closer wall, ignore the back trace
+// and continue on (dealing with limited precision...)
+//
+ if (finish && !FinishWall ()) // the wall is still behind a forward wall
+ {
+ tile.x = otx;
+ tile.y = oty;
+ rightwall->x1 = 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.y<focal.y)
+ offset = 0;
+ else if (tile.y==focal.y)
+ offset = 3;
+ else
+ offset = 6;
+ if (tile.x==focal.x)
+ offset ++;
+ else if (tile.x>focal.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.y<focal.y)
+ offset = 0;
+ else if (tile.y==focal.y)
+ offset = 3;
+ else
+ offset = 6;
+ if (tile.x==focal.x)
+ offset ++;
+ else if (tile.x>focal.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.y<focal.y)
+ offset = 0;
+ else if (tile.y==focal.y)
+ offset = 3;
+ else
+ offset = 6;
+ if (tile.x==focal.x)
+ offset ++;
+ else if (tile.x>focal.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;i<j;i++)
+ DrawChar(7+i,20,BOLTCHAR);
+ j = gamestate.nukes < SHOWITEMS ? gamestate.nukes : SHOWITEMS;
+ for (i=0;i<j;i++)
+ DrawChar(7+i,30,NUKECHAR);
+ j = gamestate.potions < SHOWITEMS ? gamestate.potions : SHOWITEMS;
+ for (i=0;i<j;i++)
+ DrawChar(7+i,40,POTIONCHAR);
+
+ x=24;
+ for (i=0;i<4;i++)
+ for (j=0;j<gamestate.keys[i];j++)
+ DrawChar(x++,20,KEYCHARS+i);
+
+ x=24;
+ for (i=0;i<8;i++)
+ if (gamestate.scrolls[i])
+ DrawChar(x++,30,SCROLLCHARS+i);
+
+ AddPoints(0);
+
+ DrawBars ();
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveBolt
+=
+===============
+*/
+
+void GiveBolt (void)
+{
+ SD_PlaySound (GETBOLTSND);
+ if (++gamestate.bolts<=9)
+ DrawChar(6+gamestate.bolts,20,BOLTCHAR);
+}
+
+
+/*
+===============
+=
+= TakeBolt
+=
+===============
+*/
+
+void TakeBolt (void)
+{
+ SD_PlaySound (USEBOLTSND);
+ if (--gamestate.bolts<=9)
+ DrawChar(7+gamestate.bolts,20,BLANKCHAR);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveNuke
+=
+===============
+*/
+
+void GiveNuke (void)
+{
+ SD_PlaySound (GETNUKESND);
+ if (++gamestate.nukes<=9)
+ DrawChar(6+gamestate.nukes,30,NUKECHAR);
+}
+
+
+/*
+===============
+=
+= TakeNuke
+=
+===============
+*/
+
+void TakeNuke (void)
+{
+ SD_PlaySound (USENUKESND);
+ if (--gamestate.nukes<=9)
+ DrawChar(7+gamestate.nukes,30,BLANKCHAR);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GivePotion
+=
+===============
+*/
+
+void GivePotion (void)
+{
+ SD_PlaySound (GETPOTIONSND);
+ if (++gamestate.potions<=9)
+ DrawChar(6+gamestate.potions,40,POTIONCHAR);
+}
+
+
+/*
+===============
+=
+= TakePotion
+=
+===============
+*/
+
+void TakePotion (void)
+{
+ SD_PlaySound (USEPOTIONSND);
+ if (--gamestate.potions<=9)
+ DrawChar(7+gamestate.potions,40,BLANKCHAR);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveKey
+=
+===============
+*/
+
+void GiveKey (int keytype)
+{
+ int i,j,x;
+
+ SD_PlaySound (GETKEYSND);
+ gamestate.keys[keytype]++;
+
+ x=24;
+ for (i=0;i<4;i++)
+ for (j=0;j<gamestate.keys[i];j++)
+ DrawChar(x++,20,KEYCHARS+i);
+
+}
+
+
+/*
+===============
+=
+= TakeKey
+=
+===============
+*/
+
+void TakeKey (int keytype)
+{
+ int i,j,x;
+
+ SD_PlaySound (USEKEYSND);
+ gamestate.keys[keytype]--;
+
+ x=24;
+ for (i=0;i<4;i++)
+ for (j=0;j<gamestate.keys[i];j++)
+ DrawChar(x++,20,KEYCHARS+i);
+
+ DrawChar(x,20,BLANKCHAR);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveScroll
+=
+===============
+*/
+
+void GiveScroll (int scrolltype,boolean show)
+{
+ int i,x;
+
+ SD_PlaySound (GETSCROLLSND);
+ gamestate.scrolls[scrolltype] = true;
+
+ x=24;
+ for (i=0;i<8;i++)
+ if (gamestate.scrolls[i])
+ DrawChar(x++,30,SCROLLCHARS+i);
+ if (show)
+ ReadScroll(scrolltype);
+}
+
+//===========================================================================
+
+/*
+===============
+=
+= GivePoints
+=
+===============
+*/
+
+void GivePoints (int points)
+{
+ pointcount = 1;
+ pointsleft += points;
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= AddPoints
+=
+===============
+*/
+
+void AddPoints (int points)
+{
+ char str[10];
+ int len,x,i;
+
+ gamestate.score += points;
+
+ ltoa (gamestate.score,str,10);
+ len = strlen (str);
+
+ x=24+(8-len);
+ for (i=0;i<len;i++)
+ DrawChar(x++,40,NUMBERCHARS+str[i]-'0');
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveChest
+=
+===============
+*/
+
+void GiveChest (void)
+{
+ SD_PlaySound (GETPOINTSSND);
+ GivePoints ((gamestate.mapon+1)*100);
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= GiveGoal
+=
+===============
+*/
+
+void GiveGoal (void)
+{
+ SD_PlaySound (GETPOINTSSND);
+ GivePoints (100000);
+ playstate = ex_victorious;
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawLevelNumber
+=
+===============
+*/
+
+void DrawLevelNumber (int number)
+{
+ char str[10];
+ int len;
+ unsigned temp;
+
+ bufferofs = 0;
+ if (number<9)
+ PrintX=13;
+ else
+ PrintX = 5;
+ PrintY = 4;
+ VW_Bar (5,4,16,9,STATUSCOLOR);
+ temp = fontcolor;
+ fontcolor = TEXTCOLOR^STATUSCOLOR;
+ US_PrintUnsigned (number+1);
+ fontcolor = temp;
+}
+
+
+//===========================================================================
+
+/*
+===============
+=
+= DrawText
+=
+===============
+*/
+
+void DrawText (void)
+{
+ unsigned number;
+ char str[80];
+ char far *text;
+ unsigned temp;
+
+ //
+ // draw a new text description if needed
+ //
+ number = *(mapsegs[0]+farmapylookup[player->tiley]+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<lasttimecount;i++)
+ newlines += (i&1);
+
+ gamestate.shotpower += newlines;
+
+ if (gamestate.shotpower > 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<NUMSCALEWALLS;i++)
+ if (walldirectory[i])
+ MM_SetPurge (&(memptr)walldirectory[i],3);
+
+ CA_CacheGrChunk (SCROLLTOPPIC);
+ CA_CacheGrChunk (SCROLL1PIC + scroll);
+ VW_DrawPic (0,0,SCROLLTOPPIC);
+ VW_DrawPic (0,32,SCROLL1PIC + scroll);
+ UNMARKGRCHUNK(SCROLL1PIC + scroll);
+ UNMARKGRCHUNK(SCROLLTOPPIC);
+ MM_FreePtr (&grsegs[SCROLL1PIC + scroll]);
+ MM_FreePtr (&grsegs[SCROLLTOPPIC]);
+
+//
+// cache wall pictures back in
+//
+ for (i=1;i<NUMFLOORS;i++)
+ if (tileneeded[i])
+ {
+ SetupScaleWall (walllight1[i]);
+ SetupScaleWall (walllight2[i]);
+ SetupScaleWall (walldark1[i]);
+ SetupScaleWall (walldark2[i]);
+ }
+
+ VW_WaitVBL(80);
+waitkey:
+ IN_ClearKeysDown ();
+ IN_Ack();
+
+}
+
+
+
+/*
+===============
+=
+= TakeDamage
+=
+===============
+*/
+
+void TakeDamage (int points)
+{
+ unsigned source,dest,topline;
+
+ if (!gamestate.body || bordertime || godmode)
+ return;
+
+ if (points >= gamestate.body)
+ {
+ points = gamestate.body;
+ playstate = ex_died;
+ }
+
+ bordertime = points*FLASHTICS;
+ VW_ColorBorder (FLASHCOLOR);
+
+ if (gamestate.body<MAXBODY/3)
+ SD_PlaySound (TAKEDMGHURTSND);
+ else
+ SD_PlaySound (TAKEDAMAGESND);
+
+ gamestate.body -= points;
+//
+// shrink the body bar
+//
+ source = latchpics[L_NOBODY]+gamestate.body*SIDEBARWIDTH;
+ dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34;
+
+
+ asm mov es,[screenseg]
+ asm mov si,[source]
+ asm mov di,[dest]
+
+ EGAWRITEMODE(1);
+
+ asm mov cx,[points]
+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);
+
+}
+
+
+
+/*
+=============================================================================
+
+ INTERACTION
+
+=============================================================================
+*/
+
+
+/*
+==================
+=
+= OpenDoor
+=
+==================
+*/
+
+void OpenDoor (unsigned bx, unsigned by, unsigned doorbase)
+{
+ int x,y;
+ unsigned far *map;
+
+ x=bx;
+ y=by;
+ map = mapsegs[0]+farmapylookup[y]+x;
+ while (tilemap[x][y]-doorbase<4)
+ {
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+ map--;
+ x--;
+ }
+ x=bx+1;
+ map = mapsegs[0]+farmapylookup[y]+x;
+ while (tilemap[x][y]-doorbase<4)
+ {
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+ map++;
+ x++;
+ }
+ x=bx;
+ y=by-1;
+ map = mapsegs[0]+farmapylookup[y]+x;
+ while (tilemap[x][y]-doorbase<4)
+ {
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+ map-=mapwidth;
+ y--;
+ }
+ y=by+1;
+ map = mapsegs[0]+farmapylookup[y]+x;
+ while (tilemap[x][y]-doorbase<4)
+ {
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
+ map+=mapwidth;
+ y++;
+ }
+}
+
+
+/*
+==================
+=
+= HitSpecialTile
+=
+= Returns true if the move is blocked
+=
+==================
+*/
+
+boolean HitSpecialTile (unsigned x, unsigned y, unsigned tile)
+{
+ switch (tile)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ if (!gamestate.keys[0])
+ return true;
+ TakeKey(0);
+ OpenDoor (x,y,SPECTILESTART+0);
+ return false;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ if (!gamestate.keys[1])
+ return true;
+ TakeKey(1);
+ OpenDoor (x,y,SPECTILESTART+4);
+ return false;
+
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ if (!gamestate.keys[2])
+ return true;
+ TakeKey(2);
+ OpenDoor (x,y,SPECTILESTART+8);
+ return false;
+
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ if (!gamestate.keys[3])
+ return true;
+ TakeKey(3);
+ OpenDoor (x,y,SPECTILESTART+12);
+ return false;
+
+ }
+
+ return true;
+}
+
+
+
+/*
+==================
+=
+= TouchActor
+=
+= Returns true if the move is blocked
+=
+==================
+*/
+
+boolean TouchActor (objtype *ob, objtype *check)
+{
+ if (ob->xh < 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<xmax;x++)
+ for (y=ymin;y<ymax;y++)
+ {
+ check = actorat[x][y];
+ if (check>(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)<NUMEXPWALLS)
+ ExplodeWall (x,y);
+ goto blockmove;
+ }
+ }
+ return false; // move is OK!
+
+
+blockmove:
+
+ SD_PlaySound (SHOOTWALLSND);
+
+ 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 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<<TILESHIFT)+TILEGLOBAL/2;
+ player->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
+ player->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 && scroll<NUMSCROLLS && gamestate.scrolls[scroll])
+ ReadScroll (scroll);
+
+ DrawText ();
+ DrawCompass ();
+
+}
diff --git a/CAT3D.PRJ b/CAT3D.PRJ
new file mode 100644
index 0000000..33d75d4
--- /dev/null
+++ b/CAT3D.PRJ
Binary files differ
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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 && source<end)
+ {
+ count++;
+ source++;
+ }
+ if (count>3 || 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 (source<end);
+
+ complength = 2*(dest-start);
+ return complength;
+}
+
+
+/*
+======================
+=
+= CA_RLEWexpand
+= length is EXPANDED length
+=
+======================
+*/
+
+void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length,
+ unsigned rlewtag)
+{
+// unsigned value,count,i;
+ unsigned huge *end;
+ unsigned sourceseg,sourceoff,destseg,destoff,endseg,endoff;
+
+
+//
+// expand it
+//
+#if 0
+ do
+ {
+ value = *source++;
+ if (value != rlewtag)
+ //
+ // uncompressed
+ //
+ *dest++=value;
+ else
+ {
+ //
+ // compressed string
+ //
+ count = *source++;
+ value = *source++;
+ for (i=1;i<=count;i++)
+ *dest++ = value;
+ }
+ } while (dest<end);
+#endif
+
+ end = dest + (length)/2;
+ sourceseg = FP_SEG(source);
+ sourceoff = FP_OFF(source);
+ destseg = FP_SEG(dest);
+ destoff = FP_OFF(dest);
+ endseg = FP_SEG(end);
+ endoff = FP_OFF(end);
+
+
+//
+// ax = source value
+// bx = tag value
+// cx = repeat counts
+// dx = scratch
+//
+// NOTE: A repeat count that produces 0xfff0 bytes can blow this!
+//
+
+asm mov bx,rlewtag
+asm mov si,sourceoff
+asm mov di,destoff
+asm mov es,destseg
+asm mov ds,sourceseg
+
+expand:
+asm lodsw
+asm cmp ax,bx
+asm je repeat
+asm stosw
+asm jmp next
+
+repeat:
+asm lodsw
+asm mov cx,ax // repeat count
+asm lodsw // repeat value
+asm rep stosw
+
+next:
+
+asm cmp si,0x10 // normalize ds:si
+asm jb sinorm
+asm mov ax,si
+asm shr ax,1
+asm shr ax,1
+asm shr ax,1
+asm shr ax,1
+asm mov dx,ds
+asm add dx,ax
+asm mov ds,dx
+asm and si,0xf
+sinorm:
+asm cmp di,0x10 // normalize es:di
+asm jb dinorm
+asm mov ax,di
+asm shr ax,1
+asm shr ax,1
+asm shr ax,1
+asm shr ax,1
+asm mov dx,es
+asm add dx,ax
+asm mov es,dx
+asm and di,0xf
+dinorm:
+
+asm cmp di,ss:endoff
+asm jne expand
+asm mov ax,es
+asm cmp ax,ss:endseg
+asm jb expand
+
+asm mov ax,ss
+asm mov ds,ax
+
+}
+
+
+
+/*
+=============================================================================
+
+ CACHE MANAGER ROUTINES
+
+=============================================================================
+*/
+
+
+/*
+======================
+=
+= CAL_SetupGrFile
+=
+======================
+*/
+
+void CAL_SetupGrFile (void)
+{
+ int handle;
+ memptr compseg;
+
+#ifdef GRHEADERLINKED
+
+#if GRMODE == EGAGR
+ grhuffman = (huffnode *)&EGAdict;
+ grstarts = (long _seg *)FP_SEG(&EGAhead);
+#endif
+#if GRMODE == CGAGR
+ grhuffman = (huffnode *)&CGAdict;
+ grstarts = (long _seg *)FP_SEG(&CGAhead);
+#endif
+
+ CAL_OptimizeNodes (grhuffman);
+
+#else
+
+//
+// load ???dict.ext (huffman dictionary for graphics files)
+//
+
+ if ((handle = open(GREXT"DICT."EXTENSION,
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)
+ Quit ("Can't open "GREXT"DICT."EXTENSION"!");
+
+ read(handle, &grhuffman, sizeof(grhuffman));
+ close(handle);
+ CAL_OptimizeNodes (grhuffman);
+//
+// load the data offsets from ???head.ext
+//
+ MM_GetPtr (&(memptr)grstarts,(NUMCHUNKS+1)*FILEPOSSIZE);
+
+ if ((handle = open(GREXT"HEAD."EXTENSION,
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)
+ Quit ("Can't open "GREXT"HEAD."EXTENSION"!");
+
+ CA_FarRead(handle, (memptr)grstarts, (NUMCHUNKS+1)*FILEPOSSIZE);
+
+ close(handle);
+
+
+#endif
+
+//
+// Open the graphics file, leaving it open until the game is finished
+//
+ grhandle = open(GREXT"GRAPH."EXTENSION, O_RDONLY | O_BINARY);
+ if (grhandle == -1)
+ Quit ("Cannot open "GREXT"GRAPH."EXTENSION"!");
+
+
+//
+// load the pic and sprite headers into the arrays in the data segment
+//
+#if NUMPICS>0
+ 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;i<NUMSOUNDS;i++,start++)
+ if (audiosegs[start])
+ MM_SetPurge (&(memptr)audiosegs[start],3); // make purgable
+
+cachein:
+
+ switch (SoundMode)
+ {
+ case sdm_Off:
+ return;
+ case sdm_PC:
+ start = STARTPCSOUNDS;
+ break;
+ case sdm_AdLib:
+ start = STARTADLIBSOUNDS;
+ break;
+ }
+
+ for (i=0;i<NUMSOUNDS;i++,start++)
+ CA_CacheAudioChunk (start);
+
+ oldsoundmode = SoundMode;
+}
+
+//===========================================================================
+
+#if GRMODE == EGAGR
+
+/*
+======================
+=
+= CAL_ShiftSprite
+=
+= Make a shifted (one byte wider) copy of a sprite into another area
+=
+======================
+*/
+
+unsigned static sheight,swidth;
+
+void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest,
+ unsigned width, unsigned height, unsigned pixshift)
+{
+
+ sheight = height; // because we are going to reassign bp
+ swidth = width;
+
+asm mov ax,[segment]
+asm mov ds,ax // source and dest are in same segment, and all local
+
+asm mov bx,[source]
+asm mov di,[dest]
+
+asm mov bp,[pixshift]
+asm shl bp,1
+asm mov bp,WORD PTR [shifttabletable+bp] // bp holds pointer to shift table
+
+//
+// table shift the mask
+//
+asm mov dx,[ss:sheight]
+
+domaskrow:
+
+asm mov BYTE PTR [di],255 // 0xff first byte
+asm mov cx,ss:[swidth]
+
+domaskbyte:
+
+asm mov al,[bx] // source
+asm not al
+asm inc bx // next source byte
+asm xor ah,ah
+asm shl ax,1
+asm mov si,ax
+asm mov ax,[bp+si] // table shift into two bytes
+asm not ax
+asm and [di],al // and with first byte
+asm inc di
+asm mov [di],ah // replace next byte
+
+asm loop domaskbyte
+
+asm inc di // the last shifted byte has 1s in it
+asm dec dx
+asm jnz domaskrow
+
+//
+// table shift the data
+//
+asm mov dx,ss:[sheight]
+asm shl dx,1
+asm shl dx,1 // four planes of data
+
+dodatarow:
+
+asm mov BYTE PTR [di],0 // 0 first byte
+asm mov cx,ss:[swidth]
+
+dodatabyte:
+
+asm mov al,[bx] // source
+asm inc bx // next source byte
+asm xor ah,ah
+asm shl ax,1
+asm mov si,ax
+asm mov ax,[bp+si] // table shift into two bytes
+asm or [di],al // or with first byte
+asm inc di
+asm mov [di],ah // replace next byte
+
+asm loop dodatabyte
+
+asm inc di // the last shifted byte has 0s in it
+asm dec dx
+asm jnz dodatarow
+
+//
+// done
+//
+
+asm mov ax,ss // restore data segment
+asm mov ds,ax
+
+}
+
+#endif
+
+//===========================================================================
+
+/*
+======================
+=
+= CAL_CacheSprite
+=
+= Generate shifts and set up sprite structure for a given sprite
+=
+======================
+*/
+
+void CAL_CacheSprite (int chunk, byte far *compressed)
+{
+ int i;
+ unsigned shiftstarts[5];
+ unsigned smallplane,bigplane,expanded;
+ spritetabletype far *spr;
+ spritetype _seg *dest;
+
+#if GRMODE == CGAGR
+//
+// CGA has no pel panning, so shifts are never needed
+//
+ spr = &spritetable[chunk-STARTSPRITES];
+ smallplane = spr->width*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<STARTTILE8M) // tile 8s are all in one chunk!
+ expanded = BLOCK*NUMTILE8;
+ else if (chunk<STARTTILE16)
+ expanded = MASKBLOCK*NUMTILE8M;
+ else if (chunk<STARTTILE16M) // all other tiles are one/chunk
+ expanded = BLOCK*4;
+ else if (chunk<STARTTILE32)
+ expanded = MASKBLOCK*4;
+ else if (chunk<STARTTILE32M)
+ expanded = BLOCK*16;
+ else
+ expanded = MASKBLOCK*16;
+ }
+ else
+ {
+ //
+ // everything else has an explicit size longword
+ //
+ expanded = *(long far *)source;
+ source += 4; // skip over length
+ }
+
+//
+// allocate final space, decompress it, and free bigbuffer
+// Sprites need to have shifts made and various other junk
+//
+ 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;plane<MAPPLANES;plane++)
+ if (mapsegs[plane])
+ MM_FreePtr (&(memptr)mapsegs[plane]);
+
+ mapon = mapnum;
+
+
+//
+// load map header
+// The header will be cached if it is still around
+//
+ if (!mapheaderseg[mapnum])
+ {
+ pos = ((mapfiletype _seg *)tinf)->headeroffsets[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; plane<MAPPLANES; plane++)
+ {
+ pos = mapheaderseg[mapnum]->planestart[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<NUMCHUNKS;i++)
+ grneeded[i]&=~ca_levelbit;
+}
+
+
+//===========================================================================
+
+/*
+======================
+=
+= CA_ClearAllMarks
+=
+= Clears out all the marks on all the levels
+=
+======================
+*/
+
+void CA_ClearAllMarks (void)
+{
+ _fmemset (grneeded,0,sizeof(grneeded));
+ ca_levelbit = 1;
+ ca_levelnum = 0;
+}
+
+
+//===========================================================================
+
+/*
+======================
+=
+= CA_FreeGraphics
+=
+======================
+*/
+
+void CA_FreeGraphics (void)
+{
+ int i;
+
+ for (i=0;i<NUMCHUNKS;i++)
+ if (grsegs[i])
+ MM_SetPurge (&(memptr)grsegs[i],3);
+}
+
+
+/*
+======================
+=
+= CA_SetAllPurge
+=
+= Make everything possible purgable
+=
+======================
+*/
+
+void CA_SetAllPurge (void)
+{
+ int i;
+
+ CA_ClearMarks ();
+
+//
+// free cursor sprite and background save
+//
+ VW_FreeCursor ();
+
+//
+// free map headers and map planes
+//
+ for (i=0;i<NUMMAPS;i++)
+ if (mapheaderseg[i])
+ MM_SetPurge (&(memptr)mapheaderseg[i],3);
+
+ for (i=0;i<3;i++)
+ if (mapsegs[i])
+ MM_FreePtr (&(memptr)mapsegs[i]);
+
+//
+// free sounds
+//
+ for (i=0;i<NUMSNDCHUNKS;i++)
+ if (audiosegs[i])
+ MM_SetPurge (&(memptr)audiosegs[i],3);
+
+//
+// free graphics
+//
+ CA_FreeGraphics ();
+}
+
+
+void CA_SetGrPurge (void)
+{
+ int i;
+
+//
+// free graphics
+//
+ for (i=0;i<NUMCHUNKS;i++)
+ if (grsegs[i])
+ MM_SetPurge (&(memptr)grsegs[i],3);
+}
+
+
+//===========================================================================
+
+
+/*
+======================
+=
+= CAL_DialogDraw
+=
+======================
+*/
+
+#define NUMBARS (17l*8)
+#define BARSTEP 8
+
+unsigned thx,thy,lastx;
+long barx,barstep;
+
+void CAL_DialogDraw (char *title,unsigned numcache)
+{
+ unsigned homex,homey,x;
+
+ barstep = (NUMBARS<<16)/numcache;
+
+//
+// draw dialog window (masked tiles 12 - 20 are window borders)
+//
+ US_CenterWindow (20,8);
+ homex = PrintX;
+ homey = PrintY;
+
+ US_CPrint ("Loading");
+ fontcolor = F_SECONDCOLOR;
+ US_CPrint (title);
+ fontcolor = F_BLACK;
+
+//
+// draw thermometer bar
+//
+ thx = homex + 8;
+ thy = homey + 32;
+ VWB_DrawTile8(thx,thy,0); // CAT3D numbers
+ VWB_DrawTile8(thx,thy+8,3);
+ VWB_DrawTile8(thx,thy+16,6);
+ VWB_DrawTile8(thx+17*8,thy,2);
+ VWB_DrawTile8(thx+17*8,thy+8,5);
+ VWB_DrawTile8(thx+17*8,thy+16,8);
+ for (x=thx+8;x<thx+17*8;x+=8)
+ {
+ VWB_DrawTile8(x,thy,1);
+ VWB_DrawTile8(x,thy+8,4);
+ VWB_DrawTile8(x,thy+16,7);
+ }
+
+ thx += 4; // first line location
+ thy += 5;
+ barx = (long)thx<<16;
+ lastx = thx;
+
+ VW_UpdateScreen();
+}
+
+
+/*
+======================
+=
+= CAL_DialogUpdate
+=
+======================
+*/
+
+void CAL_DialogUpdate (void)
+{
+ unsigned x,xh;
+
+ barx+=barstep;
+ xh = barx>>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<NUMCHUNKS;i++)
+ if (grneeded[i]&ca_levelbit)
+ {
+ if (grsegs[i]) // its allready in memory, make
+ MM_SetPurge(&grsegs[i],0); // sure it stays there!
+ else
+ numcache++;
+ }
+ else
+ {
+ if (grsegs[i]) // not needed, so make it purgeable
+ MM_SetPurge(&grsegs[i],3);
+ }
+
+ if (!numcache) // nothing to cache!
+ return;
+
+ if (dialog)
+ {
+#ifdef PROFILE
+ write(profilehandle,title,strlen(title));
+ write(profilehandle,"\n",1);
+#endif
+ if (drawcachebox)
+ drawcachebox(title,numcache);
+ }
+
+//
+// go through and load in anything still needed
+//
+ bufferstart = bufferend = 0; // nothing good in buffer now
+
+ for (i=0;i<NUMCHUNKS;i++)
+ if ( (grneeded[i]&ca_levelbit) && !grsegs[i])
+ {
+//
+// update thermometer
+//
+ if (dialog && updatecachebox)
+ updatecachebox ();
+
+ pos = GRFILEPOS(i);
+ if (pos<0)
+ continue;
+
+ next = i +1;
+ while (GRFILEPOS(next) == -1) // skip past any sparse tiles
+ next++;
+
+ compressed = GRFILEPOS(next)-pos;
+ endpos = pos+compressed;
+
+ if (compressed<=BUFFERSIZE)
+ {
+ if (bufferstart<=pos
+ && bufferend>= 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 <ALLOC.H>
+#include <CTYPE.H>
+#include <DOS.H>
+#include <ERRNO.H>
+#include <FCNTL.H>
+#include <IO.H>
+#include <MEM.H>
+#include <PROCESS.H>
+#include <STDIO.H>
+#include <STDLIB.H>
+#include <STRING.H>
+#include <SYS\STAT.H>
+
+#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;i<EMSpagesmapped;i++)
+ {
+ asm {
+ mov ah,EMS_MAPPAGE
+ mov bx,[i] // logical page
+ mov al,bl // physical page
+ mov dx,[EMShandle] // handle
+ int EMS_INT
+ or ah,ah
+ jnz error
+ }
+ }
+
+ return;
+
+error:
+ error = _AH;
+ strcpy (str,"MM_MapEMS: EMS error 0x");
+ itoa(error,str2,16);
+ strcpy (str,str2);
+ Quit (str);
+}
+
+//==========================================================================
+
+/*
+======================
+=
+= MML_CheckForXMS
+=
+= Check for XMM driver
+=
+=======================
+*/
+
+boolean MML_CheckForXMS (void)
+{
+ numUMBs = 0;
+
+asm {
+ mov ax,0x4300
+ int 0x2f // query status of installed diver
+ cmp al,0x80
+ je good
+ }
+ return false;
+good:
+ return true;
+}
+
+
+/*
+======================
+=
+= MML_SetupXMS
+=
+= Try to allocate all upper memory block
+=
+=======================
+*/
+
+void MML_SetupXMS (void)
+{
+ unsigned base,size;
+
+asm {
+ mov ax,0x4310
+ int 0x2f
+ mov [WORD PTR XMSaddr],bx
+ mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
+ }
+
+getmemory:
+asm {
+ mov ah,XMS_ALLOCUMB
+ mov dx,0xffff // try for largest block possible
+ call [DWORD PTR XMSaddr]
+ or ax,ax
+ jnz gotone
+
+ cmp bl,0xb0 // error: smaller UMB is available
+ jne done;
+
+ mov ah,XMS_ALLOCUMB
+ call [DWORD PTR XMSaddr] // DX holds largest available UMB
+ or ax,ax
+ jz done // another error...
+ }
+
+gotone:
+asm {
+ mov [base],bx
+ mov [size],dx
+ }
+ MML_UseSpace (base,size);
+ mminfo.XMSmem += size*16;
+ UMBbase[numUMBs] = base;
+ numUMBs++;
+ if (numUMBs < MAXUMBS)
+ goto getmemory;
+
+done:;
+}
+
+
+/*
+======================
+=
+= MML_ShutdownXMS
+=
+======================
+*/
+
+void MML_ShutdownXMS (void)
+{
+ int i;
+ unsigned base;
+
+ for (i=0;i<numUMBs;i++)
+ {
+ base = UMBbase[i];
+
+asm mov ah,XMS_FREEUMB
+asm mov dx,[base]
+asm call [DWORD PTR XMSaddr]
+ }
+}
+
+//==========================================================================
+
+/*
+======================
+=
+= MML_UseSpace
+=
+= Marks a range of paragraphs as usable by the memory manager
+= This is used to mark space for the near heap, far heap, ems page frame,
+= and upper memory blocks
+=
+======================
+*/
+
+void MML_UseSpace (unsigned segstart, unsigned seglength)
+{
+ mmblocktype far *scan,far *last;
+ unsigned oldend;
+ long extra;
+
+ scan = last = mmhead;
+ mmrover = mmhead; // reset rover to start of memory
+
+//
+// search for the block that contains the range of segments
+//
+ while (scan->start+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;i<MAXBLOCKS-1;i++)
+ mmblocks[i].next = &mmblocks[i+1];
+ mmblocks[i].next = NULL;
+
+//
+// locked block of all memory until we punch out free space
+//
+ GETNEWBLOCK;
+ mmhead = mmnew; // this will allways be the first node
+ mmnew->start = 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<PORTTILESHIGH;i++)
+ uwidthtable[i] = UPDATEWIDE*i;
+
+ originxmin = originymin = MAPBORDER*TILEGLOBAL;
+
+ eraselistptr[0] = &eraselist[0][0];
+ eraselistptr[1] = &eraselist[1][0];
+
+
+
+ if (grmode == EGAGR)
+ {
+ SX_T_SHIFT = 1;
+
+ baseupdatestart[0] = &update[0][UPDATESPARESIZE];
+ baseupdatestart[1] = &update[1][UPDATESPARESIZE];
+
+ screenpage = 0;
+ otherpage = 1;
+ displayofs = screenstart[screenpage];
+ bufferofs = screenstart[otherpage];
+ masterofs = screenstart[2];
+
+ updateptr = baseupdatestart[otherpage];
+
+ blockstart = &blockstarts[0];
+ for (y=0;y<UPDATEHIGH;y++)
+ for (x=0;x<UPDATEWIDE;x++)
+ *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
+
+ xpanmask = 6; // dont pan to odd pixels
+ }
+
+ else if (grmode == CGAGR)
+ {
+ SX_T_SHIFT = 2;
+
+ updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];
+
+ bufferofs = 0;
+ masterofs = 0x8000;
+
+ blockstart = &blockstarts[0];
+ for (y=0;y<UPDATEHIGH;y++)
+ for (x=0;x<UPDATEWIDE;x++)
+ *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
+ }
+}
+
+
+
+
+/*
+=====================
+=
+= RF_Shutdown
+=
+=====================
+*/
+
+void RF_Shutdown (void)
+{
+
+}
+
+//===========================================================================
+
+
+/*
+=====================
+=
+= RF_FixOfs
+=
+= Sets bufferofs,displayofs, and masterofs to regular values, for the
+= occasions when you have moved them around manually
+=
+=====================
+*/
+
+void RF_FixOfs (void)
+{
+ if (grmode == EGAGR)
+ {
+ screenpage = 0;
+ otherpage = 1;
+ panx = pany = pansx = pansy = panadjust = 0;
+ displayofs = screenstart[screenpage];
+ bufferofs = screenstart[otherpage];
+ masterofs = screenstart[2];
+ VW_SetScreen (displayofs,0);
+ }
+ else
+ {
+ bufferofs = 0;
+ masterofs = 0x8000;
+ }
+}
+
+
+//===========================================================================
+
+/*
+=====================
+=
+= RF_NewMap
+=
+= Makes some convienient calculations based on maphead->
+=
+=====================
+*/
+
+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<mapheight;i++)
+ {
+ mapbwidthtable[i] = spot;
+ spot += mapbyteswide;
+ }
+
+//
+// fill in updatemapofs with the new width info
+//
+ table = &updatemapofs[0];
+ for (y=0;y<PORTTILESHIGH;y++)
+ for (x=0;x<UPDATEWIDE;x++)
+ *table++ = mapbwidthtable[y]+x*2;
+
+//
+// the y max value clips off the bottom half of a tile so a map that is
+// 13 + MAPBORDER*2 tile high will not scroll at all vertically
+//
+ originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;
+ originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;
+ if (originxmax<originxmin) // for very small maps
+ originxmax=originxmin;
+ if (originymax<originymin)
+ originymax=originymin;
+
+//
+// clear out the lists
+//
+ RFL_InitSpriteList ();
+ RFL_InitAnimList ();
+ RFL_ClearScrollBlocks ();
+ RF_SetScrollBlock (0,MAPBORDER-1,true);
+ RF_SetScrollBlock (0,mapheight-MAPBORDER,true);
+ RF_SetScrollBlock (MAPBORDER-1,0,false);
+ RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);
+
+
+ lasttimecount = TimeCount; // setup for adaptive timing
+ tics = 1;
+}
+
+//===========================================================================
+
+/*
+==========================
+=
+= RF_MarkTileGraphics
+=
+= Goes through mapplane[0/1] and marks all background/foreground tiles
+= needed, then follows all animation sequences to make sure animated
+= tiles get all the stages. Every unique animating tile is given an
+= entry in allanims[], so every instance of that tile will animate at the
+= same rate. The info plane for each animating tile will hold a pointer
+= into allanims[], therefore you can't have both an animating foreground
+= and background tile in the same spot!
+=
+==========================
+*/
+
+void RF_MarkTileGraphics (void)
+{
+ unsigned size;
+ int tile,next,anims,change;
+ unsigned far *start,far *end,far *info;
+ unsigned i,tilehigh;
+ char str[80],str2[10];
+
+ memset (allanims,0,sizeof(allanims));
+ numanimchains = 0;
+
+ size = mapwidth*mapheight;
+
+//
+// background plane
+//
+ start = mapsegs[0];
+ info = mapsegs[2];
+ end = start+size;
+ do
+ {
+ tile = *start++;
+ if (tile>=0) // <0 is a tile that is never drawn
+ {
+ CA_MarkGrChunk(STARTTILE16+tile);
+ if (tinf[ANIM+tile])
+ {
+ // this tile will animated
+
+ if (tinf[SPEED+tile])
+ {
+ if (!tinf[ANIM+tile])
+ {
+ strcpy (str,"RF_MarkTileGraphics: Background anim of 0:");
+ itoa (tile,str2,10);
+ strcat (str,str2);
+ Quit (str);
+ }
+ for (i=0;i<numanimchains;i++)
+ if (allanims[i].current == tile)
+ {
+ *info = (unsigned)&allanims[i];
+ goto nextback;
+ }
+
+ // new chain of animating tiles
+
+ if (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<end);
+
+//
+// foreground plane
+//
+ start = mapsegs[1];
+ info = mapsegs[2];
+ end = start+size;
+ do
+ {
+ tile = *start++;
+ if (tile>=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<numanimchains;i++)
+ if (allanims[i].current == tilehigh)
+ {
+ *info = (unsigned)&allanims[i];
+ goto nextfront;
+ }
+
+ // new chain of animating tiles
+
+ if (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 (start<end);
+}
+
+
+//===========================================================================
+
+
+/*
+=========================
+=
+= RFL_InitAnimList
+=
+= Call to clear out the entire animating tile list and return all of them to
+= the free list.
+=
+=========================
+*/
+
+void RFL_InitAnimList (void)
+{
+ int i;
+
+ animfreeptr = &animarray[0];
+
+ for (i=0;i<MAXANIMTILES-1;i++)
+ animarray[i].nexttile = &animarray[i+1];
+
+ animarray[i].nexttile = NULL;
+
+ animhead = NULL; // nothing in list
+}
+
+
+/*
+====================
+=
+= RFL_CheckForAnimTile
+=
+====================
+*/
+
+void RFL_CheckForAnimTile (unsigned x, unsigned y)
+{
+ unsigned tile,offset,speed,lasttime,thistime,timemissed;
+ unsigned far *map;
+ animtiletype *anim,*next;
+
+// the info plane of each animating tile has a near pointer into allanims[]
+// which gives the current state of all concurrently animating tiles
+
+ offset = mapbwidthtable[y]/2+x;
+
+//
+// background
+//
+ map = mapsegs[0]+offset;
+ tile = *map;
+ if (tinf[ANIM+tile] && tinf[SPEED+tile])
+ {
+ if (!animfreeptr)
+ Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
+ anim = animfreeptr;
+ animfreeptr = animfreeptr->nexttile;
+ next = animhead; // stick it at the start of the list
+ animhead = anim;
+ if (next)
+ next->prevptr = &anim->nexttile;
+ anim->nexttile = next;
+ anim->prevptr = &animhead;
+
+ anim->x = x;
+ anim->y = y;
+ anim->tile = tile;
+ anim->mapplane = map;
+ anim->chain = (tiletype *)*(mapsegs[2]+offset);
+ }
+
+//
+// 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<MAXSPRITES-1;i++)
+ spritearray[i].nextsprite = &spritearray[i+1];
+
+ spritearray[i].nextsprite = NULL;
+
+// NULL in all priority levels
+
+ memset (prioritystart,0,sizeof(prioritystart));
+}
+
+//===========================================================================
+
+/*
+=================
+=
+= RFL_CalcOriginStuff
+=
+= Calculate all the global variables for a new position
+= Long parms so position can be clipped to a maximum near 64k
+=
+=================
+*/
+
+void RFL_CalcOriginStuff (long x, long y)
+{
+ originxglobal = x;
+ originyglobal = y;
+ originxtile = originxglobal>>G_T_SHIFT;
+ originytile = originyglobal>>G_T_SHIFT;
+ originxscreen = originxtile<<SX_T_SHIFT;
+ originyscreen = originytile<<SY_T_SHIFT;
+ originmap = mapbwidthtable[originytile] + originxtile*2;
+
+#if GRMODE == EGAGR
+ panx = (originxglobal>>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;check<vscrollblocks;check++)
+ if (vscrolledge[check] == newxtile)
+ {
+ originxglobal = originxglobal&0xff00;
+ break;
+ }
+ }
+ else if (x<0)
+ {
+ for (check=0;check<vscrollblocks;check++)
+ if (vscrolledge[check] == newxtile)
+ {
+ originxglobal = (originxglobal&0xff00)+0x100;
+ break;
+ }
+ }
+
+
+ if (y>0)
+ {
+ newytile+=SCREENTILESHIGH;
+ for (check=0;check<hscrollblocks;check++)
+ if (hscrolledge[check] == newytile)
+ {
+ originyglobal = originyglobal&0xff00;
+ break;
+ }
+ }
+ else if (y<0)
+ {
+ for (check=0;check<hscrollblocks;check++)
+ if (hscrolledge[check] == newytile)
+ {
+ originyglobal = (originyglobal&0xff00)+0x100;
+ break;
+ }
+ }
+
+
+ RFL_CalcOriginStuff (originxglobal, originyglobal);
+}
+
+
+//===========================================================================
+
+/*
+=====================
+=
+= RF_SetRefreshHook
+=
+=====================
+*/
+
+void RF_SetRefreshHook (void (*func) (void) )
+{
+ refreshvector = func;
+}
+
+
+//===========================================================================
+
+/*
+=================
+=
+= RFL_NewRow
+=
+= Bring a new row of tiles onto the port, spawning animating tiles
+=
+=================
+*/
+
+void RFL_NewRow (int dir)
+{
+ unsigned count,updatespot,updatestep;
+ int x,y,xstep,ystep;
+
+ switch (dir)
+ {
+ case 0: // top row
+ updatespot = 0;
+ updatestep = 1;
+ x = originxtile;
+ y = originytile;
+ xstep = 1;
+ ystep = 0;
+ count = PORTTILESWIDE;
+ break;
+
+ case 1: // right row
+ updatespot = PORTTILESWIDE-1;
+ updatestep = UPDATEWIDE;
+ x = originxtile + PORTTILESWIDE-1;
+ y = originytile;
+ xstep = 0;
+ ystep = 1;
+ count = PORTTILESHIGH;
+ break;
+
+ case 2: // bottom row
+ updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
+ updatestep = 1;
+ x = originxtile;
+ y = originytile + PORTTILESHIGH-1;
+ xstep = 1;
+ ystep = 0;
+ count = PORTTILESWIDE;
+ break;
+
+ case 3: // left row
+ updatespot = 0;
+ updatestep = UPDATEWIDE;
+ x = originxtile;
+ y = originytile;
+ xstep = 0;
+ ystep = 1;
+ count = PORTTILESHIGH;
+ break;
+ default:
+ Quit ("RFL_NewRow: Bad dir!");
+ }
+
+ while (count--)
+ {
+ RFL_NewTile(updatespot);
+ RFL_CheckForAnimTile (x,y);
+ updatespot+=updatestep;
+ x+=xstep;
+ y+=ystep;
+ }
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= RF_ForceRefresh
+=
+=====================
+*/
+
+void RF_ForceRefresh (void)
+{
+ RF_NewPosition (originxglobal,originyglobal);
+ RF_Refresh ();
+ RF_Refresh ();
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= RF_MapToMap
+=
+= Copies a block of tiles (all three planes) from one point
+= in the map to another, accounting for animating tiles
+=
+=====================
+*/
+
+void RF_MapToMap (unsigned srcx, unsigned srcy,
+ unsigned destx, unsigned desty,
+ unsigned width, unsigned height)
+{
+ int x,y;
+ unsigned source,destofs,xspot,yspot;
+ unsigned linedelta,p0,p1,p2,updatespot;
+ unsigned far *source0, far *source1, far *source2;
+ unsigned far *dest0, far *dest1, far *dest2;
+ boolean changed;
+
+ RFL_RemoveAnimsInBlock (destx,desty,width,height);
+
+ source = mapbwidthtable[srcy]/2 + srcx;
+
+ source0 = mapsegs[0]+source;
+ source1 = mapsegs[1]+source;
+ source2 = mapsegs[2]+source;
+
+ destofs = mapbwidthtable[desty]/2 + destx;
+ destofs -= source;
+
+ linedelta = mapwidth - width;
+
+ for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)
+ for (x=0;x<width;x++,source0++,source1++,source2++)
+ {
+ p0 = *source0;
+ p1 = *source1;
+ p2 = *source2;
+
+ dest0 = source0 + destofs;
+ dest1 = source1 + destofs;
+ dest2 = source2 + destofs;
+
+//
+// only make a new tile if it is different
+//
+ if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)
+ {
+ *dest0 = p0;
+ *dest1 = p1;
+ *dest2 = p2;
+ changed = true;
+ }
+ else
+ changed = false;
+
+//
+// if tile is on the view port
+//
+ xspot = destx+x-originxtile;
+ yspot = desty+y-originytile;
+ if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
+ {
+ if (changed)
+ {
+ updatespot = uwidthtable[yspot]+xspot;
+ RFL_NewTile(updatespot);
+ }
+ RFL_CheckForAnimTile (destx+x,desty+y);
+ }
+ }
+}
+
+//===========================================================================
+
+
+/*
+=====================
+=
+= RF_MemToMap
+=
+= Copies a string of tiles from main memory to the map,
+= accounting for animating tiles
+=
+=====================
+*/
+
+void RF_MemToMap (unsigned far *source, unsigned plane,
+ unsigned destx, unsigned desty,
+ unsigned width, unsigned height)
+{
+ int x,y;
+ unsigned xspot,yspot;
+ unsigned linedelta,updatespot;
+ unsigned far *dest,old,new;
+ boolean changed;
+
+ RFL_RemoveAnimsInBlock (destx,desty,width,height);
+
+ dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;
+
+ linedelta = mapwidth - width;
+
+ for (y=0;y<height;y++,dest+=linedelta)
+ for (x=0;x<width;x++)
+ {
+ old = *dest;
+ new = *source++;
+ if (old != new)
+ {
+ *dest = new;
+ changed = true;
+ }
+ else
+ changed = false;
+
+ dest++;
+ xspot = destx+x-originxtile;
+ yspot = desty+y-originytile;
+ if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
+ {
+ if (changed)
+ {
+ updatespot = uwidthtable[yspot]+xspot;
+ RFL_NewTile(updatespot);
+ }
+ RFL_CheckForAnimTile (destx+x,desty+y);
+ }
+ }
+}
+
+//===========================================================================
+
+
+/*
+=====================
+=
+= RFL_BoundNewOrigin
+=
+= Copies a string of tiles from main memory to the map,
+= accounting for animating tiles
+=
+=====================
+*/
+
+void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)
+{
+ int check,edge;
+
+//
+// calculate new origin related globals
+//
+ if (orgx<originxmin)
+ orgx=originxmin;
+ else if (orgx>originxmax)
+ orgx=originxmax;
+
+ if (orgy<originymin)
+ orgy=originymin;
+ else if (orgy>originymax)
+ orgy=originymax;
+
+ originxtile = orgx>>G_T_SHIFT;
+ originytile = orgy>>G_T_SHIFT;
+
+ for (check=0;check<vscrollblocks;check++)
+ {
+ edge = vscrolledge[check];
+ if (edge>=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<hscrollblocks;check++)
+ {
+ edge = hscrolledge[check];
+ if (edge>=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 (TimeCount<oldtimecount+DEMOTICS*2)
+ ;
+ lasttimecount = oldtimecount + DEMOTICS;
+ TimeCount = lasttimecount + DEMOTICS;
+ tics = DEMOTICS;
+ }
+ else
+ {
+//
+// non demo, so report actual time
+//
+ do
+ {
+ newtime = TimeCount;
+ tics = newtime-lasttimecount;
+ } while (tics<MINTICS);
+ lasttimecount = newtime;
+
+#ifdef PROFILE
+ strcpy (scratch,"\tTics:");
+ itoa (tics,str,10);
+ strcat (scratch,str);
+ strcat (scratch,"\n");
+ write (profilehandle,scratch,strlen(scratch));
+#endif
+
+ if (tics>MAXTICS)
+ {
+ 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;my<PORTTILESHIGH;my++)
+ {
+ for (mx=0;mx<PORTTILESWIDE;mx++)
+ {
+ RFL_NewTile(updatenum); // puts "1"s in both pages
+ RFL_CheckForAnimTile(mx+originxtile,my+originytile);
+ updatenum++;
+ }
+ updatenum++;
+ *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles
+ page0ptr+=(PORTTILESWIDE+1);
+ page1ptr+=(PORTTILESWIDE+1);
+ }
+ *(word *)(page0ptr-PORTTILESWIDE)
+ = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;
+}
+
+//===========================================================================
+
+
+/*
+=====================
+=
+= RF_Scroll EGA
+=
+= Move the origin x/y global coordinates, readjust the screen panning, and
+= scroll if needed. If the scroll distance is greater than one tile, the
+= entire screen will be redrawn (this could be generalized, but scrolling
+= more than one tile per refresh is a bad idea!).
+=
+=====================
+*/
+
+void RF_Scroll (int x, int y)
+{
+ long neworgx,neworgy;
+ int i,deltax,deltay,absdx,absdy;
+ int oldxt,oldyt,move,yy;
+ unsigned updatespot;
+ byte *update0,*update1;
+ unsigned oldpanx,oldpanadjust,oldscreen,newscreen,screencopy;
+ int screenmove;
+
+ oldxt = originxtile;
+ oldyt = originytile;
+ oldpanadjust = panadjust;
+ oldpanx = panx;
+
+ RFL_BoundScroll (x,y);
+
+ deltax = originxtile - oldxt;
+ absdx = abs(deltax);
+ deltay = originytile - oldyt;
+ absdy = abs(deltay);
+
+ if (absdx>1 || 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;yy<PORTTILESHIGH;yy++)
+ {
+ *update0 = *update1 = 0; // drop a 0 at end of each row
+ update0+=UPDATEWIDE;
+ update1+=UPDATEWIDE;
+ }
+ }
+
+//----------------
+
+ if (deltay)
+ {
+ if (deltay==1)
+ {
+ updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
+ RFL_NewRow (2); // new bottom row
+ RFL_RemoveAnimsOnY (originytile-1);
+ }
+ else
+ {
+ updatespot = 0;
+ RFL_NewRow (0); // new top row
+ RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
+ }
+
+ *(updatestart[0]+updatespot+PORTTILESWIDE) =
+ *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;
+ }
+
+//----------------
+
+ //
+ // place a new terminator
+ //
+ update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;
+ update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;
+ *update0++ = *update1++ = 0;
+ *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;
+}
+
+//===========================================================================
+
+/*
+=====================
+=
+= RF_PlaceSprite EGA
+=
+=====================
+*/
+
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
+ unsigned spritenumber, drawtype draw, int priority)
+{
+ spritelisttype register *sprite,*next;
+ spritetabletype far *spr;
+ spritetype _seg *block;
+ unsigned shift,pixx;
+ char str[80],str2[10];
+
+ if (!spritenumber || spritenumber == (unsigned)-1)
+ {
+ RF_RemoveSprite (user);
+ return;
+ }
+
+ sprite = (spritelisttype *)*user;
+
+ if (sprite)
+ {
+ // sprite allready exists in the list, so we can use it's block
+
+ //
+ // 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));
+ }
+
+ 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;priority<PRIORITIES;priority++)
+ {
+ if (priority==MASKEDTILEPRIORITY)
+ RFL_MaskForegroundTiles ();
+
+ for (sprite = prioritystart[priority]; sprite ;
+ sprite = (spritelisttype *)sprite->nextsprite)
+ {
+ //
+ // 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;my<PORTTILESHIGH;my++)
+ {
+ for (mx=0;mx<PORTTILESWIDE;mx++)
+ {
+ RFL_NewTile(updatenum); // puts "1"s in both pages
+ RFL_CheckForAnimTile(mx+originxtile,my+originytile);
+ updatenum++;
+ }
+ updatenum++;
+ *spotptr = 0; // set a 0 at end of a line of tiles
+ spotptr +=(PORTTILESWIDE+1);
+ }
+ *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;
+}
+
+
+/*
+=====================
+=
+= RF_Scroll CGA
+=
+= Move the origin x/y global coordinates, readjust the screen panning, and
+= scroll if needed. If the scroll distance is greater than one tile, the
+= entire screen will be redrawn (this could be generalized, but scrolling
+= more than one tile per refresh is a bad idea!).
+=
+=====================
+*/
+
+void RF_Scroll (int x, int y)
+{
+ long neworgx,neworgy;
+ int i,deltax,deltay,absdx,absdy;
+ int oldxt,oldyt,move,yy;
+ unsigned updatespot;
+ byte *spotptr;
+ unsigned oldoriginmap,oldscreen,newscreen,screencopy;
+ int screenmove;
+
+ oldxt = originxtile;
+ oldyt = originytile;
+
+ RFL_BoundScroll (x,y);
+
+ deltax = originxtile - oldxt;
+ absdx = abs(deltax);
+ deltay = originytile - oldyt;
+ absdy = abs(deltay);
+
+ if (absdx>1 || 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;yy<PORTTILESHIGH;yy++)
+ {
+ *spotptr = 0; // drop a 0 at end of each row
+ spotptr+=UPDATEWIDE;
+ }
+ }
+
+//----------------
+
+ if (deltay)
+ {
+ if (deltay==1)
+ {
+ RFL_NewRow (2); // new bottom row
+ *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;
+ RFL_RemoveAnimsOnY (originytile-1);
+ }
+ else
+ {
+ RFL_NewRow (0); // new top row
+ *(updateptr+PORTTILESWIDE) = 0;
+ RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
+ }
+ }
+
+//----------------
+
+ //
+ // place a new terminator
+ //
+ spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;
+ *spotptr++ = 0;
+ *(unsigned *)spotptr = UPDATETERMINATE;
+}
+
+/*
+=====================
+=
+= RF_PlaceSprite CGA
+=
+=====================
+*/
+
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
+ unsigned spritenumber, drawtype draw, int priority)
+{
+ spritelisttype register *sprite,*next;
+ spritetabletype far *spr;
+ spritetype _seg *block;
+ unsigned shift,pixx;
+ char str[80],str2[10];
+
+ if (!spritenumber || spritenumber == (unsigned)-1)
+ {
+ RF_RemoveSprite (user);
+ return;
+ }
+
+ sprite = (spritelisttype *)*user;
+
+ if (sprite)
+ {
+ // sprite allready exists in the list, so we can use it's block
+
+ //
+ // post an erase block to erase the old position by copying
+ // screenx,screeny,width,height
+ //
+ if (!sprite->updatecount) // 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;priority<PRIORITIES;priority++)
+ {
+ if (priority==MASKEDTILEPRIORITY)
+ RFL_MaskForegroundTiles ();
+
+ for (sprite = prioritystart[priority]; sprite ;
+ sprite = (spritelisttype *)sprite->nextsprite)
+ {
+ //
+ // 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 <dos.h>
+
+#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
+ *
+ * Th