--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_IN.c - Input Manager\r
+// v1.0d1\r
+// By Jason Blochowiak\r
+//\r
+\r
+//\r
+// This module handles dealing with the various input devices\r
+//\r
+// Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff),\r
+// User Mgr (for command line parms)\r
+//\r
+// Globals:\r
+// LastScan - The keyboard scan code of the last key pressed\r
+// LastASCII - The ASCII value of the last key pressed\r
+// DEBUG - there are more globals\r
+//\r
+\r
+//#include "ID_HEADS.H"
+#include "16_in.h"\r
+//#pragma hdrstop\r
+\r
+#define KeyInt 9 // The keyboard ISR number\r
+\r
+// Stuff for the joystick\r
+#define JoyScaleMax 32768\r
+#define JoyScaleShift 8\r
+#define MaxJoyValue 5000\r
+\r
+// Global variables\r
+ boolean JoystickCalibrated=false; // MDM (GAMERS EDGE) - added\r
+ ControlType ControlTypeUsed; // MDM (GAMERS EDGE) - added\r
+\r
+ boolean Keyboard[NumCodes],\r
+ JoysPresent[MaxJoys],\r
+ MousePresent;\r
+ boolean Paused;\r
+ char LastASCII;\r
+ ScanCode LastScan;\r
+ KeyboardDef KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}};\r
+ JoystickDef JoyDefs[MaxJoys];\r
+ ControlType Controls[MaxPlayers];\r
+\r
+// Demo DemoMode = demo_Off;\r
+// byte /*_1seg*/ *DemoBuffer;\r
+// word DemoOffset,DemoSize;\r
+\r
+// Internal variables\r
+static boolean IN_Started;\r
+static boolean CapsLock;\r
+static ScanCode CurCode,LastCode;\r
+static byte far ASCIINames[] = // Unshifted ASCII for scan codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8 ,9 , // 0\r
+ 'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0 ,'a','s', // 1\r
+ 'd','f','g','h','j','k','l',';',39 ,'`',0 ,92 ,'z','x','c','v', // 2\r
+ 'b','n','m',',','.','/',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4\r
+ '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+ far ShiftNames[] = // Shifted ASCII for scan codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8 ,9 , // 0\r
+ 'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0 ,'A','S', // 1\r
+ 'D','F','G','H','J','K','L',':',34 ,'~',0 ,'|','Z','X','C','V', // 2\r
+ 'B','N','M','<','>','?',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4\r
+ '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+ far SpecialNames[] = // ASCII for 0xe0 prefixed codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 0\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,13 ,0 ,0 ,0 , // 1\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 2\r
+ 0 ,0 ,0 ,0 ,0 ,'/',0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 4\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+\r
+#if 0\r
+ *ScanNames[] = // Scan code names with single chars\r
+ {\r
+ "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?",\r
+ "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S",\r
+ "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V",\r
+ "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?",\r
+ "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?"\r
+ }, // DEBUG - consolidate these\r
+#endif\r
+\r
+ far ExtScanCodes[] = // Scan codes with >1 char names\r
+ {\r
+ 1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,\r
+ 0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36,\r
+ 0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48,\r
+ 0x50,0x4b,0x4d,0x00\r
+ };\r
+#if 0\r
+ *ExtScanNames[] = // Names corresponding to ExtScanCodes\r
+ {\r
+ "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4",\r
+ "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft",\r
+ "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up",\r
+ "Down","Left","Right",""\r
+ };\r
+#endif\r
+static Direction DirTable[] = // Quick lookup for total direction\r
+ {\r
+ dir_NorthWest, dir_North, dir_NorthEast,\r
+ dir_West, dir_None, dir_East,\r
+ dir_SouthWest, dir_South, dir_SouthEast\r
+ };\r
+\r
+static void (*INL_KeyHook)(void);\r
+static void interrupt (*OldKeyVect)(void);\r
+\r
+static char *ParmStrings[] = {"nojoys","nomouse",nil};\r
+\r
+// Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_KeyService() - Handles a keyboard interrupt (key up/down)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+INL_KeyService(void)\r
+{\r
+static boolean special;\r
+ byte k,c,\r
+ temp;\r
+\r
+ k = inp(0x60); // Get the scan code\r
+\r
+ // Tell the XT keyboard controller to clear the key\r
+ outp(0x61,(temp = inp(0x61)) | 0x80);\r
+ outp(0x61,temp);\r
+\r
+ if (k == 0xe0) // Special key prefix\r
+ special = true;\r
+ else if (k == 0xe1) // Handle Pause key\r
+ Paused = true;\r
+ else\r
+ {\r
+ if (k & 0x80) // Break code\r
+ {\r
+ k &= 0x7f;\r
+\r
+// DEBUG - handle special keys: ctl-alt-delete, print scrn\r
+\r
+ Keyboard[k] = false;\r
+ }\r
+ else // Make code\r
+ {\r
+ LastCode = CurCode;\r
+ CurCode = LastScan = k;\r
+ Keyboard[k] = true;\r
+\r
+ if (special)\r
+ c = SpecialNames[k];\r
+ else\r
+ {\r
+ if (k == sc_CapsLock)\r
+ {\r
+ CapsLock ^= true;\r
+ // DEBUG - make caps lock light work\r
+ }\r
+\r
+ if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted\r
+ {\r
+ c = ShiftNames[k];\r
+ if ((c >= 'A') && (c <= 'Z') && CapsLock)\r
+ c += 'a' - 'A';\r
+ }\r
+ else\r
+ {\r
+ c = ASCIINames[k];\r
+ if ((c >= 'a') && (c <= 'z') && CapsLock)\r
+ c -= 'a' - 'A';\r
+ }\r
+ }\r
+ if (c)\r
+ LastASCII = c;\r
+ }\r
+\r
+ special = false;\r
+ }\r
+\r
+ if (INL_KeyHook && !special)\r
+ INL_KeyHook();\r
+ outp(0x20,0x20);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetMouseDelta() - Gets the amount that the mouse has moved from the\r
+// mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetMouseDelta(int *x,int *y)\r
+{\r
+ Mouse(MDelta);\r
+ *x = _CX;\r
+ *y = _DX;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetMouseButtons() - Gets the status of the mouse buttons from the\r
+// mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetMouseButtons(void)\r
+{\r
+ word buttons;\r
+\r
+ Mouse(MButtons);\r
+ buttons = _BX;\r
+ return(buttons);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetJoyAbs() - Reads the absolute position of the specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_GetJoyAbs(word joy,word *xp,word *yp)\r
+{\r
+ byte xb,yb,\r
+ xs,ys;\r
+ word x,y;\r
+\r
+ x = y = 0;\r
+ xs = joy? 2 : 0; // Find shift value for x axis\r
+ xb = 1 << xs; // Use shift value to get x bit mask\r
+ ys = joy? 3 : 1; // Do the same for y axis\r
+ yb = 1 << ys;\r
+\r
+// Read the absolute joystick values
+ __asm
+ {\r
+ pushf // Save some registers\r
+ push si\r
+ push di\r
+ cli // Make sure an interrupt doesn't screw the timings\r
+\r
+\r
+ mov dx,0x201\r
+ in al,dx\r
+ out dx,al // Clear the resistors\r
+\r
+ mov ah,[xb] // Get masks into registers\r
+ mov ch,[yb]\r
+\r
+ xor si,si // Clear count registers\r
+ xor di,di\r
+ xor bh,bh // Clear high byte of bx for later\r
+
+ push bp // Don't mess up stack frame\r
+ mov bp,MaxJoyValue\r
+\r
+loop:\r
+ in al,dx // Get bits indicating whether all are finished\r
+\r
+ dec bp // Check bounding register\r
+ jz done // We have a silly value - abort\r
+\r
+ mov bl,al // Duplicate the bits\r
+ and bl,ah // Mask off useless bits (in [xb])\r
+ add si,bx // Possibly increment count register\r
+ mov cl,bl // Save for testing later\r
+\r
+ mov bl,al\r
+ and bl,ch // [yb]\r
+ add di,bx\r
+\r
+ add cl,bl\r
+ jnz loop // If both bits were 0, drop out\r
+\r
+done:\r
+ pop bp\r
+
+ mov cl,[xs] // Get the number of bits to shift\r
+ shr si,cl // and shift the count that many times\r
+\r
+ mov cl,[ys]\r
+ shr di,cl\r
+\r
+ mov [x],si // Store the values into the variables\r
+ mov [y],di\r
+
+ pop di\r
+ pop si\r
+ popf // Restore the registers
+ }\r
+\r
+ *xp = x;\r
+ *yp = y;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetJoyDelta() - Returns the relative movement of the specified\r
+// joystick (from +/-127, scaled adaptively)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive)\r
+{\r
+ word x,y;\r
+ dword time;\r
+ JoystickDef *def;\r
+static dword lasttime;\r
+\r
+ IN_GetJoyAbs(joy,&x,&y);\r
+ def = JoyDefs + joy;\r
+\r
+ if (x < def->threshMinX)\r
+ {\r
+ if (x < def->joyMinX)\r
+ x = def->joyMinX;\r
+\r
+ x = -(x - def->threshMinX);\r
+ x *= def->joyMultXL;\r
+ x >>= JoyScaleShift;\r
+ *dx = (x > 127)? -127 : -x;\r
+ }\r
+ else if (x > def->threshMaxX)\r
+ {\r
+ if (x > def->joyMaxX)\r
+ x = def->joyMaxX;\r
+\r
+ x = x - def->threshMaxX;\r
+ x *= def->joyMultXH;\r
+ x >>= JoyScaleShift;\r
+ *dx = (x > 127)? 127 : x;\r
+ }\r
+ else\r
+ *dx = 0;\r
+\r
+ if (y < def->threshMinY)\r
+ {\r
+ if (y < def->joyMinY)\r
+ y = def->joyMinY;\r
+\r
+ y = -(y - def->threshMinY);\r
+ y *= def->joyMultYL;\r
+ y >>= JoyScaleShift;\r
+ *dy = (y > 127)? -127 : -y;\r
+ }\r
+ else if (y > def->threshMaxY)\r
+ {\r
+ if (y > def->joyMaxY)\r
+ y = def->joyMaxY;\r
+\r
+ y = y - def->threshMaxY;\r
+ y *= def->joyMultYH;\r
+ y >>= JoyScaleShift;\r
+ *dy = (y > 127)? 127 : y;\r
+ }\r
+ else\r
+ *dy = 0;\r
+\r
+ if (adaptive)\r
+ {\r
+ time = (TimeCount - lasttime) / 2;\r
+ if (time)\r
+ {\r
+ if (time > 8)\r
+ time = 8;\r
+ *dx *= time;\r
+ *dy *= time;\r
+ }\r
+ }\r
+ lasttime = TimeCount;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetJoyButtons() - Returns the button status of the specified\r
+// joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetJoyButtons(word joy)\r
+{\r
+register word result;\r
+\r
+ result = inp(0x201); // Get all the joystick buttons\r
+ result >>= joy? 6 : 4; // Shift into bits 0-1\r
+ result &= 3; // Mask off the useless bits\r
+ result ^= 3;\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetJoyButtonsDB() - Returns the de-bounced button status of the\r
+// specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+IN_GetJoyButtonsDB(word joy)\r
+{\r
+ dword lasttime;\r
+ word result1,result2;\r
+\r
+ do\r
+ {\r
+ result1 = INL_GetJoyButtons(joy);\r
+ lasttime = TimeCount;\r
+ while (TimeCount == lasttime)\r
+ ;\r
+ result2 = INL_GetJoyButtons(joy);\r
+ } while (result1 != result2);\r
+ return(result1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartKbd() - Sets up my keyboard stuff for use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_StartKbd(void)\r
+{\r
+ INL_KeyHook = 0; // Clear key hook\r
+\r
+ IN_ClearKeysDown();\r
+\r
+ OldKeyVect = getvect(KeyInt);\r
+ setvect(KeyInt,INL_KeyService);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutKbd() - Restores keyboard control to the BIOS\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutKbd(void)\r
+{\r
+ poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0); // Clear ctrl/alt/shift flags\r
+\r
+ setvect(KeyInt,OldKeyVect);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartMouse() - Detects and sets up the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartMouse(void)\r
+{\r
+ if (getvect(MouseInt))\r
+ {\r
+ Mouse(MReset);\r
+ if (_AX == 0xffff)\r
+ return(true);\r
+ }\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutMouse() - Cleans up after the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutMouse(void)\r
+{\r
+}\r
+\r
+//\r
+// INL_SetJoyScale() - Sets up scaling values for the specified joystick\r
+//\r
+static void\r
+INL_SetJoyScale(word joy)\r
+{\r
+ JoystickDef *def;\r
+\r
+ def = &JoyDefs[joy];\r
+ def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX);\r
+ def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX);\r
+ def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY);\r
+ def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale()\r
+// to set up scaling values\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy)\r
+{\r
+ word d,r;\r
+ JoystickDef *def;\r
+\r
+ def = &JoyDefs[joy];\r
+\r
+ def->joyMinX = minx;\r
+ def->joyMaxX = maxx;\r
+ r = maxx - minx;\r
+ d = r / 5;\r
+ def->threshMinX = ((r / 2) - d) + minx;\r
+ def->threshMaxX = ((r / 2) + d) + minx;\r
+\r
+ def->joyMinY = miny;\r
+ def->joyMaxY = maxy;\r
+ r = maxy - miny;\r
+ d = r / 5;\r
+ def->threshMinY = ((r / 2) - d) + miny;\r
+ def->threshMaxY = ((r / 2) + d) + miny;\r
+\r
+ INL_SetJoyScale(joy);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartJoy() - Detects & auto-configures the specified joystick\r
+// The auto-config assumes the joystick is centered\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartJoy(word joy)\r
+{\r
+ word x,y;\r
+\r
+ IN_GetJoyAbs(joy,&x,&y);\r
+\r
+ if\r
+ (\r
+ ((x == 0) || (x > MaxJoyValue - 10))\r
+ || ((y == 0) || (y > MaxJoyValue - 10))\r
+ )\r
+ return(false);\r
+ else\r
+ {\r
+ IN_SetupJoy(joy,0,x * 2,0,y * 2);\r
+ return(true);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutJoy() - Cleans up the joystick stuff\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutJoy(word joy)\r
+{\r
+ JoysPresent[joy] = false;\r
+}\r
+\r
+// Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Startup() - Starts up the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Startup(void)\r
+{\r
+ boolean checkjoys,checkmouse;\r
+ word i;\r
+\r
+ if (IN_Started)\r
+ return;\r
+\r
+ checkjoys = true;\r
+ checkmouse = true;\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ switch (US_CheckParm(_argv[i],ParmStrings))\r
+ {\r
+ case 0:\r
+ checkjoys = false;\r
+ break;\r
+ case 1:\r
+ checkmouse = false;\r
+ break;\r
+ }\r
+ }\r
+\r
+ INL_StartKbd();\r
+ MousePresent = checkmouse? INL_StartMouse() : false;\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ JoysPresent[i] = checkjoys? INL_StartJoy(i) : false;\r
+\r
+ IN_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Default() - Sets up default conditions for the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Default(boolean gotit,ControlType in)\r
+{\r
+ if\r
+ (\r
+ (!gotit)\r
+ || ((in == ctrl_Joystick1) && !JoysPresent[0])\r
+ || ((in == ctrl_Joystick2) && !JoysPresent[1])\r
+ || ((in == ctrl_Mouse) && !MousePresent)\r
+ )\r
+ in = ctrl_Keyboard1;\r
+ IN_SetControlType(0,in);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Shutdown() - Shuts down the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Shutdown(void)\r
+{\r
+ word i;\r
+\r
+ if (!IN_Started)\r
+ return;\r
+\r
+ INL_ShutMouse();\r
+ for (i = 0;i < MaxJoys;i++)\r
+ INL_ShutJoy(i);\r
+ INL_ShutKbd();\r
+\r
+ IN_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService()\r
+// everytime a real make/break code gets hit\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetKeyHook(void (*hook)())\r
+{\r
+ INL_KeyHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ClearKeyDown() - Clears the keyboard array\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ClearKeysDown(void)\r
+{\r
+ int i;\r
+\r
+ LastScan = sc_None;\r
+ LastASCII = key_None;\r
+ for (i = 0;i < NumCodes;i++)\r
+ Keyboard[i] = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy)\r
+{\r
+ if (buttons & (1 << 0))\r
+ info->button0 = true;\r
+ if (buttons & (1 << 1))\r
+ info->button1 = true;\r
+\r
+ info->x += dx;\r
+ info->y += dy;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ReadCursor() - Reads the input devices and fills in the cursor info\r
+// struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadCursor(CursorInfo *info)\r
+{\r
+ word i,\r
+ buttons;\r
+ int dx,dy;\r
+\r
+ info->x = info->y = 0;\r
+ info->button0 = info->button1 = false;\r
+\r
+ if (MousePresent)\r
+ {\r
+ buttons = INL_GetMouseButtons();\r
+ INL_GetMouseDelta(&dx,&dy);\r
+ INL_AdjustCursor(info,buttons,dx,dy);\r
+ }\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ {\r
+ if (!JoysPresent[i])\r
+ continue;\r
+\r
+ buttons = INL_GetJoyButtons(i);\r
+ INL_GetJoyDelta(i,&dx,&dy,true);\r
+ dx /= 64;\r
+ dy /= 64;\r
+ INL_AdjustCursor(info,buttons,dx,dy);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ReadControl() - Reads the device associated with the specified\r
+// player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+ boolean realdelta=false; // MDM (GAMERS EDGE)\r
+ byte dbyte;\r
+ word buttons;\r
+ int dx,dy;\r
+ Motion mx,my;\r
+ ControlType type;\r
+register KeyboardDef *def;\r
+\r
+ dx = dy = 0;\r
+ mx = my = motion_None;\r
+ buttons = 0;\r
+\r
+#if 0\r
+ if (DemoMode == demo_Playback)\r
+ {\r
+ dbyte = DemoBuffer[DemoOffset + 1];\r
+ my = (dbyte & 3) - 1;\r
+ mx = ((dbyte >> 2) & 3) - 1;\r
+ buttons = (dbyte >> 4) & 3;\r
+\r
+ if (!(--DemoBuffer[DemoOffset]))\r
+ {\r
+ DemoOffset += 2;\r
+ if (DemoOffset >= DemoSize)\r
+ DemoMode = demo_PlayDone;\r
+ }\r
+\r
+ realdelta = false;\r
+ }\r
+ else if (DemoMode == demo_PlayDone)\r
+ Quit("Demo playback exceeded");\r
+ else\r
+#endif\r
+ {\r
+ // MDM begin (GAMERS EDGE) - added this block\r
+ ControlTypeUsed = ctrl_None;\r
+\r
+ // Handle mouse input...\r
+ //\r
+ if ((MousePresent) && (ControlTypeUsed == ctrl_None))\r
+ {\r
+ INL_GetMouseDelta(&dx,&dy);\r
+ buttons = INL_GetMouseButtons();\r
+ realdelta = true;\r
+ if (dx || dy || buttons)\r
+ ControlTypeUsed = ctrl_Mouse;\r
+ }\r
+\r
+ // Handle joystick input...\r
+ //\r
+ if ((JoystickCalibrated) && (ControlTypeUsed == ctrl_None))\r
+ {\r
+ type = ctrl_Joystick1;\r
+ INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+ buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+ realdelta = true;\r
+ if (dx || dy || buttons)\r
+ ControlTypeUsed = ctrl_Joystick;\r
+ }\r
+\r
+ // Handle keyboard input...\r
+ //\r
+ if (ControlTypeUsed == ctrl_None)\r
+ {\r
+ type = ctrl_Keyboard1;\r
+ def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+ if (Keyboard[def->upleft])\r
+ mx = motion_Left,my = motion_Up;\r
+ else if (Keyboard[def->upright])\r
+ mx = motion_Right,my = motion_Up;\r
+ else if (Keyboard[def->downleft])\r
+ mx = motion_Left,my = motion_Down;\r
+ else if (Keyboard[def->downright])\r
+ mx = motion_Right,my = motion_Down;\r
+\r
+ if (Keyboard[def->up])\r
+ my = motion_Up;\r
+ else if (Keyboard[def->down])\r
+ my = motion_Down;\r
+\r
+ if (Keyboard[def->left])\r
+ mx = motion_Left;\r
+ else if (Keyboard[def->right])\r
+ mx = motion_Right;\r
+\r
+ if (Keyboard[def->button0])\r
+ buttons += 1 << 0;\r
+ if (Keyboard[def->button1])\r
+ buttons += 1 << 1;\r
+ realdelta = false;\r
+ if (mx || my || buttons)\r
+ ControlTypeUsed = ctrl_Keyboard;\r
+ } // MDM end (GAMERS EDGE)\r
+ }\r
+\r
+ if (realdelta)\r
+ {\r
+ mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+ my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+ }\r
+ else\r
+ {\r
+ dx = mx * 127;\r
+ dy = my * 127;\r
+ }\r
+\r
+ info->x = dx;\r
+ info->xaxis = mx;\r
+ info->y = dy;\r
+ info->yaxis = my;\r
+ info->button0 = buttons & (1 << 0);\r
+ info->button1 = buttons & (1 << 1);\r
+ info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+#if 0\r
+ if (DemoMode == demo_Record)\r
+ {\r
+ // Pack the control info into a byte\r
+ dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+ if\r
+ (\r
+ (DemoBuffer[DemoOffset + 1] == dbyte)\r
+ && (DemoBuffer[DemoOffset] < 255)\r
+ )\r
+ (DemoBuffer[DemoOffset])++;\r
+ else\r
+ {\r
+ if (DemoOffset || DemoBuffer[DemoOffset])\r
+ DemoOffset += 2;\r
+\r
+ if (DemoOffset >= DemoSize)\r
+ Quit("Demo buffer overflow");\r
+\r
+ DemoBuffer[DemoOffset] = 1;\r
+ DemoBuffer[DemoOffset + 1] = dbyte;\r
+ }\r
+ }\r
+#endif\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ReadControl() - Reads the device associated with the specified\r
+// player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+ boolean realdelta;\r
+ byte dbyte;\r
+ word buttons;\r
+ int dx,dy;\r
+ Motion mx,my;\r
+ ControlType type;\r
+register KeyboardDef *def;\r
+\r
+ dx = dy = 0;\r
+ mx = my = motion_None;\r
+ buttons = 0;\r
+\r
+#if 0\r
+ if (DemoMode == demo_Playback)\r
+ {\r
+ dbyte = DemoBuffer[DemoOffset + 1];\r
+ my = (dbyte & 3) - 1;\r
+ mx = ((dbyte >> 2) & 3) - 1;\r
+ buttons = (dbyte >> 4) & 3;\r
+\r
+ if (!(--DemoBuffer[DemoOffset]))\r
+ {\r
+ DemoOffset += 2;\r
+ if (DemoOffset >= DemoSize)\r
+ DemoMode = demo_PlayDone;\r
+ }\r
+\r
+ realdelta = false;\r
+ }\r
+ else if (DemoMode == demo_PlayDone)\r
+ Quit("Demo playback exceeded");\r
+ else\r
+#endif\r
+ {\r
+ switch (type = Controls[player])\r
+ {\r
+ case ctrl_Keyboard1:\r
+ case ctrl_Keyboard2:\r
+ def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+ if (Keyboard[def->upleft])\r
+ mx = motion_Left,my = motion_Up;\r
+ else if (Keyboard[def->upright])\r
+ mx = motion_Right,my = motion_Up;\r
+ else if (Keyboard[def->downleft])\r
+ mx = motion_Left,my = motion_Down;\r
+ else if (Keyboard[def->downright])\r
+ mx = motion_Right,my = motion_Down;\r
+\r
+ if (Keyboard[def->up])\r
+ my = motion_Up;\r
+ else if (Keyboard[def->down])\r
+ my = motion_Down;\r
+\r
+ if (Keyboard[def->left])\r
+ mx = motion_Left;\r
+ else if (Keyboard[def->right])\r
+ mx = motion_Right;\r
+\r
+ if (Keyboard[def->button0])\r
+ buttons += 1 << 0;\r
+ if (Keyboard[def->button1])\r
+ buttons += 1 << 1;\r
+ realdelta = false;\r
+ break;\r
+ case ctrl_Joystick1:\r
+ case ctrl_Joystick2:\r
+ INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+ buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+ realdelta = true;\r
+ break;\r
+ case ctrl_Mouse:\r
+ INL_GetMouseDelta(&dx,&dy);\r
+ buttons = INL_GetMouseButtons();\r
+ realdelta = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (realdelta)\r
+ {\r
+ mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+ my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+ }\r
+ else\r
+ {\r
+ dx = mx * 127;\r
+ dy = my * 127;\r
+ }\r
+\r
+ info->x = dx;\r
+ info->xaxis = mx;\r
+ info->y = dy;\r
+ info->yaxis = my;\r
+ info->button0 = buttons & (1 << 0);\r
+ info->button1 = buttons & (1 << 1);\r
+ info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+#if 0\r
+ if (DemoMode == demo_Record)\r
+ {\r
+ // Pack the control info into a byte\r
+ dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+ if\r
+ (\r
+ (DemoBuffer[DemoOffset + 1] == dbyte)\r
+ && (DemoBuffer[DemoOffset] < 255)\r
+ )\r
+ (DemoBuffer[DemoOffset])++;\r
+ else\r
+ {\r
+ if (DemoOffset || DemoBuffer[DemoOffset])\r
+ DemoOffset += 2;\r
+\r
+ if (DemoOffset >= DemoSize)\r
+ Quit("Demo buffer overflow");\r
+\r
+ DemoBuffer[DemoOffset] = 1;\r
+ DemoBuffer[DemoOffset + 1] = dbyte;\r
+ }\r
+ }\r
+#endif\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetControlType() - Sets the control type to be used by the specified\r
+// player\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetControlType(int player,ControlType type)\r
+{\r
+ // DEBUG - check that requested type is present?\r
+ Controls[player] = type;\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StartDemoRecord() - Starts the demo recording, using a buffer the\r
+// size passed. Returns if the buffer allocation was successful\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_StartDemoRecord(word bufsize)\r
+{\r
+ if (!bufsize)\r
+ return(false);\r
+\r
+ MM_GetPtr((memptr *)&DemoBuffer,bufsize);\r
+ DemoMode = demo_Record;\r
+ DemoSize = bufsize & ~1;\r
+ DemoOffset = 0;\r
+ DemoBuffer[0] = DemoBuffer[1] = 0;\r
+\r
+ return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StartDemoPlayback() - Plays back the demo pointed to of the given size\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StartDemoPlayback(byte /*_1seg*/ *buffer,word bufsize)\r
+{\r
+ DemoBuffer = buffer;\r
+ DemoMode = demo_Playback;\r
+ DemoSize = bufsize & ~1;\r
+ DemoOffset = 0;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StopDemo() - Turns off demo mode\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StopDemo(void)\r
+{\r
+ if ((DemoMode == demo_Record) && DemoOffset)\r
+ DemoOffset += 2;\r
+\r
+ DemoMode = demo_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_FreeDemoBuffer(void)\r
+{\r
+ if (DemoBuffer)\r
+ MM_FreePtr((memptr *)&DemoBuffer);\r
+}\r
+#endif\r
+\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetScanName() - Returns a string containing the name of the\r
+// specified scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+byte *\r
+IN_GetScanName(ScanCode scan)\r
+{\r
+ byte **p;\r
+ ScanCode far *s;\r
+\r
+ for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++)\r
+ if (*s == scan)\r
+ return(*p);\r
+\r
+ return(ScanNames[scan]);\r
+}\r
+#endif\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_WaitForKey() - Waits for a scan code, then clears LastScan and\r
+// returns the scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+ScanCode\r
+IN_WaitForKey(void)\r
+{\r
+ ScanCode result;\r
+\r
+ while (!(result = LastScan))\r
+ ;\r
+ LastScan = 0;\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and\r
+// returns the ASCII value\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char\r
+IN_WaitForASCII(void)\r
+{\r
+ char result;\r
+\r
+ while (!(result = LastASCII))\r
+ ;\r
+ LastASCII = '\0';\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_AckBack() - Waits for either an ASCII keypress or a button press\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_AckBack(void)\r
+{\r
+ word i;\r
+\r
+ while (!LastScan)\r
+ {\r
+ if (MousePresent)\r
+ {\r
+ if (INL_GetMouseButtons())\r
+ {\r
+ while (INL_GetMouseButtons())\r
+ ;\r
+ return;\r
+ }\r
+ }\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ {\r
+ if (JoysPresent[i])\r
+ {\r
+ if (IN_GetJoyButtonsDB(i))\r
+ {\r
+ while (IN_GetJoyButtonsDB(i))\r
+ ;\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ IN_ClearKey(LastScan);\r
+ LastScan = sc_None;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Ack() - Clears user input & then calls IN_AckBack()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Ack(void)\r
+{\r
+ word i;\r
+\r
+ IN_ClearKey(LastScan);\r
+ LastScan = sc_None;\r
+\r
+ if (MousePresent)\r
+ while (INL_GetMouseButtons())\r
+ ;\r
+ for (i = 0;i < MaxJoys;i++)\r
+ if (JoysPresent[i])\r
+ while (IN_GetJoyButtonsDB(i))\r
+ ;\r
+\r
+ IN_AckBack();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_IsUserInput() - Returns true if a key has been pressed or a button\r
+// is down\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_IsUserInput(void)\r
+{\r
+ boolean result;\r
+ word i;\r
+\r
+ result = LastScan;\r
+\r
+ if (MousePresent)\r
+ if (INL_GetMouseButtons())\r
+ result = true;\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ if (JoysPresent[i])\r
+ if (INL_GetJoyButtons(i))\r
+ result = true;\r
+\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_UserInput() - Waits for the specified delay time (in ticks) or the\r
+// user pressing a key or a mouse button. If the clear flag is set, it\r
+// then either clears the key or waits for the user to let the mouse\r
+// button up.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_UserInput(dword delay,boolean clear)\r
+{\r
+ dword lasttime;\r
+\r
+ lasttime = TimeCount;\r
+ do\r
+ {\r
+ if (IN_IsUserInput())\r
+ {\r
+ if (clear)\r
+ IN_AckBack();\r
+ return(true);\r
+ }\r
+ } while (TimeCount - lasttime < delay);\r
+ return(false);\r
+}\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_IN.h - Header file for Input Manager\r
+// v1.0d1\r
+// By Jason Blochowiak\r
+//\r
+
+#include "lib_head.h"
+\r
+#ifndef __TYPES__\r
+//#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef __ID_IN__\r
+#define __ID_IN__\r
+\r
+#ifdef __DEBUG__\r
+#define __DEBUG_InputMgr__\r
+#endif\r
+\r
+#define MaxPlayers 4\r
+#define MaxKbds 2\r
+#define MaxJoys 2\r
+#define NumCodes 128\r
+\r
+typedef byte ScanCode;\r
+#define sc_None 0\r
+#define sc_Bad 0xff\r
+#define sc_Return 0x1c\r
+#define sc_Enter sc_Return\r
+#define sc_Escape 0x01\r
+#define sc_Space 0x39\r
+#define sc_BackSpace 0x0e\r
+#define sc_Tab 0x0f\r
+#define sc_Alt 0x38\r
+#define sc_Control 0x1d\r
+#define sc_CapsLock 0x3a\r
+#define sc_LShift 0x2a\r
+#define sc_RShift 0x36\r
+#define sc_UpArrow 0x48\r
+#define sc_DownArrow 0x50\r
+#define sc_LeftArrow 0x4b\r
+#define sc_RightArrow 0x4d\r
+#define sc_Insert 0x52\r
+#define sc_Delete 0x53\r
+#define sc_Home 0x47\r
+#define sc_End 0x4f\r
+#define sc_PgUp 0x49\r
+#define sc_PgDn 0x51\r
+#define sc_F1 0x3b\r
+#define sc_F2 0x3c\r
+#define sc_F3 0x3d\r
+#define sc_F4 0x3e\r
+#define sc_F5 0x3f\r
+#define sc_F6 0x40\r
+#define sc_F7 0x41\r
+#define sc_F8 0x42\r
+#define sc_F9 0x43\r
+#define sc_F10 0x44\r
+#define sc_F11 0x57\r
+#define sc_F12 0x59\r
+\r
+#define sc_A 0x1e\r
+#define sc_B 0x30\r
+#define sc_C 0x2e\r
+#define sc_D 0x20\r
+#define sc_E 0x12\r
+#define sc_F 0x21\r
+#define sc_G 0x22\r
+#define sc_H 0x23\r
+#define sc_I 0x17\r
+#define sc_J 0x24\r
+#define sc_K 0x25\r
+#define sc_L 0x26\r
+#define sc_M 0x32\r
+#define sc_N 0x31\r
+#define sc_O 0x18\r
+#define sc_P 0x19\r
+#define sc_Q 0x10\r
+#define sc_R 0x13\r
+#define sc_S 0x1f\r
+#define sc_T 0x14\r
+#define sc_U 0x16\r
+#define sc_V 0x2f\r
+#define sc_W 0x11\r
+#define sc_X 0x2d\r
+#define sc_Y 0x15\r
+#define sc_Z 0x2c\r
+\r
+#define key_None 0\r
+#define key_Return 0x0d\r
+#define key_Enter key_Return\r
+#define key_Escape 0x1b\r
+#define key_Space 0x20\r
+#define key_BackSpace 0x08\r
+#define key_Tab 0x09\r
+#define key_Delete 0x7f\r
+\r
+// Stuff for the mouse\r
+#define MReset 0\r
+#define MButtons 3\r
+#define MDelta 11\r
+\r
+#define MouseInt 0x33\r
+#define Mouse(x) _AX = x,_dos_geninterrupt(MouseInt)\r
+\r
+typedef enum {\r
+ demo_Off,demo_Record,demo_Playback,demo_PlayDone\r
+ } Demo;\r
+typedef enum {\r
+ ctrl_None, // MDM (GAMERS EDGE) - added\r
+ ctrl_Keyboard,\r
+ ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2,\r
+ ctrl_Joystick,\r
+ ctrl_Joystick1 = ctrl_Joystick,ctrl_Joystick2,\r
+ ctrl_Mouse\r
+ } ControlType;\r
+typedef enum {\r
+ motion_Left = -1,motion_Up = -1,\r
+ motion_None = 0,\r
+ motion_Right = 1,motion_Down = 1\r
+ } Motion;\r
+typedef enum {\r
+ dir_North,dir_NorthEast,\r
+ dir_East,dir_SouthEast,\r
+ dir_South,dir_SouthWest,\r
+ dir_West,dir_NorthWest,\r
+ dir_None\r
+ } Direction;\r
+typedef struct {\r
+ boolean button0,button1;\r
+ int x,y;\r
+ Motion xaxis,yaxis;\r
+ Direction dir;\r
+ } CursorInfo;\r
+typedef CursorInfo ControlInfo;\r
+typedef struct {\r
+ ScanCode button0,button1,\r
+ upleft, up, upright,\r
+ left, right,\r
+ downleft, down, downright;\r
+ } KeyboardDef;\r
+typedef struct {\r
+ word joyMinX,joyMinY,\r
+ threshMinX,threshMinY,\r
+ threshMaxX,threshMaxY,\r
+ joyMaxX,joyMaxY,\r
+ joyMultXL,joyMultYL,\r
+ joyMultXH,joyMultYH;\r
+ } JoystickDef;\r
+// Global variables\r
+extern boolean Keyboard[],\r
+ MousePresent,\r
+ JoysPresent[];\r
+extern boolean Paused;\r
+extern char LastASCII;\r
+extern ScanCode LastScan;\r
+extern KeyboardDef KbdDefs[];\r
+extern JoystickDef JoyDefs[];\r
+extern ControlType Controls[MaxPlayers];\r
+\r
+extern boolean JoystickCalibrated; // MDM (GAMERS EDGE) - added\r
+extern ControlType ControlTypeUsed; // MDM (GAMERS EDGE) - added\r
+\r
+extern Demo DemoMode;\r
+extern byte /*_seg*/ *DemoBuffer;\r
+extern word DemoOffset,DemoSize;\r
+\r
+// Function prototypes\r
+#define IN_KeyDown(code) (Keyboard[(code)])\r
+#define IN_ClearKey(code) {Keyboard[code] = false;\\r
+ if (code == LastScan) LastScan = sc_None;}\r
+\r
+// DEBUG - put names in prototypes\r
+extern void IN_Startup(void),IN_Shutdown(void),\r
+ IN_Default(boolean gotit,ControlType in),\r
+ IN_SetKeyHook(void (*)()),\r
+ IN_ClearKeysDown(void),\r
+ IN_ReadCursor(CursorInfo *),\r
+ IN_ReadControl(int,ControlInfo *),\r
+ IN_SetControlType(int,ControlType),\r
+ IN_GetJoyAbs(word joy,word *xp,word *yp),\r
+ IN_SetupJoy(word joy,word minx,word maxx,\r
+ word miny,word maxy),\r
+ IN_StartDemoPlayback(byte /*_seg*/ *buffer,word bufsize),\r
+ IN_StopDemo(void),IN_FreeDemoBuffer(void),\r
+ IN_Ack(void),IN_AckBack(void);\r
+extern boolean IN_UserInput(dword delay,boolean clear),\r
+ IN_IsUserInput(void),\r
+ IN_StartDemoRecord(word bufsize);\r
+extern byte *IN_GetScanName(ScanCode);\r
+extern char IN_WaitForASCII(void);\r
+extern ScanCode IN_WaitForKey(void);\r
+extern word IN_GetJoyButtonsDB(word joy);\r
+\r
+#endif\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// NEWMM.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+ ID software memory manager\r
+ --------------------------\r
+\r
+Primary coder: John Carmack\r
+\r
+RELIES ON\r
+---------\r
+Quit (char *error) function\r
+\r
+\r
+WORK TO DO\r
+----------\r
+MM_SizePtr to change the size of a given pointer\r
+\r
+Multiple purge levels utilized\r
+\r
+EMS / XMS unmanaged routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+//#include "LIB_HEAD.H"
+#include "16_mm.h"
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL INFO\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define LOCKBIT 0x80 // if set in attributes, block cannot be moved\r
+#define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first\r
+#define PURGEMASK 0xfffc\r
+#define BASEATTRIBUTES 0 // unlocked, non purgable\r
+\r
+#define MAXUMBS 10\r
+\r
+typedef struct mmblockstruct\r
+{\r
+ unsigned start,length;\r
+ unsigned attributes;\r
+ memptr *useptr; // pointer to the segment start\r
+ struct mmblockstruct far *next;\r
+} mmblocktype;\r
+\r
+\r
+//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;}\r
+#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}\r
+#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+mminfotype mminfo;\r
+memptr bufferseg;\r
+boolean mmerror;\r
+\r
+void (* beforesort) (void);\r
+void (* aftersort) (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean mmstarted;\r
+
+void huge *hugeheap;\r
+void far *farheap;\r
+void *nearheap;\r
+\r
+mmblocktype far mmblocks[MAXBLOCKS]\r
+ ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;\r
+\r
+boolean bombonerror;\r
+\r
+unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;\r
+\r
+void (* XMSaddr) (void); // far pointer to XMS driver\r
+\r
+unsigned numUMBs,UMBbase[MAXUMBS];\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForEMS\r
+=\r
+= Routine from p36 of Extending DOS\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForEMS (void)\r
+{
+ boolean emmcfems;
+ char emmname[] = "EMMXXXX0";
+
+ __asm {
+ mov dx,OFF emmname\r
+ mov ax,0x3d00\r
+ int 0x21 // try to open EMMXXXX0 device\r
+ jc error\r
+\r
+ mov bx,ax\r
+ mov ax,0x4400\r
+\r
+ int 0x21 // get device info\r
+ jc error\r
+\r
+ and dx,0x80\r
+ jz error\r
+\r
+ mov ax,0x4407\r
+\r
+ int 0x21 // get status\r
+ jc error\r
+ or al,al\r
+ jz error\r
+\r
+ mov ah,0x3e\r
+ int 0x21 // close handle\r
+ jc error
+ //\r
+ // EMS is good\r
+ //
+ mov emmcfems,1
+ jmp End
+ error:
+ //\r
+ // EMS is bad\r
+ //\r
+ mov emmcfems,0
+ End:
+ }
+ return(emmcfems);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupEMS (void)\r
+{\r
+ char str[80],str2[10];\r
+ unsigned error;\r
+\r
+ totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;\r
+\r
+ __asm {\r
+ mov ah,EMS_STATUS\r
+ int EMS_INT // make sure EMS hardware is present\r
+ or ah,ah\r
+ jnz error\r
+\r
+ mov ah,EMS_VERSION\r
+ int EMS_INT\r
+ or ah,ah\r
+ jnz error\r
+ cmp al,0x32 // only work on ems 3.2 or greater\r
+ jb error\r
+\r
+ mov ah,EMS_GETFRAME\r
+ int EMS_INT // find the page frame address\r
+ or ah,ah\r
+ jnz error\r
+ mov [EMSpageframe],bx\r
+\r
+ mov ah,EMS_GETPAGES\r
+ int EMS_INT // find out how much EMS is there\r
+ or ah,ah\r
+ jnz error\r
+ mov [totalEMSpages],dx\r
+ mov [freeEMSpages],bx\r
+ or bx,bx\r
+ jz noEMS // no EMS at all to allocate\r
+\r
+ cmp bx,4\r
+ jle getpages // there is only 1,2,3,or 4 pages\r
+ mov bx,4 // we can't use more than 4 pages\r
+ }\r
+\r
+getpages:\r
+asm {\r
+ mov [EMSpagesmapped],bx\r
+ mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS\r
+ int EMS_INT\r
+ or ah,ah\r
+ jnz error\r
+ mov [EMShandle],dx\r
+ }\r
+ return;\r
+\r
+error:\r
+ error = _AH;\r
+ strcpy (str,"MML_SetupEMS: EMS error 0x");\r
+ itoa(error,str2,16);\r
+ strcpy (str,str2);\r
+ Quit (str);\r
+\r
+noEMS:\r
+;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_ShutdownEMS (void)\r
+{\r
+ if (!EMShandle)\r
+ return;\r
+\r
+ {\r
+ mov ah,EMS_FREEPAGES\r
+ mov dx,[EMShandle]\r
+ int EMS_INT\r
+ or ah,ah\r
+ jz ok\r
+ }\r
+\r
+ Quit ("MML_ShutdownEMS: Error freeing EMS!");\r
+\r
+ok:\r
+;\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= MM_MapEMS\r
+=\r
+= Maps the 64k of EMS used by memory manager into the page frame\r
+= for general use. This only needs to be called if you are keeping\r
+= other things in EMS.\r
+=\r
+====================\r
+*/\r
+\r
+void MM_MapEMS (void)\r
+{\r
+ char str[80],str2[10];\r
+ unsigned error;\r
+ int i;\r
+\r
+ for (i=0;i<EMSpagesmapped;i++)\r
+ {\r
+ {\r
+ mov ah,EMS_MAPPAGE\r
+ mov bx,[i] // logical page\r
+ mov al,bl // physical page\r
+ mov dx,[EMShandle] // handle\r
+ int EMS_INT\r
+ or ah,ah\r
+ jnz error\r
+ }\r
+ }\r
+\r
+ return;\r
+\r
+error:\r
+ error = _AH;\r
+ strcpy (str,"MM_MapEMS: EMS error 0x");\r
+ itoa(error,str2,16);\r
+ strcpy (str,str2);\r
+ Quit (str);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForXMS\r
+=\r
+= Check for XMM driver\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForXMS (void)\r
+{\r
+ numUMBs = 0;\r
+\r
+asm {\r
+ mov ax,0x4300\r
+ int 0x2f // query status of installed diver\r
+ cmp al,0x80\r
+ je good\r
+ }\r
+ return false;\r
+good:\r
+ return true;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupXMS\r
+=\r
+= Try to allocate all upper memory block\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupXMS (void)\r
+{\r
+ unsigned base,size;\r
+\r
+ {\r
+ mov ax,0x4310\r
+ int 0x2f\r
+ mov [WORD PTR XMSaddr],bx\r
+ mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver\r
+ }\r
+\r
+getmemory:\r
+ {\r
+ mov ah,XMS_ALLOCUMB\r
+ mov dx,0xffff // try for largest block possible\r
+ call [DWORD PTR XMSaddr]\r
+ or ax,ax\r
+ jnz gotone\r
+\r
+ cmp bl,0xb0 // error: smaller UMB is available\r
+ jne done;\r
+\r
+ mov ah,XMS_ALLOCUMB\r
+ call [DWORD PTR XMSaddr] // DX holds largest available UMB\r
+ or ax,ax\r
+ jz done // another error...\r
+ }\r
+\r
+gotone:\r
+ {\r
+ mov [base],bx\r
+ mov [size],dx\r
+ }\r
+ MML_UseSpace (base,size);\r
+ mminfo.XMSmem += size*16;\r
+ UMBbase[numUMBs] = base;\r
+ numUMBs++;\r
+ if (numUMBs < MAXUMBS)\r
+ goto getmemory;\r
+\r
+done:;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownXMS\r
+=\r
+======================\r
+*/\r
+\r
+void MML_ShutdownXMS (void)\r
+{\r
+ int i;\r
+ unsigned base;\r
+\r
+ for (i=0;i<numUMBs;i++)\r
+ {\r
+ base = UMBbase[i];\r
+\r
+ mov ah,XMS_FREEUMB\r
+ mov dx,[base]\r
+ call [DWORD PTR XMSaddr]\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_UseSpace\r
+=\r
+= Marks a range of paragraphs as usable by the memory manager\r
+= This is used to mark space for the near heap, far heap, ems page frame,\r
+= and upper memory blocks\r
+=\r
+======================\r
+*/\r
+\r
+void MML_UseSpace (unsigned segstart, unsigned seglength)\r
+{\r
+ mmblocktype far *scan,far *last;\r
+ unsigned oldend;\r
+ long extra;\r
+\r
+ scan = last = mmhead;\r
+ mmrover = mmhead; // reset rover to start of memory\r
+\r
+//\r
+// search for the block that contains the range of segments\r
+//\r
+ while (scan->start+scan->length < segstart)\r
+ {\r
+ last = scan;\r
+ scan = scan->next;\r
+ }\r
+\r
+//\r
+// take the given range out of the block\r
+//\r
+ oldend = scan->start + scan->length;\r
+ extra = oldend - (segstart+seglength);\r
+ if (extra < 0)\r
+ Quit ("MML_UseSpace: Segment spans two blocks!");\r
+\r
+ if (segstart == scan->start)\r
+ {\r
+ last->next = scan->next; // unlink block\r
+ FREEBLOCK(scan);\r
+ scan = last;\r
+ }\r
+ else\r
+ scan->length = segstart-scan->start; // shorten block\r
+\r
+ if (extra > 0)\r
+ {\r
+ GETNEWBLOCK;\r
+ mmnew->next = scan->next;\r
+ scan->next = mmnew;\r
+ mmnew->start = segstart+seglength;\r
+ mmnew->length = extra;\r
+ mmnew->attributes = LOCKBIT;\r
+ }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MML_ClearBlock\r
+=\r
+= We are out of blocks, so free a purgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MML_ClearBlock (void)\r
+{\r
+ mmblocktype far *scan,far *last;\r
+\r
+ scan = mmhead->next;\r
+\r
+ while (scan)\r
+ {\r
+ if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )\r
+ {\r
+ MM_FreePtr(scan->useptr);\r
+ return;\r
+ }\r
+ scan = scan->next;\r
+ }\r
+\r
+ Quit ("MM_ClearBlock: No purgable blocks!");\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= MM_Startup\r
+=\r
+= Grabs all space from turbo with malloc/farmalloc\r
+= Allocates bufferseg misc buffer\r
+=\r
+===================\r
+*/\r
+\r
+static char *ParmStrings[] = {"noems","noxms",""};\r
+\r
+void MM_Startup (void)\r
+{\r
+ int i;\r
+ unsigned long length;\r
+ void far *start;\r
+ unsigned segstart,seglength,endfree;\r
+\r
+ if (mmstarted)\r
+ MM_Shutdown ();\r
+\r
+\r
+ mmstarted = true;\r
+ bombonerror = true;\r
+//\r
+// set up the linked list (everything in the free list;\r
+//\r
+ mmhead = NULL;\r
+ mmfree = &mmblocks[0];\r
+ for (i=0;i<MAXBLOCKS-1;i++)\r
+ mmblocks[i].next = &mmblocks[i+1];\r
+ mmblocks[i].next = NULL;\r
+\r
+//\r
+// locked block of all memory until we punch out free space\r
+//\r
+ GETNEWBLOCK;\r
+ mmhead = mmnew; // this will allways be the first node\r
+ mmnew->start = 0;\r
+ mmnew->length = 0xffff;\r
+ mmnew->attributes = LOCKBIT;\r
+ mmnew->next = NULL;\r
+ mmrover = mmhead;\r
+\r
+\r
+//\r
+// get all available near conventional memory segments\r
+//\r
+ length=coreleft();\r
+ start = (void far *)(nearheap = malloc(length));\r
+\r
+ length -= 16-(FP_OFF(start)&15);\r
+ length -= SAVENEARHEAP;\r
+ seglength = length / 16; // now in paragraphs\r
+ segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+ MML_UseSpace (segstart,seglength);\r
+ mminfo.nearheap = length;\r
+\r
+//\r
+// get all available far conventional memory segments\r
+//\r
+ length=farcoreleft();\r
+ start = farheap = farmalloc(length);\r
+ length -= 16-(FP_OFF(start)&15);\r
+ length -= SAVEFARHEAP;\r
+ seglength = length / 16; // now in paragraphs\r
+ segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+ MML_UseSpace (segstart,seglength);\r
+ mminfo.farheap = length;\r
+ mminfo.mainmem = mminfo.nearheap + mminfo.farheap;\r
+\r
+\r
+//\r
+// detect EMS and allocate up to 64K at page frame\r
+//\r
+ mminfo.EMSmem = 0;\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+ goto emsskip; // param NOEMS\r
+ }\r
+\r
+ if (MML_CheckForEMS())\r
+ {\r
+ MML_SetupEMS(); // allocate space\r
+ MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);\r
+ MM_MapEMS(); // map in used pages\r
+ mminfo.EMSmem = EMSpagesmapped*0x4000l;\r
+ }\r
+\r
+//\r
+// detect XMS and get upper memory blocks\r
+//\r
+emsskip:\r
+ mminfo.XMSmem = 0;\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+ goto xmsskip; // param NOXMS\r
+ }\r
+\r
+ if (MML_CheckForXMS())\r
+ MML_SetupXMS(); // allocate as many UMBs as possible\r
+\r
+//\r
+// allocate the misc buffer\r
+//\r
+xmsskip:\r
+ mmrover = mmhead; // start looking for space after low block\r
+\r
+ MM_GetPtr (&bufferseg,BUFFERSIZE);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_Shutdown\r
+=\r
+= Frees all conventional, EMS, and XMS allocated\r
+=\r
+====================\r
+*/\r
+\r
+void MM_Shutdown (void)\r
+{\r
+ if (!mmstarted)\r
+ return;\r
+\r
+ farfree (farheap);\r
+ free (nearheap);\r
+ MML_ShutdownEMS ();\r
+ MML_ShutdownXMS ();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_GetPtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size)\r
+{\r
+ mmblocktype far *scan,far *lastscan,far *endscan\r
+ ,far *purge,far *next;\r
+ int search;\r
+ unsigned needed,startseg;\r
+\r
+ needed = (size+15)/16; // convert size from bytes to paragraphs\r
+\r
+ GETNEWBLOCK; // fill in start and next after a spot is found\r
+ mmnew->length = needed;\r
+ mmnew->useptr = baseptr;\r
+ mmnew->attributes = BASEATTRIBUTES;\r
+\r
+ for (search = 0; search<3; search++)\r
+ {\r
+ //\r
+ // first search: try to allocate right after the rover, then on up\r
+ // second search: search from the head pointer up to the rover\r
+ // third search: compress memory, then scan from start\r
+ if (search == 1 && mmrover == mmhead)\r
+ search++;\r
+\r
+ switch (search)\r
+ {\r
+ case 0:\r
+ lastscan = mmrover;\r
+ scan = mmrover->next;\r
+ endscan = NULL;\r
+ break;\r
+ case 1:\r
+ lastscan = mmhead;\r
+ scan = mmhead->next;\r
+ endscan = mmrover;\r
+ break;\r
+ case 2:\r
+ MM_SortMem ();\r
+ lastscan = mmhead;\r
+ scan = mmhead->next;\r
+ endscan = NULL;\r
+ break;\r
+ }\r
+\r
+ startseg = lastscan->start + lastscan->length;\r
+\r
+ while (scan != endscan)\r
+ {\r
+ if (scan->start - startseg >= needed)\r
+ {\r
+ //\r
+ // got enough space between the end of lastscan and\r
+ // the start of scan, so throw out anything in the middle\r
+ // and allocate the new block\r
+ //\r
+ purge = lastscan->next;\r
+ lastscan->next = mmnew;\r
+ mmnew->start = *(unsigned *)baseptr = startseg;\r
+ mmnew->next = scan;\r
+ while ( purge != scan)\r
+ { // free the purgable block\r
+ next = purge->next;\r
+ FREEBLOCK(purge);\r
+ purge = next; // purge another if not at scan\r
+ }\r
+ mmrover = mmnew;\r
+ return; // good allocation!\r
+ }\r
+\r
+ //\r
+ // if this block is purge level zero or locked, skip past it\r
+ //\r
+ if ( (scan->attributes & LOCKBIT)\r
+ || !(scan->attributes & PURGEBITS) )\r
+ {\r
+ lastscan = scan;\r
+ startseg = lastscan->start + lastscan->length;\r
+ }\r
+\r
+\r
+ scan=scan->next; // look at next line\r
+ }\r
+ }\r
+\r
+ if (bombonerror)\r
+ Quit (OUT_OF_MEM_MSG,(size-mminfo.nearheap));\r
+ else\r
+ mmerror = true;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_FreePtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_FreePtr (memptr *baseptr)\r
+{\r
+ mmblocktype far *scan,far *last;\r
+\r
+ last = mmhead;\r
+ scan = last->next;\r
+\r
+ if (baseptr == mmrover->useptr) // removed the last allocated block\r
+ mmrover = mmhead;\r
+\r
+ while (scan->useptr != baseptr && scan)\r
+ {\r
+ last = scan;\r
+ scan = scan->next;\r
+ }\r
+\r
+ if (!scan)\r
+ Quit ("MM_FreePtr: Block not found!");\r
+\r
+ last->next = scan->next;\r
+\r
+ FREEBLOCK(scan);\r
+}\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetPurge\r
+=\r
+= Sets the purge level for a block (locked blocks cannot be made purgable)\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge)\r
+{\r
+ mmblocktype far *start;\r
+\r
+ start = mmrover;\r
+\r
+ do\r
+ {\r
+ if (mmrover->useptr == baseptr)\r
+ break;\r
+\r
+ mmrover = mmrover->next;\r
+\r
+ if (!mmrover)\r
+ mmrover = mmhead;\r
+ else if (mmrover == start)\r
+ Quit ("MM_SetPurge: Block not found!");\r
+\r
+ } while (1);\r
+\r
+ mmrover->attributes &= ~PURGEBITS;\r
+ mmrover->attributes |= purge;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetLock\r
+=\r
+= Locks / unlocks the block\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetLock (memptr *baseptr, boolean locked)\r
+{\r
+ mmblocktype far *start;\r
+\r
+ start = mmrover;\r
+\r
+ do\r
+ {\r
+ if (mmrover->useptr == baseptr)\r
+ break;\r
+\r
+ mmrover = mmrover->next;\r
+\r
+ if (!mmrover)\r
+ mmrover = mmhead;\r
+ else if (mmrover == start)\r
+ Quit ("MM_SetLock: Block not found!");\r
+\r
+ } while (1);\r
+\r
+ mmrover->attributes &= ~LOCKBIT;\r
+ mmrover->attributes |= locked*LOCKBIT;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SortMem\r
+=\r
+= Throws out all purgable stuff and compresses movable blocks\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SortMem (void)\r
+{\r
+ mmblocktype far *scan,far *last,far *next;\r
+ unsigned start,length,source,dest,oldborder;\r
+ int playing;\r
+\r
+ //\r
+ // lock down a currently playing sound\r
+ //\r
+ playing = SD_SoundPlaying ();\r
+ if (playing)\r
+ {\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_PC:\r
+ playing += STARTPCSOUNDS;\r
+ break;\r
+ case sdm_AdLib:\r
+ playing += STARTADLIBSOUNDS;\r
+ break;\r
+ }\r
+ MM_SetLock(&(memptr)audiosegs[playing],true);\r
+ }\r
+\r
+\r
+ SD_StopSound();\r
+// oldborder = bordercolor;\r
+// VW_ColorBorder (15);\r
+\r
+ if (beforesort)\r
+ beforesort();\r
+\r
+ scan = mmhead;\r
+\r
+ last = NULL; // shut up compiler warning\r
+\r
+ while (scan)\r
+ {\r
+ if (scan->attributes & LOCKBIT)\r
+ {\r
+ //\r
+ // block is locked, so try to pile later blocks right after it\r
+ //\r
+ start = scan->start + scan->length;\r
+ }\r
+ else\r
+ {\r
+ if (scan->attributes & PURGEBITS)\r
+ {\r
+ //\r
+ // throw out the purgable block\r
+ //\r
+ next = scan->next;\r
+ FREEBLOCK(scan);\r
+ last->next = next;\r
+ scan = next;\r
+ continue;\r
+ }\r
+ else\r
+ {\r
+ //\r
+ // push the non purgable block on top of the last moved block\r
+ //\r
+ if (scan->start != start)\r
+ {\r
+ length = scan->length;\r
+ source = scan->start;\r
+ dest = start;\r
+ while (length > 0xf00)\r
+ {\r
+ movedata(source,0,dest,0,0xf00*16);\r
+ length -= 0xf00;\r
+ source += 0xf00;\r
+ dest += 0xf00;\r
+ }\r
+ movedata(source,0,dest,0,length*16);\r
+\r
+ scan->start = start;\r
+ *(unsigned *)scan->useptr = start;\r
+ }\r
+ start = scan->start + scan->length;\r
+ }\r
+ }\r
+\r
+ last = scan;\r
+ scan = scan->next; // go to next block\r
+ }\r
+\r
+ mmrover = mmhead;\r
+\r
+ if (aftersort)\r
+ aftersort();\r
+\r
+// VW_ColorBorder (oldborder);\r
+\r
+ if (playing)\r
+ MM_SetLock(&(memptr)audiosegs[playing],false);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+=====================\r
+=\r
+= MM_ShowMemory\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_ShowMemory (void)\r
+{\r
+ mmblocktype far *scan;\r
+ unsigned color,temp;\r
+ long end,owner;\r
+ char scratch[80],str[10];\r
+\r
+ VW_SetDefaultColors();\r
+ VW_SetLineWidth(40);\r
+ temp = bufferofs;\r
+ bufferofs = 0;\r
+ VW_SetScreen (0,0);\r
+\r
+ scan = mmhead;\r
+\r
+ end = -1;\r
+\r
+//CA_OpenDebug ();\r
+\r
+ while (scan)\r
+ {\r
+ if (scan->attributes & PURGEBITS)\r
+ color = 5; // dark purple = purgable\r
+ else\r
+ color = 9; // medium blue = non purgable\r
+ if (scan->attributes & LOCKBIT)\r
+ color = 12; // red = locked\r
+ if (scan->start<=end)\r
+ Quit ("MM_ShowMemory: Memory block order currupted!");\r
+ end = scan->start+scan->length-1;\r
+ VW_Hlin(scan->start,(unsigned)end,0,color);\r
+ VW_Plot(scan->start,0,15);\r
+ if (scan->next->start > end+1)\r
+ VW_Hlin(end+1,scan->next->start,0,0); // black = free\r
+\r
+#if 0\r
+strcpy (scratch,"Size:");\r
+ltoa ((long)scan->length*16,str,10);\r
+strcat (scratch,str);\r
+strcat (scratch,"\tOwner:0x");\r
+owner = (unsigned)scan->useptr;\r
+ultoa (owner,str,16);\r
+strcat (scratch,str);\r
+strcat (scratch,"\n");\r
+write (debughandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+ scan = scan->next;\r
+ }\r
+\r
+//CA_CloseDebug ();\r
+\r
+ IN_Ack();\r
+ VW_SetLineWidth(64);\r
+ bufferofs = temp;\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_UnusedMemory\r
+=\r
+= Returns the total free space without purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_UnusedMemory (void)\r
+{\r
+ unsigned free;\r
+ mmblocktype far *scan;\r
+\r
+ free = 0;\r
+ scan = mmhead;\r
+\r
+ while (scan->next)\r
+ {\r
+ free += scan->next->start - (scan->start + scan->length);\r
+ scan = scan->next;\r
+ }\r
+\r
+ return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_TotalFree\r
+=\r
+= Returns the total free space with purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_TotalFree (void)\r
+{\r
+ unsigned free;\r
+ mmblocktype far *scan;\r
+\r
+ free = 0;\r
+ scan = mmhead;\r
+\r
+ while (scan->next)\r
+ {\r
+ if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))\r
+ free += scan->length;\r
+ free += scan->next->start - (scan->start + scan->length);\r
+ scan = scan->next;\r
+ }\r
+\r
+ return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_BombOnError\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_BombOnError (boolean bomb)\r
+{\r
+ bombonerror = bomb;\r
+}\r
+\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_MM.H
+
+#ifndef __ID_EXMM__\r
+#define __ID_EXMM__
+
+#include "lib_head.h"
+//#pragma hdrstop\r
+\r
+//#pragma warn -pro\r
+//#pragma warn -use\r
+\r
+#if 1 // 1 == Debug/Dev ; 0 == Production/final\r
+#define OUT_OF_MEM_MSG "MM_GetPtr: Out of memory!\nYou were short :%ld bytes"\r
+#else\r
+#define OUT_OF_MEM_MSG "\npee\n"
+#endif\r
+\r
+\r
+#define SAVENEARHEAP 0x400 // space to leave in data segment\r
+#define SAVEFARHEAP 0 // space to leave in far heap\r
+\r
+#define BUFFERSIZE 0x1000 // miscelanious, allways available buffer\r
+\r
+#define MAXBLOCKS 600\r
+\r
+\r
+//--------\r
+\r
+#define EMS_INT 0x67\r
+\r
+#define EMS_STATUS 0x40\r
+#define EMS_GETFRAME 0x41\r
+#define EMS_GETPAGES 0x42\r
+#define EMS_ALLOCPAGES 0x43\r
+#define EMS_MAPPAGE 0x44\r
+#define EMS_FREEPAGES 0x45\r
+#define EMS_VERSION 0x46\r
+\r
+//--------\r
+\r
+#define XMS_VERSION 0x00\r
+\r
+#define XMS_ALLOCHMA 0x01\r
+#define XMS_FREEHMA 0x02\r
+\r
+#define XMS_GENABLEA20 0x03\r
+#define XMS_GDISABLEA20 0x04\r
+#define XMS_LENABLEA20 0x05\r
+#define XMS_LDISABLEA20 0x06\r
+#define XMS_QUERYA20 0x07\r
+\r
+#define XMS_QUERYREE 0x08\r
+#define XMS_ALLOC 0x09\r
+#define XMS_FREE 0x0A\r
+#define XMS_MOVE 0x0B\r
+#define XMS_LOCK 0x0C\r
+#define XMS_UNLOCK 0x0D\r
+#define XMS_GETINFO 0x0E\r
+#define XMS_RESIZE 0x0F\r
+\r
+#define XMS_ALLOCUMB 0x10\r
+#define XMS_FREEUMB 0x11\r
+\r
+//==========================================================================\r
+\r
+typedef void /*_seg*/ * memptr;\r
+\r
+typedef struct\r
+{\r
+ long nearheap,farheap,EMSmem,XMSmem,mainmem;\r
+} mminfotype;\r
+\r
+//==========================================================================\r
+\r
+extern mminfotype mminfo;\r
+extern memptr bufferseg;\r
+extern boolean mmerror;\r
+\r
+extern void (* beforesort) (void);\r
+extern void (* aftersort) (void);\r
+\r
+//==========================================================================\r
+\r
+void MM_Startup (void);\r
+void MM_Shutdown (void);\r
+void MM_MapEMS (void);\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size);\r
+void MM_FreePtr (memptr *baseptr);\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge);\r
+void MM_SetLock (memptr *baseptr, boolean locked);\r
+void MM_SortMem (void);\r
+\r
+void MM_ShowMemory (void);\r
+\r
+long MM_UnusedMemory (void);\r
+long MM_TotalFree (void);\r
+\r
+void MM_BombOnError (boolean bomb);
+
+//==========================================================================\r
+\r
+//\r
+// local prototypes\r
+//\r
+\r
+boolean MML_CheckForEMS (void);\r
+void MML_ShutdownEMS (void);\r
+void MM_MapEMS (void);\r
+boolean MML_CheckForXMS (void);\r
+void MML_ShutdownXMS (void);\r
+void MML_UseSpace (unsigned segstart, unsigned seglength);\r
+void MML_ClearBlock (void);\r
+\r
+//==========================================================================
+
+#endif
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// MUSE Header for .ARM\r
+// Created Mon Jun 08 16:14:17 1992\r
+//\r
+/////////////////////////////////////////////////\r
+\r
+#define NUMSOUNDS 35\r
+#define NUMSNDCHUNKS 106\r
+\r
+//\r
+// Sound names & indexes\r
+//\r
+typedef enum {\r
+ HITWALLSND, // 0\r
+ WARPUPSND, // 1\r
+ WARPDOWNSND, // 2\r
+ GETBOLTSND, // 3\r
+ GETNUKESND, // 4\r
+ GETPOTIONSND, // 5\r
+ GETKEYSND, // 6\r
+ GETSCROLLSND, // 7\r
+ GETPOINTSSND, // 8\r
+ USEBOLTSND, // 9\r
+ USENUKESND, // 10\r
+ USEPOTIONSND, // 11\r
+ USEKEYSND, // 12\r
+ NOITEMSND, // 13\r
+ WALK1SND, // 14\r
+ WALK2SND, // 15\r
+ TAKEDAMAGESND, // 16\r
+ MONSTERMISSSND, // 17\r
+ GAMEOVERSND, // 18\r
+ SHOOTSND, // 19\r
+ BIGSHOOTSND, // 20\r
+ SHOOTWALLSND, // 21\r
+ SHOOTMONSTERSND, // 22\r
+ TAKEDMGHURTSND, // 23\r
+ BALLBOUNCESND, // 24\r
+ NOWAYSND, // 25\r
+ WARPSND, // 26\r
+ HIT_GATESND, // 27\r
+ GETGEMSND, // 28\r
+ BOOMSND, // 29\r
+ GRELM_DEADSND, // 30\r
+ FREEZETIMESND, // 31\r
+ TIMERETURNSND, // 32\r
+ TICKSND, // 33\r
+ BODY_EXPLODESND, // 34\r
+ LASTSOUND\r
+ } soundnames;\r
+\r
+//\r
+// Base offsets\r
+//\r
+#define STARTPCSOUNDS 0\r
+#define STARTADLIBSOUNDS 35\r
+#define STARTDIGISOUNDS 70\r
+#define STARTMUSIC 105\r
+\r
+//\r
+// Music names & indexes\r
+//\r
+typedef enum {\r
+ TOOHOT_MUS, // 0\r
+ LASTMUSIC\r
+ } musicnames;\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// Thanks for playing with MUSE!\r
+//\r
+/////////////////////////////////////////////////\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if 0\r
+#define MSHOTDAMAGE 2\r
+#define MSHOTSPEED 10000\r
+\r
+#define ESHOTDAMAGE 1\r
+#define ESHOTSPEED 5000\r
+\r
+#define SSHOTDAMAGE 3\r
+#define SSHOTSPEED 6500\r
+\r
+#define RANDOM_ATTACK 20\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);\r
+void T_ShootPlayer(objtype *ob);\r
+\r
+short zombie_base_delay;\r
+\r
+short other_x[] = {0,39,39,0},\r
+ other_y[] = {0,0,27,27};\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+dirtype dirtable[9] = {northwest,north,northeast,west,nodir,east,\r
+ southwest,south,southeast};\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ BONUS ITEMS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_boltbonus2;\r
+extern statetype s_nukebonus2;\r
+extern statetype s_boltbonus3;\r
+extern statetype s_nukebonus3;\r
+\r
+statetype s_boltbonus = {BOLTOBJPIC,8,NULL,&s_boltbonus2};\r
+statetype s_boltbonus2 = {BOLT2OBJPIC,8,NULL,&s_boltbonus3};\r
+statetype s_boltbonus3 = {BOLT3OBJPIC,8,NULL,&s_boltbonus};\r
+\r
+statetype s_nukebonus = {NUKEOBJPIC,8,NULL,&s_nukebonus2};\r
+statetype s_nukebonus2 = {NUKE2OBJPIC,8,NULL,&s_nukebonus3};\r
+statetype s_nukebonus3 = {NUKE3OBJPIC,8,NULL,&s_nukebonus};\r
+\r
+statetype s_potionbonus = {POTIONOBJPIC,0,NULL,&s_potionbonus};\r
+//statetype s_rkey2bonus = {RKEY2PIC,0,NULL,&s_rkey2bonus};\r
+statetype s_rkeybonus = {RKEYOBJPIC,0,NULL,&s_rkeybonus};\r
+statetype s_ykeybonus = {YKEYOBJPIC,0,NULL,&s_ykeybonus};\r
+statetype s_gkeybonus = {GKEYOBJPIC,0,NULL,&s_gkeybonus};\r
+statetype s_bkeybonus = {BKEYOBJPIC,0,NULL,&s_bkeybonus};\r
+//////////////////////////statetype s_scrollbonus = {SCROLLOBJPIC,0,NULL,&s_scrollbonus};\r
+statetype s_chestbonus = {CHESTOBJPIC,0,NULL,&s_chestbonus};\r
+//statetype s_goalbonus = {NEMESISPIC,0,NULL,&s_goalbonus};\r
+\r
+extern statetype s_waterchestbonus2;\r
+statetype s_waterchestbonus1 = {O_WATER_CHEST1PIC,8,NULL,&s_waterchestbonus2};\r
+statetype s_waterchestbonus2 = {O_WATER_CHEST2PIC,8,NULL,&s_waterchestbonus1};\r
+\r
+extern statetype s_rgem2bonus;\r
+extern statetype s_ygem2bonus;\r
+extern statetype s_ggem2bonus;\r
+extern statetype s_bgem2bonus;\r
+extern statetype s_pgem2bonus;\r
+\r
+statetype s_rgem1bonus = {RGEM1PIC,30,NULL,&s_rgem2bonus};\r
+statetype s_ygem1bonus = {YGEM1PIC,30,NULL,&s_ygem2bonus};\r
+statetype s_ggem1bonus = {GGEM1PIC,30,NULL,&s_ggem2bonus};\r
+statetype s_bgem1bonus = {BGEM1PIC,30,NULL,&s_bgem2bonus};\r
+statetype s_pgem1bonus = {PGEM1PIC,30,NULL,&s_pgem2bonus};\r
+\r
+statetype s_rgem2bonus = {RGEM2PIC,30,NULL,&s_rgem1bonus};\r
+statetype s_ygem2bonus = {YGEM2PIC,30,NULL,&s_ygem1bonus};\r
+statetype s_ggem2bonus = {GGEM2PIC,30,NULL,&s_ggem1bonus};\r
+statetype s_bgem2bonus = {BGEM2PIC,30,NULL,&s_bgem1bonus};\r
+statetype s_pgem2bonus = {PGEM2PIC,30,NULL,&s_pgem1bonus};\r
+\r
+statetype s_bonus_die = {0,8,NULL,NULL};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnBonus\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnBonus (int tilex, int tiley, int number)\r
+{\r
+ extern unsigned gcolor;\r
+\r
+ statetype *state;\r
+\r
+ switch (number)\r
+ {\r
+ case B_BOLT: state = &s_boltbonus; break;\r
+ case B_NUKE: state = &s_nukebonus; break;\r
+ case B_POTION: state = &s_potionbonus; break;\r
+\r
+ case B_RKEY: state = &s_rkeybonus; break;\r
+ case B_YKEY: state = &s_ykeybonus; break;\r
+ case B_GKEY: state = &s_gkeybonus; break;\r
+ case B_BKEY: state = &s_bkeybonus; break;\r
+\r
+ case B_RGEM: state = &s_rgem1bonus; break;\r
+ case B_YGEM: state = &s_ygem1bonus; break;\r
+ case B_GGEM: state = &s_ggem1bonus; break;\r
+ case B_BGEM: state = &s_bgem1bonus; break;\r
+ case B_PGEM: state = &s_pgem1bonus; break;\r
+\r
+ case B_CHEST:\r
+ if (gcolor == 0x0101)\r
+ state = &s_waterchestbonus1;\r
+ else\r
+ state = &s_chestbonus;\r
+ break;\r
+\r
+#if 0\r
+ case B_SCROLL1:\r
+ case B_SCROLL2:\r
+ case B_SCROLL3:\r
+ case B_SCROLL4:\r
+ case B_SCROLL5:\r
+ case B_SCROLL6:\r
+ case B_SCROLL7:\r
+ case B_SCROLL8: state = &s_scrollbonus; break;\r
+#endif\r
+\r
+// case B_RKEY2: state = &s_rkey2bonus; break;\r
+\r
+ default:\r
+ Quit("SpawnBonus(): INVALID BONUS");\r
+ break;\r
+ }\r
+\r
+ SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2);\r
+// new->tileobject = true;\r
+ new->temp1 = number;\r
+ new->obclass = bonusobj;\r
+\r
+ switch (number)\r
+ {\r
+ case B_POTION:\r
+ case B_CHEST:\r
+ case B_BOLT:\r
+ case B_NUKE:\r
+ new->flags |= of_shootable;\r
+ break;\r
+\r
+ default:\r
+ new->flags &= ~of_shootable;\r
+ break;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnTombstone\r
+=\r
+===============\r
+*/\r
+\r
+statetype s_tombs[3] = {{TOMB1PIC,8,NULL,&s_tombs[0]},\r
+ {TOMB2PIC,8,NULL,&s_tombs[1]},\r
+ {TOMB3PIC,8,NULL,&s_tombs[2]}};\r
+\r
+void SpawnTombstone (int tilex, int tiley, int shape)\r
+{\r
+ statetype *state=&s_tombs[shape];\r
+\r
+ SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2);\r
+// new->tileobject = true;\r
+ new->obclass = realsolidobj;\r
+ new->flags |= of_shootable;\r
+}\r
+\r
+\r
+/*\r
+============================================================================\r
+\r
+ FREEZE TIME OBJECT\r
+\r
+============================================================================\r
+*/\r
+\r
+extern statetype s_ftimebonus;\r
+extern statetype s_ftimebonus2;\r
+\r
+statetype s_ftimebonus = {TIMEOBJ1PIC,6,NULL,&s_ftimebonus2};\r
+statetype s_ftimebonus2 = {TIMEOBJ2PIC,6,NULL,&s_ftimebonus};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnFTime\r
+=\r
+===============\r
+*/\r
+void SpawnFTime(int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_ftimebonus,TILEGLOBAL/2);\r
+// new->tileobject = true;\r
+ new->obclass = freezeobj;\r
+ new->flags |= of_shootable;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ EXPLODING WALL\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+void T_WallDie (objtype *ob);\r
+\r
+extern statetype s_walldie1;\r
+extern statetype s_walldie2;\r
+extern statetype s_walldie3;\r
+extern statetype s_walldie4;\r
+extern statetype s_walldie5;\r
+extern statetype s_walldie6;\r
+\r
+statetype s_walldie1 = {0,20,NULL,&s_walldie2};\r
+statetype s_walldie2 = {0,-1,T_WallDie,&s_walldie3};\r
+statetype s_walldie3 = {0,20,NULL,&s_walldie4};\r
+statetype s_walldie4 = {0,-1,T_WallDie,&s_walldie5};\r
+statetype s_walldie5 = {0,20,NULL,&s_walldie6};\r
+statetype s_walldie6 = {0,-1,T_WallDie,NULL};\r
+\r
+\r
+/*\r
+================\r
+=\r
+= ExplodeWall\r
+=\r
+================\r
+*/\r
+\r
+void ExplodeWall (int tilex, int tiley)\r
+{\r
+ extern unsigned gcolor;\r
+ unsigned tilenum;\r
+\r
+ DSpawnNewObj (tilex,tiley,&s_walldie1,0);\r
+ if (new == &dummyobj)\r
+ return;\r
+ new->obclass = inertobj;\r
+ new->active = always;\r
+ if (gcolor == 0x0101)\r
+ tilenum = WATEREXP;\r
+ else\r
+ tilenum = WALLEXP;\r
+ (unsigned)actorat[new->tilex][new->tiley] = tilemap[new->tilex][new->tiley] =\r
+ *(mapsegs[0]+farmapylookup[new->tiley]+new->tilex) = tilenum;\r
+ *(mapsegs[2]+farmapylookup[new->tiley]+new->tilex) &= 0xFF;\r
+}\r
+\r
+\r
+/*\r
+================\r
+=\r
+= T_WallDie\r
+=\r
+================\r
+*/\r
+\r
+void T_WallDie (objtype *ob)\r
+{\r
+ extern unsigned gcolor;\r
+ unsigned tile,other,spot,x,y;\r
+\r
+ if (++ob->temp1 == 3)\r
+ tile = 0;\r
+ else\r
+ if (gcolor == 0x0101)\r
+ tile = WATEREXP-1 + ob->temp1;\r
+ else\r
+ tile = WALLEXP-1 + ob->temp1;\r
+ x = ob->tilex;\r
+ y = ob->tiley;\r
+\r
+ (unsigned)actorat[x][y] = tilemap[x][y] = *(mapsegs[0]+farmapylookup[y]+x) = tile;\r
+\r
+ if (ob->temp1 == 1)\r
+ {\r
+ //\r
+ // blow up nearby walls\r
+ //\r
+ spot = (*(mapsegs[2]+farmapylookup[y]+(x-1))) >> 8;\r
+ if (spot == EXP_WALL_CODE)\r
+ ExplodeWall (x-1,y);\r
+ spot = (*(mapsegs[2]+farmapylookup[y]+(x+1))) >> 8;\r
+ if (spot == EXP_WALL_CODE)\r
+ ExplodeWall (x+1,y);\r
+ spot = (*(mapsegs[2]+farmapylookup[y-1]+x)) >> 8;\r
+ if (spot == EXP_WALL_CODE)\r
+ ExplodeWall (x,y-1);\r
+ spot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8;\r
+ if (spot == EXP_WALL_CODE)\r
+ ExplodeWall (x,y+1);\r
+ }\r
+}\r
+/*\r
+=============================================================================\r
+\r
+ OBJ_WARP GATE\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Gate (objtype *ob);\r
+\r
+extern statetype s_obj_gate1;\r
+extern statetype s_obj_gate2;\r
+extern statetype s_obj_gate3;\r
+extern statetype s_obj_gate4;\r
+\r
+statetype s_obj_gate1 = {OBJ_WARP1PIC,10,T_Gate,&s_obj_gate2};\r
+statetype s_obj_gate2 = {OBJ_WARP2PIC,10,T_Gate,&s_obj_gate3};\r
+statetype s_obj_gate3 = {OBJ_WARP3PIC,10,T_Gate,&s_obj_gate4};\r
+statetype s_obj_gate4 = {OBJ_WARP4PIC,10,T_Gate,&s_obj_gate1};\r
+\r
+extern statetype s_anthill;\r
+statetype s_anthill = {ANT_HILLPIC, 20, T_Gate, &s_anthill};\r
+\r
+//---------------------------------------------------------------------------\r
+// SpawnWarp()\r
+//\r
+// TYPE : Type param is the gate number (1-what ever) will link you to\r
+// gate of that type.\r
+//---------------------------------------------------------------------------\r
+void SpawnWarp (int tilex, int tiley, int type)\r
+{\r
+\r
+ if (type)\r
+ SpawnNewObj (tilex,tiley,&s_obj_gate1,TILEGLOBAL/3);\r
+ else\r
+ SpawnNewObj (tilex,tiley,&s_anthill,TILEGLOBAL/3);\r
+ new->obclass = gateobj;\r
+ new->temp1 = type;\r
+}\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Gate\r
+=\r
+===============\r
+*/\r
+\r
+#define STATUSCOLOR 4\r
+\r
+void T_Gate (objtype *ob)\r
+{\r
+ objtype *check;\r
+ unsigned temp,spot;\r
+\r
+ if (CheckHandAttack (ob) && !playstate)\r
+ {\r
+ // make\r
+ //\r
+// spot = (*(mapsegs[2]+farmapylookup[ob->tiley+1]+ob->tilex)) >> 8;\r
+// if (spot--)\r
+// if (gamestate.keys[spot])\r
+// TakeKey(spot);\r
+// else\r
+// return;\r
+\r
+ //\r
+ // warp\r
+ //\r
+\r
+// temp = bufferofs;\r
+// bufferofs = 0;\r
+// VW_Bar (26,4,232,9,STATUSCOLOR); // clear text description\r
+// bufferofs = temp;\r
+\r
+// IN_ClearKeysDown ();\r
+ if (ob->temp1)\r
+ {\r
+ //\r
+ // teleport inside level\r
+ //\r
+\r
+ for (check=player->next;check;check=check->next)\r
+ if (check->obclass==gateobj && check->temp1==ob->temp1 &&\r
+ check != ob)\r
+ {\r
+ player->x = check->x;\r
+ player->y = check->y;\r
+ Thrust (player->angle,TILEGLOBAL/2); // move forwards\r
+ Thrust (player->angle,TILEGLOBAL/2); // move forwards\r
+ Thrust (player->angle,TILEGLOBAL/2); // move forwards\r
+ fizzlein=true;\r
+ SD_PlaySound(WARPSND);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ //\r
+ // teleport out of level\r
+ //\r
+\r
+ playstate = ex_warped;\r
+ spot = (*(mapsegs[2]+farmapylookup[ob->tiley+1]+ob->tilex)) >> 8;\r
+ gamestate.mapon=spot;\r
+ SD_PlaySound(WARPUPSND);\r
+ }\r
+ }\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ FAT DEMON\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define FATCLOUDDAMAGE 2\r
+\r
+void T_FatDemon (objtype *ob);\r
+void T_CheckCnt(objtype *ob);\r
+void ExplodeSound(objtype *ob);\r
+\r
+extern statetype s_fatdemon_pause;\r
+extern statetype s_fatdemon_walk1;\r
+extern statetype s_fatdemon_walk2;\r
+extern statetype s_fatdemon_walk3;\r
+extern statetype s_fatdemon_walk4;\r
+extern statetype s_fatdemon_attack1;\r
+extern statetype s_fatdemon_attack2;\r
+extern statetype s_fatdemon_blowup2;\r
+extern statetype s_fatdemon_blowup3;\r
+extern statetype s_fatdemon_blowup4;\r
+extern statetype s_fatdemon_blowup5;\r
+extern statetype s_fatdemon_blowup6;\r
+extern statetype s_fatdemon_blowup7;\r
+extern statetype s_fatdemon_explode;\r
+extern statetype s_fatdemon_feet;\r
+\r
+statetype s_fatdemon_pause = {FATDEMON_WALK1PIC,40,NULL,&s_fatdemon_walk2};\r
+\r
+statetype s_fatdemon_walk1 = {FATDEMON_WALK1PIC,13,T_FatDemon,&s_fatdemon_walk2};\r
+statetype s_fatdemon_walk2 = {FATDEMON_WALK2PIC,13,T_FatDemon,&s_fatdemon_walk3};\r
+statetype s_fatdemon_walk3 = {FATDEMON_WALK3PIC,13,T_FatDemon,&s_fatdemon_walk4};\r
+statetype s_fatdemon_walk4 = {FATDEMON_WALK4PIC,13,T_FatDemon,&s_fatdemon_walk1};\r
+\r
+statetype s_fatdemon_attack1 = {FATDEMON_ATTACK1PIC,20,NULL,&s_fatdemon_attack2};\r
+statetype s_fatdemon_attack2 = {FATDEMON_ATTACK2PIC,20,T_DoDamage,&s_fatdemon_pause};\r
+\r
+statetype s_fatdemon_ouch = {FATDEMON_OUCHPIC,14,NULL,&s_fatdemon_walk1};\r
+\r
+statetype s_fatdemon_blowup1 = {FATDEMON_BLOWUP1PIC,25,NULL,&s_fatdemon_blowup2};\r
+statetype s_fatdemon_blowup2 = {FATDEMON_BLOWUP2PIC,25,NULL,&s_fatdemon_blowup3};\r
+statetype s_fatdemon_blowup3 = {FATDEMON_BLOWUP1PIC,15,NULL,&s_fatdemon_blowup4};\r
+statetype s_fatdemon_blowup4 = {FATDEMON_BLOWUP2PIC,15,NULL,&s_fatdemon_blowup5};\r
+statetype s_fatdemon_blowup5 = {FATDEMON_BLOWUP1PIC,6,NULL,&s_fatdemon_blowup6};\r
+statetype s_fatdemon_blowup6 = {FATDEMON_BLOWUP2PIC,6,T_CheckCnt,&s_fatdemon_blowup5};\r
+statetype s_fatdemon_blowup7 = {FATDEMON_BLOWUP3PIC,30,NULL,&s_fatdemon_explode};\r
+\r
+\r
+statetype s_fatdemon_explode = {FATDEMON_EXPLODEPIC,40,ExplodeSound,&s_fatdemon_feet};\r
+statetype s_fatdemon_feet = {FATDEMON_FEETPIC,30,NULL,&s_fatdemon_feet};\r
+\r
+#define cnt ob->temp1\r
+#define cloud_delay ob->temp2\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnFatDemon\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnFatDemon (int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_fatdemon_walk1,35*PIXRADIUS);\r
+ new->speed = 2500;\r
+ new->obclass = fatdemonobj;\r
+ new->flags |= of_shootable;\r
+ new->hitpoints = EasyHitPoints(10);\r
+ new->temp1 = 25; //used to "shake" the fat dude??????\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_FatDemon\r
+=\r
+===============\r
+*/\r
+\r
+void T_FatDemon (objtype *ob)\r
+{\r
+ if (Chase(ob,true) || (random(1000)<RANDOM_ATTACK))\r
+ {\r
+ ob->state = &s_fatdemon_attack1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_DecCnt\r
+=\r
+===============\r
+*/\r
+\r
+void T_CheckCnt (objtype *ob)\r
+{\r
+ ob->temp1--;\r
+ if (!ob->temp1)\r
+ {\r
+ ob->state = &s_fatdemon_blowup7;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= ExplodeSound\r
+=\r
+===============\r
+*/\r
+void ExplodeSound(objtype *ob)\r
+{\r
+ if (ob->temp1 != 666) // Has this think been called already?\r
+ {\r
+ SD_PlaySound(BODY_EXPLODESND);\r
+ ob->temp1 = 666; // Has now!\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ WATER DRAGON\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_dragon_shot1;\r
+extern statetype s_dragon_shot2;\r
+\r
+\r
+void T_Dragon(objtype *ob);\r
+void T_DragonShoot(objtype *ob);\r
+\r
+\r
+statetype s_wet_bubbles1 = {DRAGON_BUBBLES1PIC,13,T_Dragon,&s_wet_bubbles2};\r
+statetype s_wet_bubbles2 = {DRAGON_BUBBLES2PIC,15,T_Dragon,&s_wet_bubbles1};\r
+statetype s_wet_bubbles3 = {0,35,T_Dragon,&s_wet_bubbles1};\r
+\r
+statetype s_wet_peek = {DRAGON_EYESPIC,45,NULL,&s_wet_bubbles1};\r
+\r
+statetype s_wet_rise1 = {DRAGON_BUBBLES2PIC,15,NULL,&s_wet_rise3};\r
+statetype s_wet_rise3 = {DRAGON_EYESPIC,20,NULL,&s_wet_rise4};\r
+statetype s_wet_rise4 = {DRAGON_RISE1PIC,20,NULL,&s_wet_rise5};\r
+statetype s_wet_rise5 = {DRAGON_RISE2PIC,20,NULL,&s_wet_walk1};\r
+\r
+statetype s_wet_sink1 = {DRAGON_RISE2PIC,20,NULL,&s_wet_sink2};\r
+statetype s_wet_sink2 = {DRAGON_RISE1PIC,20,NULL,&s_wet_sink3};\r
+statetype s_wet_sink3 = {DRAGON_EYESPIC,20,NULL,&s_wet_bubbles1};\r
+\r
+statetype s_wet_walk1 = {DRAGON_WALK1PIC,12,T_Dragon,&s_wet_walk2};\r
+statetype s_wet_walk2 = {DRAGON_WALK2PIC,12,T_Dragon,&s_wet_walk3};\r
+statetype s_wet_walk3 = {DRAGON_WALK3PIC,12,T_Dragon,&s_wet_walk4};\r
+statetype s_wet_walk4 = {DRAGON_WALK4PIC,12,T_Dragon,&s_wet_walk1};\r
+\r
+statetype s_wet_attack1 = {DRAGON_ATTACK1PIC,10,NULL,&s_wet_attack2};\r
+statetype s_wet_attack2 = {DRAGON_ATTACK2PIC,10,NULL,&s_wet_attack3};\r
+statetype s_wet_attack3 = {DRAGON_ATTACK2PIC,10,NULL,&s_wet_attack4};\r
+statetype s_wet_attack4 = {DRAGON_ATTACK3PIC,10,T_DragonShoot,&s_wet_walk1};\r
+\r
+statetype s_wet_ouch = {DRAGON_OUCHPIC,10,T_Dragon,&s_wet_walk1};\r
+\r
+statetype s_wet_die1 = {DRAGON_DEATH1PIC,27,NULL,&s_wet_die2};\r
+statetype s_wet_die2 = {DRAGON_DEATH2PIC,29,NULL,&s_wet_die3};\r
+statetype s_wet_die3 = {DRAGON_DEATH3PIC,44,NULL,&s_wet_die4};\r
+statetype s_wet_die4 = {DRAGON_BUBBLES2PIC,26,NULL,&s_wet_die5};\r
+statetype s_wet_die5 = {DRAGON_BUBBLES1PIC,23,NULL,NULL};\r
+\r
+statetype s_dragon_shot1 = {PSHOT1PIC,8,&T_ShootPlayer,&s_dragon_shot2};\r
+statetype s_dragon_shot2 = {PSHOT2PIC,8,&T_ShootPlayer,&s_dragon_shot1};\r
+\r
+\r
+typedef enum {wt_BUBBLES,wt_WALK,wt_CORNER1,wt_CORNER2,wt_CORNER3,wt_CORNER4} DragonTypes;\r
+\r
+\r
+#define WD_TIMEREMAIN (ob->temp1)\r
+#define WD_STAGE (ob->temp2)\r
+#define WATER_DRAGON_LEAVE 0x04\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnDragon\r
+=\r
+===============\r
+*/\r
+void SpawnDragon(int tilex, int tiley)\r
+{\r
+ objtype *ob;\r
+ SpawnNewObj(tilex,tiley,&s_wet_bubbles1,PIXRADIUS*35);\r
+ ob=new;\r
+\r
+ WD_STAGE = wt_BUBBLES;\r
+ WD_TIMEREMAIN = 80;\r
+\r
+ new->obclass = wetobj;\r
+ new->speed = 1000;\r
+ new->flags &= ~of_shootable;\r
+ new->hitpoints = EasyHitPoints(20);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Dragon\r
+=\r
+===============\r
+*/\r
+\r
+void T_Dragon(objtype *ob)\r
+{\r
+ switch (WD_STAGE)\r
+ {\r
+ case wt_BUBBLES:\r
+ ob->flags &= ~of_shootable;\r
+ if (Chase(ob,true))\r
+ {\r
+ // RISE & GOTO WALK STAGE\r
+ //\r
+\r
+ WD_STAGE = wt_WALK;\r
+ WD_TIMEREMAIN = 60*8+random(60*5);\r
+ ob->state = &s_wet_rise1;\r
+ ob->speed = 2200;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+ else\r
+ {\r
+ // DEC COUNTER - And check for WALK\r
+ //\r
+ if ((WD_TIMEREMAIN-=realtics) < 0)\r
+ {\r
+ // RISE & GOTO WALK STAGE\r
+ //\r
+\r
+ WD_STAGE = wt_WALK;\r
+ WD_TIMEREMAIN = 60*8+random(60*5);\r
+ ob->state = &s_wet_rise1;\r
+ ob->speed = 2200;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+ else\r
+ if (random(1000)<5)\r
+ {\r
+ // RANDOM PEEK UP OUT OF WATER\r
+ //\r
+\r
+ ob->state=&s_wet_peek;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+ }\r
+ break;\r
+\r
+\r
+ case wt_WALK:\r
+ ob->flags |= of_shootable;\r
+\r
+ if (Chase(ob,true) || (CheckHandAttack(ob)))\r
+\r
+ {\r
+ ob->flags |= WATER_DRAGON_LEAVE;\r
+ WD_STAGE = random(wt_CORNER3) + 2;\r
+ WD_TIMEREMAIN = 60*2+(random(6)*60);\r
+ ob->state = &s_wet_bubbles1;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+ else\r
+ if (AngleNearPlayer(ob) != -1)\r
+ {\r
+ ob->state = &s_wet_attack1;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+\r
+ else\r
+ {\r
+ // DEC COUNTER - And check for SINK\r
+ //\r
+ if ((WD_TIMEREMAIN-=realtics) < 0)\r
+ {\r
+ // SINK & GOTO BUBBLE STAGE\r
+ //\r
+\r
+ WD_STAGE = wt_BUBBLES;\r
+ WD_TIMEREMAIN = 60*2+random(60*2);\r
+ ob->state = &s_wet_sink1;\r
+ ob->speed = 1200;\r
+ ob->ticcount = ob->state->tictime;\r
+ ob->flags &= ~of_shootable;\r
+ }\r
+\r
+ }\r
+ break;\r
+ case wt_CORNER1:\r
+ case wt_CORNER2:\r
+ case wt_CORNER3:\r
+ case wt_CORNER4:\r
+ ob->flags &= ~of_shootable;\r
+ if ((WD_TIMEREMAIN -= realtics) < 0)\r
+ {\r
+ WD_STAGE = wt_BUBBLES;\r
+ ob->flags &= ~WATER_DRAGON_LEAVE;\r
+ }\r
+ else\r
+ {\r
+ fixed tempx,tempy;\r
+ unsigned temp_tilex,temp_tiley;\r
+\r
+ tempx = player->x;\r
+ tempy = player->y;\r
+ temp_tilex = player->tilex;\r
+ temp_tiley = player->tiley;\r
+\r
+ player->x = ((long)other_x[WD_STAGE-2]<<TILESHIFT)+TILEGLOBAL/2;\r
+ player->y = ((long)other_y[WD_STAGE-2]<<TILESHIFT)+TILEGLOBAL/2;\r
+ player->tilex = other_x[WD_STAGE-2];\r
+ player->tiley = other_y[WD_STAGE-2];\r
+\r
+\r
+ Chase(ob,true);\r
+\r
+ player->x = tempx;\r
+ player->y = tempy;\r
+ player->tilex = temp_tilex;\r
+ player->tiley = temp_tiley;\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_DragonShoot\r
+=\r
+===============\r
+*/\r
+void T_DragonShoot (objtype *ob)\r
+{\r
+ ShootPlayer(ob,dshotobj,10000,&s_dragon_shot1);\r
+}\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+void SpawnSkeleton(int tilex, int tiley);\r
+\r
+#if 0\r
+#define MSHOTDAMAGE 2\r
+#define MSHOTSPEED 10000\r
+\r
+#define ESHOTDAMAGE 1\r
+#define ESHOTSPEED 5000\r
+\r
+#define SSHOTDAMAGE 3\r
+#define SSHOTSPEED 6500\r
+\r
+#define RANDOM_ATTACK 20\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);\r
+void T_ShootPlayer(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ SKELETON IN WALL\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_WallSkeleton(objtype *ob);\r
+\r
+statetype s_wallskel = {0,40,T_WallSkeleton,&s_wallskel};\r
+statetype s_wallskel2 = {0,1,NULL,NULL};\r
+\r
+\r
+enum wskel_modes {ws_wall1,ws_wall2,ws_wall3,ws_exit};\r
+//enum wskel_modes {ws_wall1,ws_exit};\r
+\r
+#define wskel_mode ob->temp1\r
+#define wskel_delay ob->temp2\r
+#define wskel_base ob->angle\r
+#define wskel_wallx ob->hitpoints\r
+#define wskel_wally ob->speed\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnWallSkeleton\r
+=\r
+===============\r
+*/\r
+void SpawnWallSkeleton(int tilex, int tiley)\r
+{\r
+ char xofs[] = {0,0,-1,+1};\r
+ char yofs[] = {-1,+1,0,0};\r
+\r
+ objtype *ob;\r
+ int wallx=tilex,wally=tiley,wallbase,wallmode,loop;\r
+ unsigned tile,current_delay;\r
+\r
+ for (loop=0; loop<4; loop++)\r
+ {\r
+ tile = *(mapsegs[0]+farmapylookup[tiley+yofs[loop]]+tilex+xofs[loop]);\r
+ switch (tile)\r
+ {\r
+// case WALL_SKELETON_CODE:\r
+// case WALL_SKELETON_CODE+1:\r
+// case WALL_SKELETON_CODE+2:\r
+// wallmode = ws_wall1+(tile-WALL_SKELETON_CODE);\r
+// wallbase = WALL_SKELETON_CODE;\r
+// goto foundtile;\r
+// break;\r
+\r
+ case 66:\r
+ case 68:\r
+// case 21:\r
+ wallmode = ws_wall1+(tile-66);\r
+ wallbase = 66;\r
+ goto foundtile;\r
+// break;\r
+\r
+ case 67:\r
+ case 69:\r
+ wallmode = ws_wall1+(tile-67);\r
+ wallbase = 67;\r
+ goto foundtile;\r
+// break;\r
+ }\r
+ }\r
+\r
+ return;\r
+foundtile:;\r
+\r
+ wallx += xofs[loop];\r
+ wally += yofs[loop];\r
+\r
+ SpawnNewObj(tilex,tiley,&s_wallskel,PIXRADIUS*35);\r
+ ob = new;\r
+ new->obclass = wallskelobj;\r
+ new->speed = 1900;\r
+ new->flags &= ~of_shootable;\r
+ new->hitpoints = 12;\r
+\r
+// new->tilex = wallx;\r
+// new->tiley = wally;\r
+ wskel_wallx = wallx;\r
+ wskel_wally = wally;\r
+ wskel_base = wallbase;\r
+ new->active = no;\r
+\r
+ wskel_mode = wallmode;\r
+\r
+ tile = *(mapsegs[2]+farmapylookup[wally]+wallx);\r
+ if (tile)\r
+ wskel_delay = (tile>>8)*30;\r
+ else\r
+ {\r
+ current_delay = (2*60)+random(4*60);\r
+ wskel_delay = zombie_base_delay+current_delay;\r
+ zombie_base_delay += current_delay;\r
+ if (zombie_base_delay > 8*60)\r
+ zombie_base_delay = 0;\r
+ }\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_WallSkeleton\r
+=\r
+===============\r
+*/\r
+void T_WallSkeleton(objtype *ob)\r
+{\r
+ int x=wskel_wallx,y=wskel_wally;\r
+\r
+ wskel_delay -= realtics;\r
+ if (wskel_delay > 0)\r
+ return;\r
+\r
+ switch (wskel_mode)\r
+ {\r
+ case ws_wall2:\r
+ if ((wskel_base == 66) || (wskel_base == 67))\r
+ wskel_mode++;\r
+ case ws_wall1:\r
+ case ws_wall3:\r
+ (unsigned)actorat[x][y]\r
+ = tilemap[x][y]\r
+ = *(mapsegs[0]+farmapylookup[y]+x)\r
+ = wskel_base+(wskel_mode-ws_wall1);\r
+\r
+ wskel_mode++;\r
+ wskel_delay = (120);\r
+ ob->active = always;\r
+ break;\r
+\r
+ case ws_exit:\r
+ (unsigned)actorat[x][y]\r
+ = tilemap[x][y]\r
+ = *(mapsegs[0]+farmapylookup[y]+x)\r
+ = 21;\r
+// = wskel_base;\r
+ ob->tilex = ob->x >> TILESHIFT;\r
+ ob->tiley = ob->y >> TILESHIFT;\r
+\r
+ ob->obclass = skeletonobj;\r
+ ob->speed = 2036;\r
+ ob->flags |= of_shootable;\r
+ ob->hitpoints = 12;\r
+ ob->state = &s_skel_1;\r
+ ob->ticcount = ob->state->tictime;\r
+ break;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ SKELETONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Skeleton(objtype *ob);\r
+\r
+\r
+\r
+\r
+statetype s_skel_pause = {SKELETON_1PIC,40,NULL,&s_skel_2};\r
+\r
+statetype s_skel_1 = {SKELETON_1PIC,10,T_Skeleton,&s_skel_2};\r
+statetype s_skel_2 = {SKELETON_2PIC,10,T_Skeleton,&s_skel_3};\r
+statetype s_skel_3 = {SKELETON_3PIC,10,T_Skeleton,&s_skel_4};\r
+statetype s_skel_4 = {SKELETON_4PIC,10,T_Skeleton,&s_skel_1};\r
+\r
+statetype s_skel_attack1 = {SKELETON_ATTACK_1PIC,12,NULL,&s_skel_attack2};\r
+statetype s_skel_attack2 = {SKELETON_ATTACK_2PIC,12,NULL,&s_skel_attack3};\r
+statetype s_skel_attack3 = {SKELETON_ATTACK_3PIC,12,T_DoDamage,&s_skel_pause};\r
+\r
+statetype s_skel_ouch = {SKELETON_OUCHPIC,8,NULL,&s_skel_1};\r
+\r
+statetype s_skel_die1 = {SKELETON_OUCHPIC,18,NULL,&s_skel_die2};\r
+statetype s_skel_die2 = {SKELETON_DEATH_1PIC,18,NULL,&s_skel_die3};\r
+statetype s_skel_die3 = {SKELETON_DEATH_2PIC,18,NULL,&s_skel_die3};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnSkeleton\r
+=\r
+===============\r
+*/\r
+void SpawnSkeleton(int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_skel_1,PIXRADIUS*35);\r
+ new->obclass = skeletonobj;\r
+ new->speed = 2036;\r
+ new->flags |= of_shootable;\r
+ new->hitpoints = EasyHitPoints(12);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Skeleton\r
+=\r
+===============\r
+*/\r
+\r
+void T_Skeleton(objtype *ob)\r
+{\r
+ if (Chase (ob,true) || (random(1000)<RANDOM_ATTACK))\r
+ {\r
+ ob->state = &s_skel_attack1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ EYE\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_EyeMage (objtype *ob);\r
+boolean T_EyeShoot (objtype *ob, boolean eyeshot);\r
+void T_EyeShootPlayer (objtype *ob);\r
+\r
+extern statetype s_eye_shootplayer_1;\r
+extern statetype s_eye_shootplayer_2;\r
+\r
+statetype s_eye_pause = {EYE_WALK1PIC,40,NULL,&s_eye_2};\r
+\r
+statetype s_eye_1 = {EYE_WALK1PIC,20,T_EyeMage,&s_eye_2};\r
+statetype s_eye_2 = {EYE_WALK2PIC,20,T_EyeMage,&s_eye_3};\r
+statetype s_eye_3 = {EYE_WALK3PIC,20,T_EyeMage,&s_eye_4};\r
+statetype s_eye_4 = {EYE_WALK2PIC,20,T_EyeMage,&s_eye_1};\r
+statetype s_eye_shootplayer_1 = {EYE_SCOWLPIC,1,T_EyeShootPlayer,&s_eye_shootplayer_2};\r
+statetype s_eye_shootplayer_2 = {EYE_SCOWLPIC,20,NULL,&s_eye_1};\r
+\r
+statetype s_eye_ouch = {EYE_OUCH1PIC,8,NULL,&s_eye_ouch2};\r
+statetype s_eye_ouch2 = {EYE_OUCH2PIC,8,NULL,&s_eye_1};\r
+\r
+statetype s_eye_die1 = {EYE_DEATH1PIC,22,NULL,&s_eye_die2};\r
+statetype s_eye_die2 = {EYE_DEATH2PIC,22,NULL,&s_eye_die3};\r
+statetype s_eye_die3 = {EYE_DEATH2PIC,22,NULL,NULL};\r
+\r
+extern statetype s_eshot2;\r
+\r
+statetype s_eshot1 = {EYE_SHOT1PIC,8,&T_ShootPlayer,&s_eshot2};\r
+statetype s_eshot2 = {EYE_SHOT2PIC,8,&T_ShootPlayer,&s_eshot1};\r
+\r
+#define eye_mode ob->temp1\r
+#define eye_delay ob->temp2\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// SpawnEye()\r
+//-------------------------------------------------------------------------\r
+void SpawnEye(int tilex, int tiley)\r
+{\r
+ objtype *ob;\r
+\r
+ SpawnNewObj(tilex,tiley,&s_eye_1,PIXRADIUS*35);\r
+ ob = new;\r
+ new->obclass = eyeobj;\r
+ new->speed = 1200;\r
+ new->flags |= of_shootable;\r
+ new->hitpoints = EasyHitPoints(15);\r
+ eye_mode = em_other1;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// T_EyeShootPlayer\r
+//---------------------------------------------------------------------------\r
+void T_EyeShootPlayer (objtype *ob)\r
+{\r
+ ShootPlayer(ob,eshotobj,ESHOTSPEED,&s_eshot1);\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ SUCCUBUS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Succubus (objtype *ob);\r
+void T_SuccubusShot (objtype *ob);\r
+\r
+extern statetype s_succubus_pause;\r
+extern statetype s_succubus_walk1;\r
+extern statetype s_succubus_walk2;\r
+extern statetype s_succubus_walk3;\r
+extern statetype s_succubus_walk4;\r
+extern statetype s_succubus_shot1;\r
+extern statetype s_succubus_attack1;\r
+extern statetype s_succubus_attack2;\r
+extern statetype s_succubus_attack3;\r
+extern statetype s_succubus_death1;\r
+extern statetype s_succubus_death2;\r
+\r
+statetype s_succubus_pause = {SUCCUBUS_WALK2PIC,10,NULL,&s_succubus_walk3};\r
+\r
+statetype s_succubus_walk1 = {SUCCUBUS_WALK1PIC,10,T_EyeMage,&s_succubus_walk2};\r
+statetype s_succubus_walk2 = {SUCCUBUS_WALK2PIC,10,T_EyeMage,&s_succubus_walk3};\r
+statetype s_succubus_walk3 = {SUCCUBUS_WALK3PIC,10,T_EyeMage,&s_succubus_walk4};\r
+statetype s_succubus_walk4 = {SUCCUBUS_WALK4PIC,10,T_EyeMage,&s_succubus_walk1};\r
+\r
+statetype s_succubus_attack1 = {SUCCUBUS_ATTACK1PIC,15,NULL,&s_succubus_attack2};\r
+statetype s_succubus_attack2 = {SUCCUBUS_ATTACK1PIC,-1,T_SuccubusShot,&s_succubus_attack3};\r
+statetype s_succubus_attack3 = {SUCCUBUS_ATTACK2PIC,15,NULL,&s_succubus_pause};\r
+\r
+statetype s_succubus_ouch = {SUCCUBUS_OUCHPIC,15,NULL,&s_succubus_walk1};\r
+\r
+statetype s_succubus_death1 = {SUCCUBUS_DEATH1PIC,55,NULL,&s_succubus_death2};\r
+statetype s_succubus_death2 = {SUCCUBUS_DEATH2PIC,20,NULL,&s_succubus_death2};\r
+\r
+statetype s_succubus_shot1 = {SUCCUBUS_SHOT1PIC,12,&T_ShootPlayer,&s_succubus_shot1};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnSuccubus\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnSuccubus (int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_succubus_walk1,PIXRADIUS*30);\r
+ new->obclass = succubusobj;\r
+ new->speed = 2500;\r
+ new->flags |= of_shootable;\r
+ new->hitpoints = EasyHitPoints(12);\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_SuccubusShot\r
+=\r
+===============\r
+*/\r
+\r
+void T_SuccubusShot (objtype *ob)\r
+{\r
+ ShootPlayer(ob,sshotobj,ob->temp1 ? MSHOTSPEED : SSHOTSPEED,&s_succubus_shot1);\r
+// ob->state = &s_succubus_attack3;\r
+// ob->ticcount = ob->temp1 ? 7 : ob->state->tictime;\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ MAGE\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+void T_MageShoot (objtype *ob);\r
+\r
+extern statetype s_magepause;\r
+\r
+extern statetype s_mage1;\r
+extern statetype s_mage2;\r
+\r
+extern statetype s_mageattack1;\r
+extern statetype s_mageattack2;\r
+extern statetype s_mageattack3;\r
+\r
+extern statetype s_mageouch;\r
+\r
+extern statetype s_magedie1;\r
+extern statetype s_magedie2;\r
+\r
+\r
+statetype s_magepause = {MAGE1PIC,10,NULL,&s_mage2};\r
+\r
+statetype s_mage1 = {MAGE1PIC,20,T_EyeMage,&s_mage2};\r
+statetype s_mage2 = {MAGE2PIC,20,T_EyeMage,&s_mage1};\r
+\r
+//statetype s_mageattack1 = {MAGEATTACKPIC,20,NULL,&s_mageattack2};\r
+//statetype s_mageattack2 = {MAGEATTACKPIC,-1,T_MageShoot,&s_mageattack3};\r
+statetype s_mageattack3 = {MAGEATTACKPIC,30,NULL,&s_magepause};\r
+\r
+statetype s_mageouch = {MAGEOUCHPIC,10,NULL,&s_mage1};\r
+\r
+statetype s_magedie1 = {MAGEDIE1PIC,20,NULL,&s_magedie2};\r
+statetype s_magedie2 = {MAGEDIE2PIC,0,NULL,&s_magedie2};\r
+\r
+\r
+statetype s_mshot1 = {PSHOT1PIC,8,&T_ShootPlayer,&s_mshot2};\r
+statetype s_mshot2 = {PSHOT2PIC,8,&T_ShootPlayer,&s_mshot1};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnMage\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnMage (int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_mage1,PIXRADIUS*35);\r
+ new->obclass = mageobj;\r
+ new->speed = 3072;\r
+ new->flags |= of_shootable;\r
+ new->hitpoints = EasyHitPoints(12);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_EyeMage\r
+=\r
+= **********\r
+= ***NOTE*** This routine controls the thinks for the Eye, Mage, and Succubus.\r
+= **********\r
+=\r
+===============\r
+*/\r
+\r
+void T_EyeMage (objtype *ob)\r
+{\r
+ fixed tempx,tempy;\r
+ unsigned temp_tilex,temp_tiley;\r
+ int angle;\r
+\r
+ eye_delay -= realtics;\r
+ if (eye_delay < 0)\r
+ {\r
+ eye_mode = random(em_dummy);\r
+ eye_delay = (10*60);\r
+ }\r
+\r
+ tempx = player->x;\r
+ tempy = player->y;\r
+ temp_tilex = player->tilex;\r
+ temp_tiley = player->tiley;\r
+\r
+\r
+ switch (eye_mode)\r
+ {\r
+ case em_other1:\r
+ case em_other2:\r
+ case em_other3:\r
+ case em_other4:\r
+ player->x = ((long)other_x[eye_mode]<<TILESHIFT)+TILEGLOBAL/2;\r
+ player->y = ((long)other_y[eye_mode]<<TILESHIFT)+TILEGLOBAL/2;\r
+ player->tilex = other_x[eye_mode];\r
+ player->tiley = other_y[eye_mode];\r
+ break;\r
+ }\r
+\r
+ if (Chase(ob,true))\r
+ eye_delay = 0;\r
+\r
+ player->x = tempx;\r
+ player->y = tempy;\r
+ player->tilex = temp_tilex;\r
+ player->tiley = temp_tiley;\r
+\r
+\r
+ if (ob->obclass == mageobj) // do the mage shot\r
+ {\r
+ if (!random(10))\r
+ if (ShootPlayer(ob,mshotobj,MSHOTSPEED,&s_mshot1))\r
+ {\r
+ ob->state = &s_mageattack3;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+ }\r
+ else\r
+ if (ob->obclass == succubusobj) // do the succubus shot\r
+ {\r
+ angle = AngleNearPlayer(ob); // make sure angle is correct\r
+ // - see AngleNearPlayer\r
+ if (!random(5) && (angle != -1)) // if correct angle and random # attack\r
+ {\r
+ ob->state = &s_succubus_attack1; // change state to attack\r
+ ob->ticcount = ob->state->tictime; // init ticcount - otherwise\r
+ } // object may get hung in a\r
+ } // endless state\r
+\r
+ else\r
+ {\r
+ angle = AngleNearPlayer(ob); // do the eye shot\r
+\r
+ if (!random(2) && (angle != -1))\r
+ {\r
+ ob->state = &s_eye_shootplayer_1;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+ }\r
+\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ BUNNY\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_HarmlessBunnyWalk(objtype *ob);\r
+void T_Bunny(objtype *ob);\r
+\r
+extern statetype s_bunny_left1;\r
+extern statetype s_bunny_left2;\r
+extern statetype s_bunny_left3;\r
+extern statetype s_bunny_right1;\r
+extern statetype s_bunny_right2;\r
+extern statetype s_bunny_right3;\r
+extern statetype s_bunny_meta1;\r
+extern statetype s_bunny_meta2;\r
+extern statetype s_bunny_walk1;\r
+extern statetype s_bunny_walk2;\r
+extern statetype s_bunny_attack1;\r
+extern statetype s_bunny_attack2;\r
+extern statetype s_bunny_pause;\r
+extern statetype s_bunny_death1;\r
+extern statetype s_bunny_death2;\r
+extern statetype s_bunny_death3;\r
+\r
+statetype s_bunny_left1 = {BUNNY_LEFT1PIC, 55, NULL, &s_bunny_left2};\r
+statetype s_bunny_left2 = {BUNNY_LEFT1PIC, 10, T_HarmlessBunnyWalk, &s_bunny_left1};\r
+statetype s_bunny_left3 = {BUNNY_LEFT2PIC, 30, NULL, &s_bunny_left1};\r
+\r
+statetype s_bunny_right1 = {BUNNY_RIGHT1PIC, 55, NULL, &s_bunny_right2};\r
+statetype s_bunny_right2 = {BUNNY_RIGHT1PIC, 10, T_HarmlessBunnyWalk, &s_bunny_right1};\r
+statetype s_bunny_right3 = {BUNNY_RIGHT2PIC, 30, NULL, &s_bunny_right1};\r
+\r
+statetype s_bunny_meta1 = {BUNNY_META1PIC, 30, NULL, &s_bunny_meta2};\r
+statetype s_bunny_meta2 = {BUNNY_META2PIC, 30, NULL, &s_bunny_walk1};\r
+\r
+statetype s_bunny_walk1 = {BUNNY_WALK1PIC, 25, T_Bunny, &s_bunny_walk2};\r
+statetype s_bunny_walk2 = {BUNNY_WALK2PIC, 25, T_Bunny, &s_bunny_walk1};\r
+\r
+statetype s_bunny_attack1 = {BUNNY_WALK1PIC, 25, NULL, &s_bunny_attack2};\r
+statetype s_bunny_attack2 = {BUNNY_WALK2PIC, 25, T_DoDamage, &s_bunny_walk1};\r
+\r
+statetype s_bunny_ouch = {BUNNY_OUCHPIC, 30, NULL, &s_bunny_pause};\r
+statetype s_bunny_pause = {BUNNY_WALK1PIC, 50, T_Bunny, &s_bunny_walk2};\r
+\r
+statetype s_bunny_death1 = {BUNNY_OUCHPIC, 40, NULL, &s_bunny_death2};\r
+statetype s_bunny_death2 = {BUNNY_DEATH1PIC, 50, NULL, &s_bunny_death3};\r
+statetype s_bunny_death3 = {BUNNY_DEATH2PIC, 20, NULL, &s_bunny_death3};\r
+\r
+\r
+#define bunny_dir_hop ob->temp1\r
+#define bunny_delay ob->temp2\r
+#define LEFTSIDE 0x8 // 1=left 0=right --side showing\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnBunny\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnBunny (int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_bunny_left1,PIXRADIUS*35);\r
+ new->obclass = hbunnyobj;\r
+ new->speed = 1947;\r
+ new->temp1 = (random(3))+2;\r
+ new->temp2 = random(30);\r
+ new->flags &= ~of_shootable;\r
+ new->flags |= LEFTSIDE; //left side showing}\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_HarmlessBunnyWalk\r
+=\r
+===============\r
+*/\r
+\r
+\r
+void T_HarmlessBunnyWalk(objtype *ob)\r
+{\r
+ int valid_dir[8][2] = {{6,5}, {7,6}, {4,7}, {5,4}, {3,2}, {0,3}, {1,0}, {2,1}};\r
+ long move;\r
+ dirtype player_dir;\r
+ fixed old_x, old_y;\r
+ unsigned old_tilex, old_tiley;\r
+ long old_distance;\r
+\r
+\r
+ ob->temp2 -= realtics;\r
+ if (ob->temp2 <= 0)\r
+ {\r
+ if (CheckHandAttack(ob))\r
+ {\r
+ ob->temp2 = -1;\r
+ return;\r
+ }\r
+\r
+ actorat[ob->tilex][ob->tiley] = 0;\r
+ ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+ ob->distance = TILEGLOBAL;\r
+ ob->state = &s_bunny_meta1;\r
+ ob->ticcount = ob->state->tictime;\r
+ ob->obclass = bunnyobj;\r
+ ob->flags |= of_shootable;\r
+ ob->hitpoints = EasyHitPoints(10);\r
+ ob->dir = nodir;\r
+ ChaseThink(ob,true); // JTR - testing..\r
+ return;\r
+ }\r
+\r
+ // The direction of the player isn't updated so it must be\r
+ // calculated. This is done so the correct side (left/right)\r
+ // of the bunny will be showed.\r
+\r
+ if ((player->angle > 337) || (player->angle <= 22))\r
+ player_dir = east;\r
+ else\r
+ if (player->angle <= 67)\r
+ player_dir = northeast;\r
+ else\r
+ if (player->angle <= 112)\r
+ player_dir = north;\r
+ else\r
+ if (player->angle <= 157)\r
+ player_dir = northwest;\r
+ else\r
+ if (player->angle <= 202)\r
+ player_dir = west;\r
+ else\r
+ if (player->angle <= 247)\r
+ player_dir = southwest;\r
+ else\r
+ if (player->angle <= 292)\r
+ player_dir = south;\r
+ else\r
+ if (player->angle <= 337)\r
+ player_dir = southeast;\r
+ if (ob->temp1)\r
+ ob->temp1--;\r
+ else\r
+ ob->temp1 = (random(3))+2;\r
+ if (ob->flags & LEFTSIDE)\r
+ {\r
+ if (ob->temp1)\r
+ {\r
+ if (valid_dir[player_dir][0] != ob->dir)\r
+ {\r
+ ob->dir = valid_dir[player_dir][0];\r
+ }\r
+ }\r
+ else\r
+ {\r
+ ob->state = &s_bunny_right1;\r
+ ob->ticcount = ob->state->tictime;\r
+ ob->flags &= ~LEFTSIDE;\r
+ ob->dir = valid_dir[player_dir][1];\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (ob->temp1)\r
+ {\r
+ if (valid_dir[player_dir][1] != ob->dir)\r
+ {\r
+ ob->dir = valid_dir[player_dir][1];\r
+ }\r
+ }\r
+ else\r
+ {\r
+ ob->state = &s_bunny_left1;\r
+ ob->ticcount = ob->state->tictime;\r
+ ob->flags |= LEFTSIDE;\r
+ ob->dir = valid_dir[player_dir][2];\r
+ return;\r
+ }\r
+ }\r
+\r
+ move = ob->speed*tics;\r
+\r
+ do\r
+ {\r
+ old_distance = ob->distance;\r
+ old_x = ob->x;\r
+ old_y = ob->y;\r
+ old_tilex = ob->tilex;\r
+ old_tiley = ob->tiley;\r
+\r
+ MoveObj (ob, move);\r
+\r
+ ob->tilex = ob->x >> TILESHIFT;\r
+ ob->tiley = ob->y >> TILESHIFT;\r
+\r
+ if (ob->tilex == old_tilex && ob->tiley == old_tiley)\r
+ {\r
+ break;\r
+ }\r
+ else\r
+ if (actorat[ob->tilex][ob->tiley] == 0)\r
+ {\r
+ actorat[old_tilex][old_tiley] = 0;\r
+ actorat[ob->tilex][ob->tiley] = ob;\r
+ ob->distance = TILEGLOBAL;\r
+ }\r
+ else\r
+ {\r
+ ob->distance = old_distance;\r
+ ob->x = old_x;\r
+ ob->y = old_y;\r
+ ob->tilex = old_tilex;\r
+ ob->tiley = old_tiley;\r
+ return;\r
+ }\r
+\r
+ } while (0);\r
+\r
+ CalcBounds (ob);\r
+\r
+ if (ob->flags & LEFTSIDE)\r
+ ob->state = &s_bunny_left3;\r
+ else\r
+ ob->state = &s_bunny_right3;\r
+ ob->ticcount = ob->state->tictime;\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_Bunny\r
+=\r
+===============\r
+*/\r
+\r
+void T_Bunny(objtype *ob)\r
+{\r
+ if (Chase (ob, true) || (random(1000)<RANDOM_ATTACK))\r
+ {\r
+ ob->state = &s_bunny_attack1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+}\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if 0\r
+#define MSHOTDAMAGE 2\r
+#define MSHOTSPEED 10000\r
+\r
+#define ESHOTDAMAGE 1\r
+#define ESHOTSPEED 5000\r
+\r
+#define SSHOTDAMAGE 3\r
+#define SSHOTSPEED 6500\r
+\r
+#define RANDOM_ATTACK 20\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);\r
+void T_ShootPlayer(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+=============================================================================\r
+\r
+ RED DEMON\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_RedDemon (objtype *ob);\r
+void T_RedDemonCheckCnt (objtype *ob);\r
+\r
+extern statetype s_red_demonpause;\r
+\r
+extern statetype s_red_demon1;\r
+extern statetype s_red_demon2;\r
+extern statetype s_red_demon3;\r
+extern statetype s_red_demon4;\r
+\r
+extern statetype s_red_demonattack1;\r
+extern statetype s_red_demonattack2;\r
+extern statetype s_red_demonattack3;\r
+\r
+extern statetype s_red_demonouch;\r
+\r
+extern statetype s_red_demondie1;\r
+extern statetype s_red_demondie2;\r
+extern statetype s_red_demondie3;\r
+extern statetype s_red_demondie4;\r
+\r
+statetype s_red_demonpause = {RED_DEMON1PIC,30,NULL,&s_red_demon2};\r
+\r
+statetype s_red_demon1 = {RED_DEMON1PIC,20,T_RedDemon,&s_red_demon2};\r
+statetype s_red_demon2 = {RED_DEMON2PIC,20,T_RedDemon,&s_red_demon3};\r
+statetype s_red_demon3 = {RED_DEMON3PIC,20,T_RedDemon,&s_red_demon4};\r
+statetype s_red_demon4 = {RED_DEMON4PIC,20,T_RedDemon,&s_red_demon1};\r
+\r
+statetype s_red_demonattack1 = {RED_DEMONATTACK1PIC,20,NULL,&s_red_demonattack2};\r
+statetype s_red_demonattack2 = {RED_DEMONATTACK2PIC,20,NULL,&s_red_demonattack3};\r
+statetype s_red_demonattack3 = {RED_DEMONATTACK3PIC,30,T_DoDamage,&s_red_demon2};\r
+\r
+statetype s_red_demonouch = {RED_DEMONOUCHPIC,30,NULL,&s_red_demon1};\r
+\r
+statetype s_red_demondie1 = {RED_DEMONOUCHPIC,9,NULL,&s_red_demondie2};\r
+statetype s_red_demondie2 = {RED_DEMONDIE1PIC,9,T_RedDemonCheckCnt,&s_red_demondie1};\r
+statetype s_red_demondie3 = {RED_DEMONDIE2PIC,20,NULL,&s_red_demondie4};\r
+statetype s_red_demondie4 = {RED_DEMONDIE3PIC,10,NULL,&s_red_demondie4};\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnRedDemon\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnRedDemon (int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_red_demon1,PIXRADIUS*35);\r
+ new->obclass = reddemonobj;\r
+ new->speed = 2048;\r
+ new->flags |= of_shootable;\r
+ new->hitpoints = EasyHitPoints(50);\r
+ new->temp1 = 25;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_RedDemon\r
+=\r
+===============\r
+*/\r
+\r
+void T_RedDemon (objtype *ob)\r
+{\r
+ if (Chase (ob,true) || (random(1000)<RANDOM_ATTACK))\r
+ {\r
+ ob->state = &s_red_demonattack1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_RedDemonCheckCnt\r
+=\r
+===============\r
+*/\r
+\r
+void T_RedDemonCheckCnt (objtype *ob)\r
+{\r
+ ob->temp1--;\r
+ if (!ob->temp1)\r
+ {\r
+ ob->state = &s_red_demondie3;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GRELMINAR\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+void T_Grelminar (objtype *ob);\r
+void T_GrelminarShoot (objtype *ob);\r
+void T_Grelm_DropKey(objtype *ob);\r
+\r
+extern statetype s_grelpause;\r
+\r
+extern statetype s_grel1;\r
+extern statetype s_grel2;\r
+\r
+extern statetype s_grelattack1;\r
+extern statetype s_grelattack2;\r
+extern statetype s_grelattack3;\r
+\r
+extern statetype s_grelouch;\r
+\r
+extern statetype s_greldie1;\r
+extern statetype s_greldie2;\r
+extern statetype s_greldie3;\r
+extern statetype s_greldie4;\r
+extern statetype s_greldie5;\r
+extern statetype s_greldie5a;\r
+extern statetype s_greldie6;\r
+\r
+\r
+statetype s_grelpause = {GREL1PIC,50,NULL,&s_grel2};\r
+\r
+statetype s_grel1 = {GREL1PIC,20,T_Grelminar,&s_grel2};\r
+statetype s_grel2 = {GREL2PIC,20,T_Grelminar,&s_grel1};\r
+\r
+//statetype s_grelattack1 = {GRELATTACKPIC,20,NULL,&s_grelattack2};\r
+//statetype s_grelattack2 = {GRELATTACKPIC,-1,T_GrelminarShoot,&s_grelattack3};\r
+statetype s_grelattack3 = {GRELATTACKPIC,30,NULL,&s_grelpause};\r
+\r
+statetype s_grelouch = {GRELHITPIC,6,NULL,&s_grel1};\r
+\r
+statetype s_greldie1 = {GRELDIE1PIC,22,NULL,&s_greldie2};\r
+statetype s_greldie2 = {GRELDIE2PIC,22,NULL,&s_greldie3};\r
+statetype s_greldie3 = {GRELDIE3PIC,22,NULL,&s_greldie4};\r
+statetype s_greldie4 = {GRELDIE4PIC,22,NULL,&s_greldie5};\r
+statetype s_greldie5 = {GRELDIE5PIC,22,NULL,&s_greldie5a};\r
+statetype s_greldie5a = {GRELDIE5PIC,1,T_Grelm_DropKey,&s_greldie6};\r
+statetype s_greldie6 = {GRELDIE6PIC,0,NULL,&s_greldie6};\r
+\r
+\r
+extern statetype s_gshot1;\r
+\r
+statetype s_gshot1 = {SKULL_SHOTPIC,8,T_ShootPlayer,&s_gshot1};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnGrelminar\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnGrelminar (int tilex, int tiley)\r
+{\r
+ unsigned Grel_Hard;\r
+ unsigned DropKey;\r
+\r
+ SpawnNewObj(tilex,tiley,&s_grel1,PIXRADIUS*35);\r
+ new->obclass = grelmobj;\r
+ new->speed = 2048;\r
+ new->flags |= of_shootable;\r
+\r
+ //\r
+ // if Grelminar is to drop a key the info-plane byte to the right\r
+ // should have a 1 in the highbyte, else he will not drop the key.\r
+ //\r
+ DropKey = *(mapsegs[2]+farmapylookup[tiley]+tilex+1);\r
+ if (DropKey)\r
+ new->temp1 = DropKey>>8;\r
+ else\r
+ new->temp1 = 0;\r
+\r
+ //\r
+ // The info-plane byte below Grelminar will determine how powerful\r
+ // Grelminar is. If nothing is there, he is the most powerful.\r
+ // -- affected are the hit points and the shot damage.\r
+ // The hit points are controlled here, the shot damage is controlled\r
+ // within the spawning of the shot. See ShootPlayer for more info.\r
+ //\r
+ Grel_Hard = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+ if (Grel_Hard)\r
+ {\r
+ new->temp2 = Grel_Hard>>8;\r
+ new->hitpoints = EasyHitPoints((new->temp2 * 10));\r
+ }\r
+ else\r
+ new->hitpoints = EasyHitPoints(100);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Grelminar\r
+=\r
+===============\r
+*/\r
+\r
+void T_Grelminar (objtype *ob)\r
+{\r
+ Chase (ob,false);\r
+\r
+ if (!random(10))\r
+ if (ShootPlayer(ob,gshotobj,ob->temp2,&s_gshot1))\r
+ {\r
+ ob->state = &s_grelattack3;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+ if (CheckHandAttack(ob))\r
+ TakeDamage (ob->temp2*3);\r
+\r
+}\r
+\r
+\r
+//=================================\r
+//\r
+// T_Grelm_DropKey\r
+//\r
+//=================================\r
+void T_Grelm_DropKey(objtype *ob)\r
+{\r
+ if (!(ob->temp1))\r
+ {\r
+ ob->state = NULL;\r
+ return;\r
+ }\r
+\r
+ SpawnBonus(ob->tilex,ob->tiley,B_RKEY);\r
+ SD_PlaySound(GRELM_DEADSND);\r
+ ob->temp1 = false;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ BAT\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Bat (objtype *ob);\r
+void T_BatPast (objtype *ob);\r
+\r
+extern statetype s_bat1;\r
+extern statetype s_bat2;\r
+extern statetype s_bat3;\r
+extern statetype s_bat4;\r
+\r
+extern statetype s_batdie1;\r
+extern statetype s_batdie2;\r
+\r
+\r
+statetype s_bat1 = {BAT1PIC,6,T_Bat,&s_bat2};\r
+statetype s_bat2 = {BAT2PIC,6,T_Bat,&s_bat3};\r
+statetype s_bat3 = {BAT3PIC,6,T_Bat,&s_bat4};\r
+statetype s_bat4 = {BAT4PIC,6,T_Bat,&s_bat1};\r
+\r
+statetype s_batpast = {BAT4PIC,80,T_BatPast,&s_bat1};\r
+\r
+statetype s_batdie1 = {BATDIE1PIC,18,NULL,&s_batdie2};\r
+statetype s_batdie2 = {BATDIE2PIC,18,NULL,NULL};\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnBat\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnBat (int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_bat1,PIXRADIUS*35);\r
+ new->obclass = batobj;\r
+ new->flags |= of_shootable;\r
+\r
+ new->hitpoints = 1;\r
+ new->speed = 2000;\r
+}\r
+\r
+\r
+/*\r
+==================================\r
+=\r
+= BatChaseThink\r
+=\r
+==================================\r
+*/\r
+\r
+void BatChaseThink (objtype *obj)\r
+{\r
+ int deltax,deltay;\r
+\r
+ deltax=player->tilex - obj->tilex;\r
+ deltay=player->tiley - obj->tiley;\r
+\r
+ if (deltax>0)\r
+ deltax = 2;\r
+ else if (deltax<0)\r
+ deltax = 0;\r
+ else deltax = 1;\r
+\r
+ if (deltay>0)\r
+ deltay = 2;\r
+ else if (deltay<0)\r
+ deltay = 0;\r
+ else deltay = 1;\r
+\r
+ obj->dir = dirtable[deltay*3+deltax];\r
+ if (Walk(obj))\r
+ return;\r
+\r
+ obj->dir = dirtable[3+deltax];\r
+ if (Walk(obj))\r
+ return;\r
+\r
+ obj->dir = dirtable[deltay*3+1];\r
+ if (Walk(obj))\r
+ return;\r
+\r
+ obj->dir = nodir;\r
+}\r
+\r
+\r
+void BatRunThink (objtype *obj)\r
+{\r
+ int deltax,deltay;\r
+\r
+ deltax=player->tilex - obj->tilex;\r
+ deltay=player->tiley - obj->tiley;\r
+\r
+ if (deltax>=0)\r
+ deltax = 0;\r
+ else\r
+ deltax = 2;\r
+\r
+ if (deltay>=0)\r
+ deltay = 0;\r
+ else\r
+ deltay = 2;\r
+\r
+ obj->dir = dirtable[deltay*3+deltax];\r
+ if (Walk(obj))\r
+ return;\r
+\r
+ obj->dir = dirtable[3+deltax];\r
+ if (Walk(obj))\r
+ return;\r
+\r
+ obj->dir = dirtable[deltay*3+1];\r
+ Walk(obj);\r
+}\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Bat\r
+=\r
+===============\r
+*/\r
+\r
+void T_Bat (objtype *ob)\r
+{\r
+ long move;\r
+ long deltax,deltay,size;\r
+\r
+ move = ob->speed*tics;\r
+ size = (long)ob->size + player->size + move;\r
+\r
+\r
+ do\r
+ {\r
+ deltax = ob->x - player->x;\r
+ deltay = ob->y - player->y;\r
+\r
+ if (deltax <= size && deltax >= -size\r
+ && deltay <= size && deltay >= -size && !ob->temp1)\r
+ {\r
+ TakeDamage (4);\r
+ ob->temp1 = 2;\r
+ }\r
+\r
+ if (move < ob->distance)\r
+ {\r
+ MoveObj (ob,move);\r
+ break;\r
+ }\r
+\r
+ actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal\r
+ if (ob->dir == nodir)\r
+ ob->dir = north;\r
+\r
+ ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+ move -= ob->distance;\r
+\r
+ if (ob->temp1)\r
+ {\r
+ Walk (ob); // go straight\r
+ if (!--ob->temp1)\r
+ {\r
+ ob->state = &s_batpast;\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+ }\r
+ else\r
+ BatChaseThink (ob); // head towards player\r
+\r
+ actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker\r
+ } while (0); // just once\r
+ CalcBounds (ob);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_BatPast\r
+=\r
+===============\r
+*/\r
+\r
+void T_BatPast (objtype *ob)\r
+{\r
+ long move;\r
+ long deltax,deltay,size;\r
+\r
+ move = ob->speed*tics;\r
+\r
+ do\r
+ {\r
+ if (move < ob->distance)\r
+ {\r
+ MoveObj (ob,move);\r
+ break;\r
+ }\r
+ actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal\r
+\r
+ ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+ move -= ob->distance;\r
+\r
+ BatRunThink (ob);\r
+\r
+ actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker\r
+ } while (0); //(move)\r
+ CalcBounds (ob);\r
+}\r
+\r
+\r
+void T_ChaseThink(objtype *obj);\r
+void T_AwakeThink(objtype *obj);\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GODESS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Godess (objtype *ob);\r
+\r
+\r
+extern statetype s_godesspause;\r
+\r
+extern statetype s_godess_statue1;\r
+extern statetype s_godess_statue2;\r
+\r
+extern statetype s_godess1;\r
+extern statetype s_godess2;\r
+extern statetype s_godess3;\r
+\r
+extern statetype s_godessattack1;\r
+extern statetype s_godessattack2;\r
+extern statetype s_godessattack3;\r
+\r
+extern statetype s_godessouch;\r
+\r
+extern statetype s_godessdie1;\r
+extern statetype s_godessdie2;\r
+extern statetype s_godessdie3;\r
+\r
+\r
+statetype s_godesspause = {GODESS_WALK1PIC,25,NULL,&s_godess2};\r
+\r
+statetype s_godess_statue1 = {GODESS_STATUEPIC,20,T_ChaseThink,&s_godess_statue1};\r
+statetype s_godess_statue2 = {GODESS_STATUEPIC,1,T_AwakeThink,&s_godess1};\r
+\r
+statetype s_godess1 = {GODESS_WALK1PIC,20,T_ChaseThink,&s_godess2};\r
+statetype s_godess2 = {GODESS_WALK2PIC,20,T_ChaseThink,&s_godess3};\r
+statetype s_godess3 = {GODESS_WALK3PIC,20,T_ChaseThink,&s_godess1};\r
+\r
+statetype s_godessattack1 = {GODESS_ATTACK1PIC,10,NULL,&s_godessattack2};//20\r
+statetype s_godessattack2 = {GODESS_ATTACK2PIC,8,NULL,&s_godessattack3};//20\r
+statetype s_godessattack3 = {GODESS_ATTACK3PIC,10,T_DoDamage,&s_godesspause};//30\r
+\r
+statetype s_godessouch = {GODESS_OUCHPIC,10,NULL,&s_godess1};\r
+\r
+statetype s_godessdie1 = {GODESS_DEATH1PIC,65,NULL,&s_godessdie2};\r
+statetype s_godessdie2 = {GODESS_DEATH2PIC,30,NULL,&s_godessdie2};\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnGodess\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnGodess (int tilex, int tiley)\r
+{\r
+ objtype *ob;\r
+ short current_zombie_delay;\r
+ unsigned tile;\r
+\r
+ SpawnNewObj(tilex,tiley,&s_godess_statue1,PIXRADIUS*35);\r
+ ob = new;\r
+ zombie_mode = zm_wait_for_dark;\r
+\r
+ tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+ if (tile)\r
+ zombie_delay = (tile>>8)*30;\r
+ else\r
+ {\r
+ current_zombie_delay = (2*60)+random(4*60);\r
+ zombie_delay = zombie_base_delay+current_zombie_delay;\r
+ zombie_base_delay += current_zombie_delay;\r
+ if (zombie_base_delay > 8*60)\r
+ zombie_base_delay = 0;\r
+ }\r
+\r
+ new->obclass = realsolidobj;//godessobj;\r
+ new->speed = 3000;\r
+ new->flags |= of_shootable;\r
+ new->flags &= ~of_tree;\r
+// new->hitpoints = EasyHitPoints(10);\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ ANT\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Ant(objtype *ob);\r
+\r
+statetype s_ant_wait = {ANT_EGG1PIC,10,T_ChaseThink,&s_ant_wait};\r
+\r
+statetype s_ant_egg = {ANT_EGG2PIC,45,T_AwakeThink,&s_ant_walk1};\r
+\r
+statetype s_ant_walk1 = {ANT_WALK1PIC,20,T_ChaseThink,&s_ant_walk2};\r
+statetype s_ant_walk2 = {ANT_WALK2PIC,20,T_ChaseThink,&s_ant_walk3};\r
+statetype s_ant_walk3 = {ANT_WALK3PIC,20,T_ChaseThink,&s_ant_walk1};\r
+\r
+statetype s_ant_attack1 = {ANT_ATTACKPIC,20,NULL,&s_ant_pause};\r
+\r
+statetype s_ant_pause = {ANT_WALK2PIC,15,T_DoDamage,&s_ant_walk1};\r
+\r
+statetype s_ant_ouch = {ANT_WALK1PIC,15,NULL,&s_ant_walk1};\r
+\r
+statetype s_ant_die1 = {ANT_DEATH1PIC,40,NULL,&s_ant_die2};\r
+statetype s_ant_die2 = {ANT_DEATH2PIC,10,NULL,&s_ant_die3};\r
+statetype s_ant_die3 = {ANT_DEATH3PIC,10,NULL,&s_ant_die2};\r
+\r
+#define ant_mode ob->temp1\r
+#define ant_delay ob->temp2\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnAnt\r
+=\r
+===============\r
+*/\r
+void SpawnAnt(int tilex, int tiley)\r
+{\r
+ objtype *ob;\r
+ unsigned tile;\r
+ SpawnNewObj(tilex,tiley,&s_ant_wait,PIXRADIUS*35);\r
+ ob = new;\r
+\r
+ tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+ if (tile)\r
+ ant_delay = (tile>>8)*30;\r
+ else\r
+ ant_delay = 2*60+random(5*60);\r
+\r
+ ant_mode = zm_wait_for_dark;\r
+\r
+ new->obclass = antobj;\r
+ new->speed = 1900;\r
+ new->flags &= ~of_shootable;\r
+ new->hitpoints = EasyHitPoints(15);\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ ZOMBIE\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_zombie_rise1;\r
+extern statetype s_zombie_rise2;\r
+extern statetype s_zombie_rise3;\r
+extern statetype s_zombie_rise4;\r
+\r
+extern statetype s_zombie_alive1;\r
+extern statetype s_zombie_alive2;\r
+extern statetype s_zombie_alive3;\r
+\r
+//extern statetype s_zombie_attack1;\r
+\r
+extern statetype s_zombie_death1;\r
+extern statetype s_zombie_death2;\r
+extern statetype s_zombie_death3;\r
+\r
+void T_Zombie (objtype *ob);\r
+void T_ZombieRisen(objtype *obj);\r
+\r
+statetype s_zombie_risen = {ZOMB_WALK3PIC,1,T_AwakeThink,&s_zombie_alive1};\r
+\r
+statetype s_zombie_pause = {ZOMB_WALK1PIC,20,NULL,&s_zombie_alive1};\r
+\r
+statetype s_zombie_inground = {0,13,T_ChaseThink,&s_zombie_inground};\r
+\r
+statetype s_zombie_rise1 = {ZOMB_APPEAR1PIC,24,NULL,&s_zombie_rise2};\r
+statetype s_zombie_rise2 = {ZOMB_APPEAR2PIC,24,NULL,&s_zombie_rise3};\r
+statetype s_zombie_rise3 = {ZOMB_APPEAR3PIC,24,NULL,&s_zombie_rise4};\r
+statetype s_zombie_rise4 = {ZOMB_APPEAR4PIC,24,NULL,&s_zombie_risen};\r
+\r
+statetype s_zombie_alive1 = {ZOMB_WALK1PIC,13,T_ChaseThink,&s_zombie_alive2};\r
+statetype s_zombie_alive2 = {ZOMB_WALK2PIC,13,T_ChaseThink,&s_zombie_alive3};\r
+statetype s_zombie_alive3 = {ZOMB_WALK3PIC,13,T_ChaseThink,&s_zombie_alive1};\r
+\r
+statetype s_zombie_death1 = {ZOMB_DIE1PIC,16,NULL,&s_zombie_death2};\r
+statetype s_zombie_death2 = {ZOMB_DIE2PIC,16,NULL,&s_zombie_death3};\r
+statetype s_zombie_death3 = {ZOMB_DIE3PIC,16,NULL,&s_zombie_death3};\r
+\r
+statetype s_zombie_attack = {ZOMB_ATTACKPIC,15,T_DoDamage,&s_zombie_pause};\r
+//statetype s_zombie_attack1 = {ZOMB_ATTACKPIC,15,NULL,&s_zombie_pause};\r
+\r
+statetype s_zombie_ouch = {ZOMB_OUCHPIC,15,NULL,&s_zombie_alive1};\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// SpawnZombie()\r
+//--------------------------------------------------------------------------\r
+void SpawnZombie (int tilex, int tiley)\r
+{\r
+ objtype *ob;\r
+ short current_zombie_delay;\r
+ unsigned tile;\r
+\r
+ SpawnNewObj(tilex,tiley,&s_zombie_inground,35*PIXRADIUS);\r
+ ob = new;\r
+ zombie_mode = zm_wait_for_dark;\r
+\r
+ tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+ if (tile)\r
+ zombie_delay = (tile>>8)*30;\r
+ else\r
+ {\r
+ current_zombie_delay = (2*60)+random(4*60);\r
+ zombie_delay = zombie_base_delay+current_zombie_delay;\r
+ zombie_base_delay += current_zombie_delay;\r
+ if (zombie_base_delay > 8*60)\r
+ zombie_base_delay = 0;\r
+ }\r
+\r
+ new->speed = 2500;\r
+ new->obclass = zombieobj;\r
+ new->hitpoints = EasyHitPoints(8);\r
+ new->active = yes;\r
+ new->flags &= ~of_shootable;\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ TREE\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_tree_pause;\r
+extern statetype s_tree_idle;\r
+extern statetype s_tree_awakening1;\r
+extern statetype s_tree_awakening2;\r
+extern statetype s_tree_walk1;\r
+extern statetype s_tree_walk2;\r
+extern statetype s_tree_walk3;\r
+extern statetype s_tree_death1;\r
+extern statetype s_tree_death2;\r
+extern statetype s_tree_death3;\r
+extern statetype s_tree_death4;\r
+extern statetype s_tree_death5;\r
+extern statetype s_tree_attack1;\r
+extern statetype s_tree_attack2;\r
+extern statetype s_tree_attack3;\r
+extern statetype s_tree_ouch;\r
+\r
+void T_Tree (objtype *ob);\r
+void T_DeathThink(objtype *ob);\r
+\r
+statetype s_tree_pause = {TREE_WALK1PIC,25,NULL,&s_tree_walk2};\r
+\r
+statetype s_tree_idle = {TREE_IDLEPIC,13,T_ChaseThink,&s_tree_idle};\r
+\r
+statetype s_tree_awakening1 = {TREE_AWAKENINGPIC,1,T_AwakeThink,&s_tree_awakening2};\r
+statetype s_tree_awakening2 = {TREE_AWAKENINGPIC,50,NULL,&s_tree_walk1};\r
+\r
+statetype s_tree_walk1 = {TREE_WALK1PIC,13,T_ChaseThink,&s_tree_walk2};\r
+statetype s_tree_walk2 = {TREE_WALK2PIC,13,T_ChaseThink,&s_tree_walk1};\r
+\r
+statetype s_tree_death1 = {TREE_DEATH1PIC,45,NULL,&s_tree_death2};\r
+statetype s_tree_death2 = {TREE_DEATH2PIC,25,NULL,&s_tree_death3};\r
+statetype s_tree_death3 = {TREE_DEATH1PIC,15,T_DeathThink,&s_tree_death4};\r
+statetype s_tree_death4 = {TREE_DEATH2PIC,15,T_DeathThink,&s_tree_death5};\r
+statetype s_tree_death5 = {TREE_DEATH3PIC,15,T_DeathThink,&s_tree_death3};\r
+\r
+statetype s_tree_attack1 = {TREE_ATTACK1PIC,15,T_DoDamage,&s_tree_attack2};\r
+statetype s_tree_attack2 = {TREE_ATTACK2PIC,15,T_DoDamage,&s_tree_attack3};\r
+statetype s_tree_attack3 = {TREE_ATTACK3PIC,15,T_DoDamage,&s_tree_pause};\r
+\r
+statetype s_tree_ouch = {TREE_AWAKENINGPIC,15,NULL,&s_tree_walk1};\r
+\r
+\r
+#define zombie_mode ob->temp1\r
+#define zombie_delay ob->temp2\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// SpawnTree()\r
+//--------------------------------------------------------------------------\r
+void SpawnTree(int tilex, int tiley)\r
+{\r
+ objtype *ob;\r
+ short current_zombie_delay;\r
+ unsigned tile;\r
+\r
+ SpawnNewObj(tilex,tiley,&s_tree_idle,35*PIXRADIUS);\r
+ ob = new;\r
+ zombie_mode = zm_wait_for_dark;\r
+\r
+ tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+ if (tile)\r
+ zombie_delay = (tile>>8)*30;\r
+ else\r
+ {\r
+ current_zombie_delay = (2*60)+random(4*60);\r
+ zombie_delay = zombie_base_delay+current_zombie_delay;\r
+ zombie_base_delay += current_zombie_delay;\r
+ if (zombie_base_delay > 8*60)\r
+ zombie_base_delay = 0;\r
+ }\r
+\r
+ new->speed = 2500;\r
+ new->obclass = realsolidobj;\r
+// new->hitpoints = EasyHitPoints(12);\r
+ new->active = yes;\r
+ new->flags |= of_shootable;\r
+ new->flags |= of_tree;\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// T_DeathThink()\r
+//--------------------------------------------------------------------------\r
+void T_DeathThink(objtype *ob)\r
+{\r
+ char num;\r
+\r
+ if ((ob->ticcount - realtics) <= 0)\r
+ {\r
+ num = random(2);\r
+ switch (ob->temp1)\r
+ {\r
+ case 3:\r
+ if (num)\r
+ ob->state = &s_tree_death4;\r
+ else\r
+ ob->state = &s_tree_death5;\r
+ ob->temp1++;\r
+ break;\r
+\r
+ case 4:\r
+ if (num)\r
+ ob->state = &s_tree_death3;\r
+ else\r
+ ob->state = &s_tree_death5;\r
+ ob->temp1++;\r
+ break;\r
+\r
+ case 5:\r
+ if (num)\r
+ ob->state = &s_tree_death3;\r
+ else\r
+ ob->state = &s_tree_death4;\r
+ ob->temp1 = 3;\r
+ break;\r
+ }\r
+ ob->ticcount = ob->state->tictime;\r
+ }\r
+\r
+\r
+\r
+ if (CheckHandAttack(ob))\r
+ TakeDamage (1);\r
+}\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////////\r
+//\r
+// GENERAL THINK ROUTINES USED BY THE ZOMBIE, TREE, ANT, AND GODESS\r
+// ----trying to cut down on the code size----\r
+//\r
+//////////////////////////////////////////////////////////////////////////\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// T_ChaseThink()\r
+//--------------------------------------------------------------------------\r
+void T_ChaseThink(objtype *ob)\r
+{\r
+ switch (zombie_mode)\r
+ {\r
+ case zm_wait_for_dark:\r
+#if 0\r
+ if (gamestate.mapon == 0)\r
+ {\r
+ if (BGFLAGS & BGF_NIGHT)\r
+ zombie_mode = zm_wait_to_rise;\r
+ }\r
+ else\r
+#endif\r
+ zombie_mode = zm_wait_to_rise;\r
+ break;\r
+\r
+ case zm_wait_to_rise:\r
+ if (zombie_delay < 0)\r
+ {\r
+ if ((ob->tilex == player->tilex) && (ob->tiley == player->tiley))\r
+ break;\r
+ if (CheckHandAttack(ob))\r
+ break;\r
+\r
+ ob->active = always;\r
+ switch (ob->obclass)\r
+ {\r
+ case zombieobj:\r
+ ob->state = &s_zombie_rise1;\r
+ break;\r
+\r
+ case antobj:\r
+ ob->state = &s_ant_egg;\r
+ break;\r
+\r
+ case realsolidobj: //tree and godess\r
+ if (ob->flags & of_tree)\r
+ ob->state = &s_tree_awakening1;\r
+ else\r
+ ob->state = &s_godess_statue2;\r
+ break;\r
+ }\r
+ ob->ticcount = ob->state->tictime;\r
+ zombie_mode = zm_active;\r
+ }\r
+ else\r
+ zombie_delay -= tics;\r
+\r
+ break;\r
+\r
+ case zm_active:\r
+ if (Chase (ob,true) || (random(1000)<RANDOM_ATTACK))\r
+ {\r
+ switch (ob->obclass)\r
+ {\r
+ case zombieobj:\r
+ ob->state = &s_zombie_attack;\r
+ break;\r
+\r
+ case antobj:\r
+ ob->state = &s_ant_attack1;\r
+ break;\r
+\r
+ case treeobj:\r
+ ob->state = &s_tree_attack1;\r
+ break;\r
+\r
+ case godessobj:\r
+ ob->state = &s_godessattack1;\r
+ break;\r
+ }\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// T_AwakeThink()\r
+//--------------------------------------------------------------------------\r
+void T_AwakeThink(objtype *obj)\r
+{\r
+ if (obj->obclass == realsolidobj)\r
+ {\r
+ if (obj->flags & of_tree)\r
+ obj->obclass = treeobj;\r
+ else\r
+ obj->obclass = godessobj;\r
+ obj->hitpoints = EasyHitPoints(12);\r
+ }\r
+ else\r
+ obj->flags |= of_shootable;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// ShootPlayer()\r
+//--------------------------------------------------------------------------\r
+boolean ShootPlayer(objtype *ob, short obclass, short speed, statetype *state)\r
+{\r
+ int angle = AngleNearPlayer(ob);\r
+\r
+ if (angle == -1)\r
+ return(false);\r
+\r
+ DSpawnNewObjFrac (ob->x,ob->y,state,PIXRADIUS*35);\r
+ new->obclass = obclass;\r
+ new->active = always;\r
+ new->angle = angle;\r
+\r
+ //\r
+ // If the shot is Grelminar's, then determine the power of the shot.\r
+ // The shot speed is hard-wired as 10000. But the shot power is\r
+ // determined by speed. Speed now contains "Grelminar's level of\r
+ // hardness" and this is multiplied by 3 to get the shot power.\r
+ //\r
+ if (obclass == gshotobj)\r
+ {\r
+ new->speed = 10000;\r
+ new->temp1 = speed*3;\r
+ }\r
+ else\r
+ new->speed = speed;\r
+\r
+\r
+ return(true);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// T_ShootPlayer()\r
+//--------------------------------------------------------------------------\r
+void T_ShootPlayer(objtype *ob)\r
+{\r
+ objtype *check;\r
+ long xmove,ymove,speed;\r
+\r
+ speed = ob->speed*tics;\r
+\r
+ xmove = FixedByFrac(speed,costable[ob->angle]);\r
+ ymove = -FixedByFrac(speed,sintable[ob->angle]);\r
+\r
+ if (ShotClipMove(ob,xmove,ymove))\r
+ {\r
+ ob->state = &s_pshot_exp1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+\r
+ ob->tilex = ob->x >> TILESHIFT;\r
+ ob->tiley = ob->y >> TILESHIFT;\r
+\r
+\r
+// check for collision with wall\r
+//\r
+ if (tilemap[ob->tilex][ob->tiley])\r
+ {\r
+ SD_PlaySound (SHOOTWALLSND);\r
+ ob->state = &s_pshot_exp1;\r
+ ob->ticcount = s_pshot_exp1.tictime;\r
+ return;\r
+ }\r
+\r
+\r
+\r
+// check for collision with player\r
+//\r
+ if ( ob->xl <= player->xh\r
+ && ob->xh >= player->xl\r
+ && ob->yl <= player->yh\r
+ && ob->yh >= player->yl)\r
+ {\r
+ switch (ob->obclass)\r
+ {\r
+ case eshotobj:\r
+ TakeDamage (ESHOTDAMAGE);\r
+ break;\r
+\r
+ case mshotobj:\r
+ TakeDamage (MSHOTDAMAGE);\r
+ break;\r
+\r
+ case gshotobj:\r
+ TakeDamage (ob->temp1); // the damage of Grelminar's shot -\r
+ break; // see Grelminar's spawning\r
+\r
+ case sshotobj:\r
+ TakeDamage(SSHOTDAMAGE);\r
+ break;\r
+\r
+ case dshotobj:\r
+ TakeDamage(7);\r
+ break;\r
+ }\r
+ ob->state = NULL;\r
+ return;\r
+ }\r
+\r
+// check for collision with other solid and realsolid objects.\r
+// Great terminology!! -- solid objects really aren't solid\r
+// -- realsolid objects ARE solid\r
+// if ((actorat[ob->tilex][ob->tiley]) && (actorat[ob->tilex][ob->tiley]->obclass != ob->obclass))\r
+ if (((actorat[ob->tilex][ob->tiley]->obclass == realsolidobj) ||\r
+ (actorat[ob->tilex][ob->tiley]->obclass == solidobj)) &&\r
+ (actorat[ob->tilex][ob->tiley]->flags & of_shootable))\r
+ {\r
+ ob->state = &s_pshot_exp1;\r
+ ob->ticcount = s_pshot_exp1.tictime;\r
+ return;\r
+ }\r
+\r
+\r
+// check for collision with player\r
+//\r
+ for (check = player->next; check; check=check->next)\r
+ if ((ob->flags & of_shootable) && ob->obclass != mageobj\r
+ && ob->xl <= check->xh\r
+ && ob->xh >= check->xl\r
+ && ob->yl <= check->yh\r
+ && ob->yh >= check->yl)\r
+ {\r
+ switch (ob->obclass)\r
+ {\r
+ case eshotobj:\r
+ ShootActor (check,ESHOTDAMAGE);\r
+ break;\r
+\r
+ case mshotobj:\r
+ ShootActor (check,MSHOTDAMAGE);\r
+ break;\r
+\r
+ case gshotobj:\r
+ ShootActor (check,25); //NOLAN--check on me!!!!!!!\r
+ break;\r
+\r
+ case pshotobj:\r
+ ShootActor (check,25);\r
+ break;\r
+\r
+ case sshotobj:\r
+ ShootActor(check, SSHOTDAMAGE);\r
+ break;\r
+\r
+ case dshotobj:\r
+ ShootActor(check, 7);\r
+ break;\r
+ }\r
+ ob->state = &s_pshot_exp1;\r
+ ob->ticcount = s_pshot_exp1.tictime;\r
+ return;\r
+ }\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// AngleNearPlayer()\r
+//-------------------------------------------------------------------------\r
+int AngleNearPlayer(objtype *ob)\r
+{\r
+ int angle=-1;\r
+ int xdiff = ob->tilex-player->tilex;\r
+ int ydiff = ob->tiley-player->tiley;\r
+\r
+ if (ob->tiley == player->tiley)\r
+ {\r
+ if (ob->tilex < player->tilex)\r
+ angle = 0;\r
+ else\r
+ angle = 180;\r
+ }\r
+ else\r
+ if (ob->tilex == player->tilex)\r
+ {\r
+ if (ob->tiley < player->tiley)\r
+ angle = 270;\r
+ else\r
+ angle = 90;\r
+ }\r
+ else\r
+ if (xdiff == ydiff)\r
+ if (ob->tilex < player->tilex)\r
+ {\r
+ if (ob->tiley < player->tiley)\r
+ angle = 315;\r
+ else\r
+ angle = 45;\r
+ }\r
+ else\r
+ {\r
+ if (ob->tiley < player->tiley)\r
+ angle = 225;\r
+ else\r
+ angle = 135;\r
+ }\r
+\r
+ return(angle);\r
+}\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C4_PLAY.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+//\r
+// ARCH OBJECTS\r
+//\r
+//-------------------------------------------------------------------------\r
+\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// ARCH\r
+//-------------------------------------------------------------------------\r
+\r
+void SpawnArch(int tilex, int tiley, int num);\r
+\r
+extern statetype s_arch_1;\r
+extern statetype s_arch_2;\r
+extern statetype s_arch_3;\r
+extern statetype s_arch_4;\r
+extern statetype s_arch_5;\r
+extern statetype s_arch_6;\r
+extern statetype s_arch_7;\r
+extern statetype s_arch_8;\r
+extern statetype s_arch_9;\r
+extern statetype s_arch_10;\r
+extern statetype s_arch_11;\r
+extern statetype s_arch_12;\r
+extern statetype s_arch_13;\r
+\r
+statetype s_arch_1 = {ARCH1PIC, 20, NULL, &s_arch_1};\r
+statetype s_arch_2 = {ARCH2PIC, 20, NULL, &s_arch_2};\r
+statetype s_arch_3 = {ARCH3PIC, 20, NULL, &s_arch_3};\r
+statetype s_arch_4 = {ARCH4PIC, 20, NULL, &s_arch_4};\r
+statetype s_arch_5 = {ARCH5PIC, 20, NULL, &s_arch_5};\r
+statetype s_arch_6 = {ARCH6PIC, 20, NULL, &s_arch_6};\r
+statetype s_arch_7 = {ARCH7PIC, 20, NULL, &s_arch_7};\r
+statetype s_arch_8 = {ARCH8PIC, 20, NULL, &s_arch_8};\r
+statetype s_arch_9 = {ARCH9PIC, 20, NULL, &s_arch_9};\r
+statetype s_arch_10 = {ARCH10PIC, 20, NULL, &s_arch_10};\r
+statetype s_arch_11 = {ARCH11PIC, 20, NULL, &s_arch_11};\r
+statetype s_arch_12 = {ARCH12PIC, 20, NULL, &s_arch_12};\r
+statetype s_arch_13 = {ARCH13PIC, 20, NULL, &s_arch_13};\r
+\r
+void SpawnArch (int tilex, int tiley, int num)\r
+{\r
+ statetype *objstate;\r
+\r
+\r
+ switch (num)\r
+ {\r
+ case 1:\r
+ objstate = &s_arch_1;\r
+ break;\r
+ case 2:\r
+ objstate = &s_arch_2;\r
+ break;\r
+ case 3:\r
+ objstate = &s_arch_3;\r
+ break;\r
+ case 4:\r
+ objstate = &s_arch_4;\r
+ break;\r
+ case 5:\r
+ objstate = &s_arch_5;\r
+ break;\r
+ case 6:\r
+ objstate = &s_arch_6;\r
+ break;\r
+ case 7:\r
+ objstate = &s_arch_7;\r
+ break;\r
+ case 8:\r
+ objstate = &s_arch_8;\r
+ break;\r
+ case 9:\r
+ objstate = &s_arch_9;\r
+ break;\r
+ case 10:\r
+ objstate = &s_arch_10;\r
+ break;\r
+ case 11:\r
+ objstate = &s_arch_11;\r
+ break;\r
+ case 12:\r
+ objstate = &s_arch_12;\r
+ break;\r
+ case 13:\r
+ objstate = &s_arch_13;\r
+ break;\r
+ }\r
+ ASpawnNewObj(tilex,tiley,objstate,PIXRADIUS*35);\r
+ new->obclass = solidobj;\r
+ new->flags &= ~of_shootable;\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+//\r
+// MISC OBJECTS\r
+//\r
+//-------------------------------------------------------------------------\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// COLUMN, SULPHUR GAS HOLE, FIRE POT, FOUNTAIN\r
+//-------------------------------------------------------------------------\r
+\r
+\r
+void SpawnMiscObjects(int tilex, int tiley, int num);\r
+\r
+extern statetype s_column;\r
+extern statetype s_sulphur_gas_1;\r
+extern statetype s_sulphur_gas_2;\r
+extern statetype s_sulphur_gas_3;\r
+extern statetype s_fire_pot_1;\r
+extern statetype s_fire_pot_2;\r
+extern statetype s_fountain;\r
+\r
+statetype s_column = {COLUMNPIC, 20, NULL, &s_column};\r
+statetype s_sulphur_gas_1 = {SULPHUR_GAS_1PIC, 20, NULL, &s_sulphur_gas_2};\r
+statetype s_sulphur_gas_2 = {SULPHUR_GAS_2PIC, 20, NULL, &s_sulphur_gas_3};\r
+statetype s_sulphur_gas_3 = {SULPHUR_GAS_3PIC, 20, NULL, &s_sulphur_gas_1};\r
+statetype s_fire_pot_1 = {FIRE_POT_1PIC, 20, NULL, &s_fire_pot_2};\r
+statetype s_fire_pot_2 = {FIRE_POT_2PIC, 20, NULL, &s_fire_pot_1};\r
+statetype s_fountain = {WFOUNTAINPIC, 20, NULL, &s_fountain};\r
+\r
+\r
+void SpawnMiscObjects(int tilex, int tiley, int num)\r
+{\r
+ statetype *objstate;\r
+\r
+ switch (num)\r
+ {\r
+ case 1:\r
+ objstate = &s_column;\r
+ break;\r
+\r
+ case 2:\r
+ objstate = &s_sulphur_gas_1;\r
+ break;\r
+\r
+ case 3:\r
+ objstate = &s_fire_pot_1;\r
+ break;\r
+\r
+ case 4:\r
+ objstate = &s_fountain;\r
+ break;\r
+ }\r
+\r
+ SpawnNewObj(tilex,tiley,objstate,PIXRADIUS*35);\r
+ new->obclass = realsolidobj;\r
+ if (num == 2)\r
+ new->flags &= ~of_shootable;\r
+ else\r
+ new->flags |= of_shootable;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+#if 0\r
+void SpawnColumn(int tilex, int tiley);\r
+\r
+extern statetype s_column;\r
+statetype s_column = {COLUMNPIC, 20, NULL, &s_column};\r
+\r
+void SpawnColumn(int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_column,PIXRADIUS*35);\r
+ new->obclass = realsolidobj;\r
+ new->flags |= of_shootable;\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// SULPHUR GAS\r
+//-------------------------------------------------------------------------\r
+\r
+void SpawnSulphurGas(int tilex, int tiley);\r
+\r
+extern statetype s_sulphur_gas_1;\r
+extern statetype s_sulphur_gas_2;\r
+extern statetype s_sulphur_gas_3;\r
+\r
+statetype s_sulphur_gas_1 = {SULPHUR_GAS_1PIC, 20, NULL, &s_sulphur_gas_2};\r
+statetype s_sulphur_gas_2 = {SULPHUR_GAS_2PIC, 20, NULL, &s_sulphur_gas_3};\r
+statetype s_sulphur_gas_3 = {SULPHUR_GAS_3PIC, 20, NULL, &s_sulphur_gas_1};\r
+\r
+void SpawnSulphurGas(int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_sulphur_gas_1,PIXRADIUS*35);\r
+ new->obclass = realsolidobj;\r
+ new->flags &= ~of_shootable;\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// FIRE POT\r
+//-------------------------------------------------------------------------\r
+\r
+void SpawnFirePot(int tilex, int tiley);\r
+\r
+extern statetype s_fire_pot_1;\r
+extern statetype s_fire_pot_2;\r
+\r
+statetype s_fire_pot_1 = {FIRE_POT_1PIC, 20, NULL, &s_fire_pot_2};\r
+statetype s_fire_pot_2 = {FIRE_POT_2PIC, 20, NULL, &s_fire_pot_1};\r
+\r
+void SpawnFirePot(int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_fire_pot_1,PIXRADIUS*35);\r
+ new->obclass = realsolidobj;\r
+ new->flags |= of_shootable;\r
+\r
+}\r
+\r
+//------------------------------------------------------------------------\r
+// FOUNTAIN\r
+//------------------------------------------------------------------------\r
+\r
+void SpawnFountain(int tilex, int tiley);\r
+\r
+extern statetype s_fountain;\r
+statetype s_fountain = {WFOUNTAINPIC, 20, NULL, &s_fountain};\r
+\r
+void SpawnFountain(int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_fountain,PIXRADIUS*35);\r
+ new->obclass = realsolidobj;\r
+ new->flags |= of_shootable;\r
+}\r
+\r
+#endif\r
+\r
+\r
+//------------------------------------------------------------------------\r
+// FORCE FIELD\r
+//------------------------------------------------------------------------\r
+\r
+void SpawnForceField(int tilex, int tiley);\r
+void T_ForceField(objtype *ob);\r
+void T_ForceFieldRemove(objtype *ob);\r
+\r
+extern statetype s_force_field_1;\r
+extern statetype s_force_field_2;\r
+extern statetype s_force_field_3;\r
+extern statetype s_force_field_4;\r
+extern statetype s_force_field_die1;\r
+\r
+statetype s_force_field_1 = {FORCE_FIELD_1PIC, 10, T_ForceField, &s_force_field_2};\r
+statetype s_force_field_2 = {FORCE_FIELD_2PIC, 10, T_ForceField, &s_force_field_3};\r
+statetype s_force_field_3 = {FORCE_FIELD_3PIC, 10, T_ForceField, &s_force_field_4};\r
+statetype s_force_field_4 = {FORCE_FIELD_4PIC, 10, T_ForceField, &s_force_field_1};\r
+\r
+statetype s_force_field_die = {0,0,T_ForceFieldRemove,&s_force_field_die1};\r
+statetype s_force_field_die1 = {0,0,NULL,NULL};\r
+\r
+void SpawnForceField(int tilex, int tiley)\r
+{\r
+ SpawnNewObj(tilex,tiley,&s_force_field_1,PIXRADIUS*35);\r
+ new->obclass = solidobj;\r
+ new->hitpoints = EasyHitPoints(20);\r
+ new->flags |= of_forcefield; //sets bit 7 :: makes it nonsolid, but also detectable\r
+ // without adding another object type!\r
+ new->flags |= of_shootable;\r
+}\r
+\r
+void T_ForceField(objtype *ob)\r
+{\r
+ long move,deltax,deltay,size;\r
+\r
+ size = (long)ob->size + player->size;\r
+\r
+ deltax = ob->x - player->x;\r
+ deltay = ob->y - player->y;\r
+\r
+ if (deltax <= size && deltax >= -size\r
+ && deltay <= size && deltay >= -size)\r
+ TakeDamage (20);\r
+\r
+}\r
+\r
+void T_ForceFieldRemove(objtype *ob)\r
+{\r
+ actorat[ob->tilex][ob->tiley] = 0;\r
+}\r
+\r
+\r
+//------------------------------------------------------------------------\r
+// SKELETON HANGING FROM CEILING\r
+//------------------------------------------------------------------------\r
+\r
+void SpawnSkeletonHanging(int tilex, int tiley);\r
+void T_SkelHangThink(objtype *ob);\r
+\r
+extern statetype s_skeleton_hanging;\r
+statetype s_skeleton_hanging = {SKEL_HANGPIC, 20, T_SkelHangThink, &s_skeleton_hanging};\r
+\r
+void SpawnSkeletonHanging(int tilex, int tiley)\r
+{\r
+ unsigned tile;\r
+\r
+ SpawnNewObj(tilex,tiley,&s_skeleton_hanging,PIXRADIUS*35);\r
+ new->obclass = solidobj;\r
+\r
+ tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+ if (tile)\r
+ new->temp1 = (tile>>8)*30;\r
+ else\r
+ new->temp1 = (3*60)+random(4*60);\r
+\r
+ new->flags |= of_shootable;\r
+}\r
+\r
+void T_SkelHangThink(objtype *ob)\r
+{\r
+ ob->temp1 -= realtics;\r
+ if (ob->temp1 <= 0)\r
+ {\r
+ ob->state = &s_skel_1;\r
+ ob->ticcount = ob->state->tictime;\r
+ ob->obclass = skeletonobj;\r
+ ob->speed = 2036;\r
+ ob->flags |= of_shootable;\r
+ ob->hitpoints = EasyHitPoints(12);\r
+ }\r
+}\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// EasyHitPoints\r
+//\r
+// Checks to see if the player has selected the easy mode for playing.\r
+// If so then the normal hit points are cut in half.\r
+// This is when the object is spawned.\r
+//\r
+// Parms\r
+// NrmHitPts - the normal hit points\r
+//\r
+// Returns\r
+// Half of NrmHitPts\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+int EasyHitPoints(int NrmHitPts)\r
+{\r
+ if (EASYMODEON) // Wimpy, Wimpy, Wimpy!!!!!\r
+ {\r
+ return(NrmHitPts/4);\r
+ }\r
+ else\r
+ return(NrmHitPts);\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// EasyDoDamage\r
+//\r
+// Checks to see if the player has selected the easy mode for playing.\r
+// If so then the normal amount of damage is cut in half.\r
+// This is called each time a monster does damage.\r
+//\r
+// Parms\r
+// Damage - the normal damage taken\r
+//\r
+// Returns\r
+// Half of Damage\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+int EasyDoDamage(int Damage)\r
+{\r
+ if (EASYMODEON) // Wimpy, Wimpy, Wimpy!!!!!\r
+ return(Damage/2);\r
+ else\r
+ return(Damage);\r
+}\r
+\r
--- /dev/null
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+IDEAL\r
+\r
+MODEL MEDIUM,C\r
+\r
+INCLUDE "ID_ASM.EQU"\r
+\r
+VIEWWIDTH = (40*8) ;33\r
+GC_INDEX = 03CEh\r
+\r
+DATASEG\r
+EVEN\r
+\r
+;=================== Tables filled in by DrawVWall ==========================\r
+\r
+;\r
+; wallheight has the height (scale number) of that collumn of scaled wall\r
+; it is pre bounded to 1-MAXSCALE (the actuial height on screen is 2*height)\r
+;\r
+wallheight dw VIEWWIDTH dup (?)\r
+\r
+;\r
+; wallwidth has the pixel width (1-7) of that collumn\r
+;\r
+wallwidth dw VIEWWIDTH dup (?)\r
+\r
+;\r
+; wallseg has the segment of the wall picture\r
+;\r
+wallseg dw VIEWWIDTH dup (?)\r
+\r
+;\r
+; wallofs has the offset of the wall picture\r
+;\r
+wallofs dw VIEWWIDTH dup (?)\r
+\r
+;============================================================================\r
+\r
+;\r
+; screenbyte is just position/8\r
+;\r
+LABEL screenbyte WORD\r
+pos = 0\r
+REPT VIEWWIDTH\r
+ dw pos/8\r
+pos = pos+1\r
+ENDM\r
+\r
+;\r
+; screenbit is (position&7)*16\r
+;\r
+LABEL screenbit WORD\r
+pos = 0\r
+REPT VIEWWIDTH\r
+ dw (pos AND 7)*16\r
+pos = pos+1\r
+ENDM\r
+\r
+;\r
+; Use offset: screenbit[]+pixwidth*2\r
+; acess from bitmasks-2+offset for one biased pixwidth\r
+; the low byte of bitmasks is for the first screen byte, the high byte\r
+; is the bitmask for the second screen byte (if non 0)\r
+;\r
+\r
+bitmasks dw 0080h,00c0h,00e0h,00f0h,00f8h,00fch,00feh,00ffh\r
+ dw 0040h,0060h,0070h,0078h,007ch,007eh,007fh,807fh\r
+ dw 0020h,0030h,0038h,003ch,003eh,003fh,803fh,0c03fh\r
+ dw 0010h,0018h,001ch,001eh,001fh,801fh,0c01fh,0e01fh\r
+ dw 0008h,000ch,000eh,000fh,800fh,0c00fh,0e00fh,0f00fh\r
+ dw 0004h,0006h,0007h,8007h,0c007h,0e007h,0f007h,0f807h\r
+ dw 0002h,0003h,8003h,0c003h,0e003h,0f003h,0f803h,0fc03h\r
+ dw 0001h,8001h,0c001h,0e001h,0f001h,0f801h,0fc01h,0fe01h\r
+\r
+\r
+;\r
+; wallscalecall is a far pointer to the start of a compiled scaler\r
+; The low word will never change, while the high word is set to\r
+; compscaledirectory[scale]\r
+;\r
+wallscalecall dd (65*6) ; offset of t_compscale->code[0]\r
+\r
+\r
+PUBLIC wallheight,wallwidth,wallseg,wallofs,screenbyte,screenbit\r
+PUBLIC bitmasks,wallscalecall\r
+\r
+\r
+EXTRN scaledirectory:WORD ; array of MAXSCALE segment pointers to\r
+ ; compiled scalers\r
+EXTRN screenseg:WORD ; basically just 0xa000\r
+EXTRN bufferofs:WORD ; offset of the current work screen\r
+EXTRN ylookup:WORD\r
+EXTRN screenpage:WORD\r
+\r
+CODESEG\r
+\r
+;============================================================================\r
+;\r
+; ScaleWalls\r
+;\r
+; AX AL is scratched in bit mask setting and scaling\r
+; BX table index\r
+; CX pixwidth*2\r
+; DX GC_INDEX\r
+; SI offset into wall data to scale from, allways 0,64,128,...4032\r
+; DI byte at top of screen that the collumn is contained in\r
+; BP x pixel * 2, index into VIEWWIDTH wide tables\r
+; DS segment of the wall data to texture map\r
+; ES screenseg\r
+; SS addressing DGROUP variables\r
+;\r
+;============================================================================\r
+\r
+PROC ScaleWalls\r
+PUBLIC ScaleWalls\r
+USES SI,DI,BP\r
+\r
+ xor bp,bp ; start at location 0 in the tables\r
+ mov dx,GC_INDEX+1\r
+ mov es,[screenseg]\r
+\r
+;\r
+; scale one collumn of data, possibly across two bytes\r
+;\r
+nextcollumn:\r
+\r
+ mov bx,[wallheight+bp] ; height of walls (1-MAXSCALE)\r
+ shl bx,1\r
+ mov ax,[ss:scaledirectory+bx] ; segment of the compiled scaler\r
+ mov [WORD PTR ss:wallscalecall+2],ax\r
+\r
+ mov cx,[wallwidth+bp]\r
+ or cx,cx\r
+ jnz okwidth\r
+ mov cx,2\r
+ jmp next\r
+\r
+okwidth:\r
+ shl cx,1\r
+ mov ds,[wallseg+bp]\r
+ mov si,[wallofs+bp]\r
+\r
+ mov di,[screenbyte+bp] ; byte at the top of the scaled collumn\r
+ add di,[ss:bufferofs] ; offset of current page flip\r
+ mov bx,[screenbit+bp] ; 0-7 << 4\r
+ add bx,cx\r
+ mov ax,[ss:bitmasks-2+bx]\r
+ out dx,al ; set bit mask register\r
+ call [DWORD PTR ss:wallscalecall] ; scale the line of pixels\r
+ or ah,ah ; is there anything in the second byte?\r
+ jnz secondbyte\r
+;\r
+; next\r
+;\r
+next:\r
+ add bp,cx\r
+ cmp bp,VIEWWIDTH*2\r
+ jb nextcollumn\r
+ jmp done\r
+\r
+;\r
+; draw a second byte for vertical strips that cross two bytes\r
+;\r
+secondbyte:\r
+ mov al,ah\r
+ inc di ; next byte over\r
+ out dx,al ; set bit mask register\r
+ call [DWORD PTR ss:wallscalecall] ; scale the line of pixels\r
+;\r
+; next\r
+;\r
+ add bp,cx\r
+ cmp bp,VIEWWIDTH*2\r
+ jb nextcollumn\r
+\r
+done:\r
+ mov ax,ss\r
+ mov ds,ax\r
+ ret\r
+\r
+ENDP\r
+\r
+;---------------------------------------------------------------------------\r
+;\r
+; RadarBlip()\r
+;\r
+; Displays a 'blip' (1 pixel wide X 2 pixel high) on the radar at\r
+; an (X,Y) relative to (RADAR_X,RADAR_Y) (defined below...)\r
+;\r
+;---------------------------------------------------------------------------\r
+\r
+PROC RadarBlip x:WORD, y:WORD, color:WORD\r
+USES SI,DI\r
+PUBLIC RadarBlip\r
+\r
+ mov ax,[screenseg]\r
+\r
+ mov es,ax\r
+ xor di,di\r
+\r
+ lea si,[ylookup]\r
+ add si,[y]\r
+ add si,[y]\r
+ add di,[si]\r
+\r
+ mov ax,[x]\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1\r
+ add di,ax\r
+\r
+ mov ax,[x]\r
+ and ax,7\r
+ mov cl,al\r
+ mov ah,080h\r
+ shr ah,cl\r
+ cli\r
+ mov al,GC_BITMASK\r
+ mov dx,GC_INDEX\r
+ out dx,ax\r
+ sti\r
+\r
+ mov ax,[color]\r
+ mov ah,[es:di] ; read into latches\r
+ mov [es:di],al ; write latches / color bit\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+END\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_DEBUG.C\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define DEBUG_OVERHEAD 0\r
+\r
+\r
+#define VIEWTILEX 20\r
+#define VIEWTILEY (VIEWHEIGHT/16)\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+short colordelay=0;\r
+boolean autofire=false;\r
+int maporgx;\r
+int maporgy;\r
+enum {mapview,tilemapview,actoratview,visview,mapseg2,lastview} viewtype;\r
+\r
+void ViewMap (void);\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+================\r
+=\r
+= PicturePause\r
+=\r
+================\r
+*/\r
+\r
+void PicturePause (void)\r
+{\r
+ int y;\r
+ unsigned source;\r
+\r
+ source = displayofs+panadjust;\r
+\r
+// VW_ColorBorder (15);\r
+ VW_SetLineWidth (40);\r
+ VW_SetScreen (0,0);\r
+\r
+ if (source<0x10000l-200*64)\r
+ {\r
+ //\r
+ // copy top line first\r
+ //\r
+ for (y=0;y<200;y++)\r
+ VW_ScreenToScreen (source+y*64,y*40,40,1);\r
+ }\r
+ else\r
+ {\r
+ //\r
+ // copy bottom line first\r
+ //\r
+ for (y=199;y>=0;y--)\r
+ VW_ScreenToScreen (source+y*64,y*40,40,1);\r
+ }\r
+\r
+ IN_Shutdown ();\r
+\r
+ VW_WaitVBL(70);\r
+ bioskey(0);\r
+ VW_WaitVBL(70);\r
+ Quit (NULL);\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+//===========================================================================\r
+\r
+#define sc_1 0x02\r
+#define sc_2 0x03\r
+#define sc_3 0x04\r
+#define sc_4 0x05\r
+#define sc_5 0x06\r
+#define sc_6 0x07\r
+#define sc_7 0x08\r
+#define sc_8 0x09\r
+#define sc_9 0x0a\r
+#define sc_0 0x0b\r
+\r
+\r
+\r
+/*\r
+================\r
+=\r
+= DebugKeys\r
+=\r
+================\r
+*/\r
+\r
+int DebugKeys (void)\r
+{\r
+ boolean esc;\r
+ int level,i;\r
+\r
+#if DEBUG_KEYS_AVAILABLE\r
+ if (Keyboard[sc_R])\r
+ {\r
+ CenterWindow (12,2);\r
+ if (autofire)\r
+ US_PrintCentered ("Rapid-Fire OFF");\r
+ else\r
+ US_PrintCentered ("Rapid-Fire ON");\r
+ VW_UpdateScreen();\r
+ IN_Ack();\r
+ autofire ^= 1;\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+#if DEBUG_KEYS_AVAILABLE\r
+ if (Keyboard[sc_A])\r
+ {\r
+ char levelstr[50];\r
+ unsigned org_tile,org_mapon,msgnum;\r
+ boolean newmsg=true,newlevel=false;\r
+\r
+ VW_FixRefreshBuffer ();\r
+ CenterWindow (16,3);\r
+ US_Print("\n");\r
+ US_CPrint("Message Test");\r
+ VW_UpdateScreen();\r
+\r
+ org_mapon = mapon;\r
+ msgnum = (org_tile = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex))-NAMESTART;\r
+ while (1)\r
+ {\r
+ // Get outta' here\r
+ //\r
+ if (Keyboard[sc_Escape])\r
+ {\r
+ while (Keyboard[sc_Escape]);\r
+ break;\r
+ }\r
+\r
+ // Move to previous message\r
+ //\r
+ if (Keyboard[sc_UpArrow])\r
+ {\r
+ if (msgnum)\r
+ {\r
+ msgnum--;\r
+ newmsg = true;\r
+ }\r
+ }\r
+\r
+ // Move to next message\r
+ //\r
+ if (Keyboard[sc_DownArrow])\r
+ {\r
+ if (msgnum < 24)\r
+ {\r
+ msgnum++;\r
+ newmsg = true;\r
+ }\r
+ }\r
+\r
+ // Move to previous level\r
+ //\r
+ if (Keyboard[sc_LeftArrow])\r
+ {\r
+ if (mapon)\r
+ {\r
+ MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);\r
+ mapon--;\r
+ newlevel = true;\r
+ }\r
+ }\r
+\r
+ // Move to next level\r
+ //\r
+ if (Keyboard[sc_RightArrow])\r
+ {\r
+ if (mapon < LASTMAP-1)\r
+ {\r
+ MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);\r
+ mapon++;\r
+ newlevel = true;\r
+ }\r
+ }\r
+\r
+ // Load new level text\r
+ //\r
+ if (newlevel)\r
+ {\r
+ CA_CacheGrChunk(LEVEL1TEXT+mapon);\r
+ ScanText();\r
+ newmsg = true;\r
+ newlevel=false;\r
+ }\r
+\r
+ // Display new message text\r
+ //\r
+ if (newmsg)\r
+ {\r
+ *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = msgnum+NAMESTART;\r
+ DrawText(true);\r
+ strcpy(levelstr,"Level: ");\r
+ itoa(mapon,levelstr+strlen(levelstr),10);\r
+ strcat(levelstr," Msg: ");\r
+ itoa(msgnum,levelstr+strlen(levelstr),10);\r
+ DisplaySMsg(levelstr,NULL);\r
+ newmsg = false;\r
+\r
+ if (Keyboard[sc_UpArrow] || Keyboard[sc_DownArrow] || Keyboard[sc_LeftArrow] || Keyboard[sc_RightArrow])\r
+ VW_WaitVBL(6);\r
+ }\r
+\r
+ }\r
+// Restore game\r
+//\r
+ MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);\r
+ mapon = org_mapon;\r
+ CA_CacheGrChunk(LEVEL1TEXT+mapon);\r
+ ScanText();\r
+ *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = org_tile;\r
+ DrawText(true);\r
+ status_flag = 0;\r
+ }\r
+\r
+\r
+ if (Keyboard[sc_V])\r
+ {\r
+ displayofs = bufferofs = screenloc[screenpage];\r
+ CenterWindow (16,4);\r
+ US_CPrint("\n"GAMENAME);\r
+ US_CPrint(VERSION);\r
+ US_CPrint(REVISION);\r
+ VW_UpdateScreen();\r
+ IN_Ack ();\r
+ }\r
+\r
+\r
+ if (Keyboard[sc_Q]) // Q = Insta-Quit!\r
+ Quit("Insta-Quit!");\r
+\r
+ if (Keyboard[sc_Z]) // Z = freeze Time\r
+ {\r
+ if (FreezeTime)\r
+ FreezeTime = 1; // Allow refresh to dec to zero..\r
+ else\r
+ StopTime();\r
+\r
+ IN_Ack();\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+\r
+// if (Keyboard[sc_E])\r
+// FaceDoor((player->x>>16l)+1,(player->y>>16l));\r
+// FaceAngle(90);\r
+\r
+#if 0\r
+ if (Keyboard[sc_B]) // B = border color\r
+ {\r
+ CenterWindow(24,3);\r
+ PrintY+=6;\r
+ US_Print(" Border color (0-15):");\r
+ VW_UpdateScreen();\r
+ esc = !US_LineInput (px,py,str,NULL,true,2,0);\r
+ if (!esc)\r
+ {\r
+ level = atoi (str);\r
+ if (level>=0 && level<=15)\r
+ VW_ColorBorder (level);\r
+ }\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+\r
+#if 1//DEBUG_KEYS_AVAILABLE\r
+ if (Keyboard[sc_O])\r
+ {\r
+ extern unsigned objectcount,latchmemavail;\r
+ unsigned unused,total;\r
+\r
+ CenterWindow (30,13);\r
+ US_Print ("Objects: ");\r
+ US_PrintUnsigned (objectcount);\r
+\r
+ US_Print("\n\nTics: ");\r
+ US_PrintUnsigned (tics);\r
+ US_Print(" Real Tics: ");\r
+ US_PrintUnsigned(realtics);\r
+\r
+ US_Print ("\n\n Total Available: ");\r
+ US_PrintUnsigned (mminfo.mainmem/1024);\r
+ US_Print ("k\n Mem In Use: ");\r
+ unused=MM_UnusedMemory()/1024;\r
+ US_PrintUnsigned (unused);\r
+ US_Print ("k\n Mem After Purge: ");\r
+ total=MM_TotalFree()/1024;\r
+ US_PrintUnsigned (total);\r
+ US_Print ("k (");\r
+ US_PrintUnsigned (total-unused);\r
+\r
+ US_Print (")\n\nLatch Mem Free: ");\r
+ US_PrintUnsigned (latchmemavail);\r
+ US_Print ("\n");\r
+ VW_UpdateScreen();\r
+ IN_Ack();\r
+ }\r
+\r
+ if (colordelay<1)\r
+ {\r
+ if (Keyboard[26])\r
+ {\r
+ extern unsigned *groundcolor,debug_gnd;\r
+\r
+ groundcolor = &debug_gnd;\r
+ debug_gnd += 0x0101;\r
+ if (debug_gnd == 0x1010)\r
+ debug_gnd = 0;\r
+ colordelay = 10;\r
+ }\r
+\r
+ if (Keyboard[27])\r
+ {\r
+ extern unsigned *skycolor,debug_sky;\r
+\r
+ skycolor = &debug_sky;\r
+ debug_sky += 0x0101;\r
+ if (debug_sky == 0x1010)\r
+ debug_sky = 0;\r
+ colordelay = 10;\r
+ }\r
+ }\r
+ else\r
+ colordelay -= realtics;\r
+#endif\r
+\r
+\r
+#if 0\r
+ if (Keyboard[sc_C]) // C = count objects\r
+ {\r
+ CountObjects();\r
+ return 1;\r
+ }\r
+\r
+\r
+ if (Keyboard[sc_D]) // D = start / end demo record\r
+ {\r
+ if (DemoMode == demo_Off)\r
+ StartDemoRecord ();\r
+ else if (DemoMode == demo_Record)\r
+ {\r
+ EndDemoRecord ();\r
+ playstate = ex_completed;\r
+ }\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+#if 0\r
+ if (Keyboard[sc_E]) // E = quit level\r
+ {\r
+ if (tedlevel)\r
+ TEDDeath();\r
+ playstate = ex_warped;\r
+ gamestate.mapon++;\r
+ }\r
+#endif\r
+\r
+#if 0\r
+ if (Keyboard[sc_F]) // F = facing spot\r
+ {\r
+ CenterWindow (12,4);\r
+ US_Print ("X:");\r
+ US_PrintUnsigned (player->x);\r
+ US_Print ("Y:");\r
+ US_PrintUnsigned (player->y);\r
+ US_Print ("A:");\r
+ US_PrintUnsigned (player->angle);\r
+ VW_UpdateScreen();\r
+ IN_Ack();\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+ if (Keyboard[sc_G]) // G = god mode\r
+ {\r
+ CenterWindow (12,2);\r
+ if (godmode)\r
+ US_PrintCentered ("God mode OFF");\r
+ else\r
+ US_PrintCentered ("God mode ON");\r
+ VW_UpdateScreen();\r
+ IN_Ack();\r
+ godmode ^= 1;\r
+ return 1;\r
+ }\r
+\r
+#if 0\r
+ if (Keyboard[sc_H]) // H = hurt self\r
+ {\r
+ TakeDamage (5);\r
+ }\r
+#endif\r
+\r
+ if (Keyboard[sc_I]) // I = item cheat\r
+ {\r
+ extern boolean redraw_gems;\r
+\r
+ CenterWindow (12,3);\r
+ US_PrintCentered ("Free items!");\r
+ VW_UpdateScreen();\r
+ for (i=0;i<4;i++)\r
+ {\r
+ GiveBolt ();\r
+ GiveNuke ();\r
+ GivePotion ();\r
+// if (!gamestate.keys[i])\r
+ GiveKey (i);\r
+ gamestate.gems[i] = GEM_DELAY_TIME;\r
+ }\r
+ gamestate.gems[4] = GEM_DELAY_TIME;\r
+ redraw_gems = true;\r
+///////// for (i=0;i<8;i++)\r
+///////// GiveScroll (i,false);\r
+\r
+ IN_Ack ();\r
+ return 1;\r
+ }\r
+\r
+#if DEBUG_OVERHEAD\r
+ if (Keyboard[sc_Z]) // O is used elsewhere...\r
+ {\r
+ ViewMap();\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+#if 0\r
+ if (Keyboard[sc_P]) // P = pause with no screen disruptioon\r
+ {\r
+ PicturePause ();\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+#if 0\r
+ if (Keyboard[sc_S]) // S = slow motion\r
+ {\r
+ singlestep^=1;\r
+ CenterWindow (18,3);\r
+ if (singlestep)\r
+ US_PrintCentered ("Slow motion ON");\r
+ else\r
+ US_PrintCentered ("Slow motion OFF");\r
+ VW_UpdateScreen();\r
+ IN_Ack ();\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+#if 0\r
+ if (Keyboard[sc_V]) // V = extra VBLs\r
+ {\r
+ CenterWindow(30,3);\r
+ PrintY+=6;\r
+ US_Print(" Add how many extra VBLs(0-8):");\r
+ VW_UpdateScreen();\r
+ esc = !US_LineInput (px,py,str,NULL,true,2,0);\r
+ if (!esc)\r
+ {\r
+ level = atoi (str);\r
+ if (level>=0 && level<=8)\r
+ extravbls = level;\r
+ }\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+ if (Keyboard[sc_W]) // W = warp to level\r
+ {\r
+ CenterWindow(26,3);\r
+ PrintY+=6;\r
+ US_Print(" Warp to which level(0-16):");\r
+ VW_UpdateScreen();\r
+ esc = !US_LineInput (px,py,str,NULL,true,2,0);\r
+ if (!esc)\r
+ {\r
+ level = atoi (str);\r
+ if (level>=0 && level<=LASTMAP-1)\r
+ {\r
+ gamestate.mapon = level;\r
+ playstate = ex_warped;\r
+ lasttext = -1;\r
+ }\r
+ }\r
+ return 1;\r
+ }\r
+\r
+#if 0\r
+ if (Keyboard[sc_X]) // X = item cheat\r
+ {\r
+ CenterWindow (12,3);\r
+ US_PrintCentered ("Extra stuff!");\r
+ VW_UpdateScreen();\r
+ for (i=0;i<4;i++)\r
+ {\r
+ GiveBolt ();\r
+ GiveNuke ();\r
+ GivePotion ();\r
+ }\r
+ IN_Ack ();\r
+ return 1;\r
+ }\r
+#endif\r
+\r
+//////// if (LastScan >= sc_1 && LastScan <= sc_8) // free scrolls\r
+//////// {\r
+//////// GiveScroll (LastScan-sc_1,false);\r
+//////// IN_ClearKeysDown ();\r
+//////// }\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+#if DEBUG_OVERHEAD\r
+\r
+/*\r
+=====================\r
+=\r
+= LatchDrawChar\r
+=\r
+=====================\r
+*/\r
+\r
+void LatchDrawChar (unsigned x, unsigned y, unsigned picnum)\r
+{\r
+ unsigned source, dest;\r
+\r
+ dest = bufferofs + ylookup[y]+x;\r
+ source = latchpics[0]+picnum*8;\r
+\r
+ EGAWRITEMODE(1);\r
+ EGAMAPMASK(15);\r
+\r
+asm mov bx,[linewidth]\r
+asm dec bx\r
+\r
+asm mov ax,[screenseg]\r
+asm mov es,ax\r
+asm mov ds,ax\r
+\r
+asm mov si,[source]\r
+asm mov di,[dest]\r
+\r
+asm movsb\r
+asm add di,bx\r
+asm movsb\r
+asm add di,bx\r
+asm movsb\r
+asm add di,bx\r
+asm movsb\r
+asm add di,bx\r
+asm movsb\r
+asm add di,bx\r
+asm movsb\r
+asm add di,bx\r
+asm movsb\r
+asm add di,bx\r
+asm movsb\r
+\r
+asm mov ax,ss\r
+asm mov ds,ax // restore turbo's data segment\r
+\r
+ EGAWRITEMODE(0);\r
+}\r
+\r
+#endif\r
+\r
+\r
+#if DEBUG_OVERHEAD\r
+/*\r
+=====================\r
+=\r
+= LatchDrawTile\r
+=\r
+=====================\r
+*/\r
+\r
+void LatchDrawTile (unsigned x, unsigned y, unsigned picnum)\r
+{\r
+ unsigned source, dest;\r
+\r
+ dest = bufferofs + ylookup[y]+x;\r
+ source = tileoffsets[picnum];\r
+\r
+ EGAWRITEMODE(1);\r
+ EGAMAPMASK(15);\r
+\r
+asm mov bx,[linewidth]\r
+asm sub bx,2\r
+\r
+asm mov ax,[screenseg]\r
+asm mov es,ax\r
+asm mov ds,ax\r
+\r
+asm mov si,[source]\r
+asm mov di,[dest]\r
+asm mov dx,16\r
+\r
+lineloop:\r
+asm movsb\r
+asm movsb\r
+asm add di,bx\r
+\r
+asm dec dx\r
+asm jnz lineloop\r
+\r
+asm mov ax,ss\r
+asm mov ds,ax // restore turbo's data segment\r
+\r
+ EGAWRITEMODE(0);\r
+}\r
+#endif\r
+\r
+\r
+#if DEBUG_OVERHEAD\r
+/*\r
+===================\r
+=\r
+= OverheadRefresh\r
+=\r
+===================\r
+*/\r
+\r
+void OverheadRefresh (void)\r
+{\r
+ unsigned x,y,endx,endy,sx,sy;\r
+ unsigned tile;\r
+\r
+\r
+ if (++screenpage == 3)\r
+ screenpage = 0;\r
+\r
+ bufferofs = screenloc[screenpage];\r
+\r
+ endx = maporgx+VIEWTILEX;\r
+ endy = maporgy+VIEWTILEY;\r
+\r
+ for (y=maporgy;y<endy;y++)\r
+ for (x=maporgx;x<endx;x++)\r
+ {\r
+ sx = (x-maporgx)*2;\r
+ sy = (y-maporgy)*16;\r
+\r
+ switch (viewtype)\r
+ {\r
+ case mapview:\r
+ tile = *(mapsegs[0]+farmapylookup[y]+x);\r
+ break;\r
+\r
+ case tilemapview:\r
+ tile = tilemap[x][y];\r
+ break;\r
+\r
+ case actoratview:\r
+ tile = (unsigned)actorat[x][y];\r
+ break;\r
+\r
+ case visview:\r
+ tile = spotvis[x][y];\r
+ break;\r
+\r
+ case mapseg2:\r
+ tile = *(mapsegs[2]+farmapylookup[y]+x);\r
+ if (tile < 256)\r
+ tile = *(mapsegs[0]+farmapylookup[y]+x);\r
+ break;\r
+\r
+ }\r
+\r
+ if (tile<NUMTILE16)\r
+ LatchDrawTile(sx,sy,tile);\r
+ else\r
+ {\r
+ LatchDrawChar(sx,sy,NUMBERCHARS+((tile&0xf000)>>12));\r
+ LatchDrawChar(sx+1,sy,NUMBERCHARS+((tile&0x0f00)>>8));\r
+ LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4));\r
+ LatchDrawChar(sx+1,sy+8,NUMBERCHARS+(tile&0x000f));\r
+ }\r
+ }\r
+\r
+ VW_SetScreen (bufferofs,0);\r
+ displayofs = bufferofs;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ViewMap\r
+=\r
+===================\r
+*/\r
+\r
+void ViewMap (void)\r
+{\r
+ boolean button0held;\r
+\r
+ viewtype = actoratview;\r
+ button0held = false;\r
+\r
+\r
+ maporgx = player->tilex - VIEWTILEX/2;\r
+ if (maporgx<0)\r
+ maporgx = 0;\r
+ maporgy = player->tiley - VIEWTILEY/2;\r
+ if (maporgy<0)\r
+ maporgy = 0;\r
+\r
+ do\r
+ {\r
+//\r
+// let user pan around\r
+//\r
+ IN_ReadControl(0,&control);\r
+ if (control.xaxis == -1 && maporgx>0)\r
+ maporgx--;\r
+ if (control.xaxis == 1 && maporgx<mapwidth-VIEWTILEX)\r
+ maporgx++;\r
+ if (control.yaxis == -1 && maporgy>0)\r
+ maporgy--;\r
+ if (control.yaxis == 1 && maporgy<mapheight-VIEWTILEY)\r
+ maporgy++;\r
+\r
+ if (control.button0 && !button0held)\r
+ {\r
+ button0held = true;\r
+ viewtype++;\r
+ if (viewtype==lastview)\r
+ viewtype = mapview;\r
+ }\r
+ if (!control.button0)\r
+ button0held = false;\r
+\r
+\r
+ OverheadRefresh ();\r
+\r
+ } while (!Keyboard[sc_Escape]);\r
+\r
+ IN_ClearKeysDown ();\r
+ DrawPlayScreen ();\r
+}\r
+#endif\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_DRAW.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+//#define DRAWEACH // draw walls one at a time for debugging\r
+\r
+unsigned highest;\r
+unsigned mostwalls,numwalls;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define PI 3.141592657\r
+#define ANGLEQUAD (ANGLES/4)\r
+\r
+unsigned oldend;\r
+\r
+#define FINEANGLES 3600\r
+\r
+#define MINRATIO 16\r
+\r
+\r
+const unsigned MAXSCALEHEIGHT = (VIEWWIDTH/2);\r
+const unsigned MAXVISHEIGHT = (VIEWHEIGHT/2);\r
+const unsigned BASESCALE = 32;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+//\r
+// calculate location of screens in video memory so they have the\r
+// maximum possible distance seperating them (for scaling overflow)\r
+//\r
+\r
+unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START};\r
+unsigned freelatch = FREESTART;\r
+\r
+boolean fizzlein;\r
+\r
+long scaleshapecalll;\r
+long scaletablecall;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+long bytecount,endcount; // for profiling\r
+int animframe;\r
+int pixelangle[VIEWWIDTH];\r
+int far finetangent[FINEANGLES+1];\r
+int fineviewangle;\r
+unsigned viewxpix,viewypix;\r
+\r
+/*\r
+============================================================================\r
+\r
+ 3 - D DEFINITIONS\r
+\r
+============================================================================\r
+*/\r
+\r
+fixed tileglobal = TILEGLOBAL;\r
+fixed focallength = FOCALLENGTH;\r
+fixed mindist = MINDIST;\r
+int viewheight = VIEWHEIGHT;\r
+fixed scale;\r
+\r
+\r
+tilept tile,lasttile, // tile of wall being followed\r
+ focal, // focal point in tiles\r
+ left,mid,right; // rightmost tile in view\r
+\r
+globpt edge,view;\r
+\r
+int segstart[VIEWHEIGHT], // addline tracks line segment and draws\r
+ segend[VIEWHEIGHT],\r
+ segcolor[VIEWHEIGHT]; // only when the color changes\r
+\r
+\r
+walltype walls[MAXWALLS],*leftwall,*rightwall;\r
+\r
+\r
+//==========================================================================\r
+\r
+//\r
+// refresh stuff\r
+//\r
+\r
+int screenpage;\r
+\r
+long lasttimecount;\r
+\r
+//\r
+// rendering stuff\r
+//\r
+\r
+int firstangle,lastangle;\r
+\r
+fixed prestep;\r
+\r
+fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4);\r
+\r
+fixed viewx,viewy; // the focal point\r
+int viewangle;\r
+fixed viewsin,viewcos;\r
+\r
+int zbuffer[VIEWXH+1]; // holds the height of the wall at that point\r
+\r
+//==========================================================================\r
+\r
+void DrawLine (int xl, int xh, int y,int color);\r
+void DrawWall (walltype *wallptr);\r
+void TraceRay (unsigned angle);\r
+fixed FixedByFrac (fixed a, fixed b);\r
+fixed FixedAdd (void);\r
+fixed TransformX (fixed gx, fixed gy);\r
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);\r
+int BackTrace (int finish);\r
+void ForwardTrace (void);\r
+int TurnClockwise (void);\r
+int TurnCounterClockwise (void);\r
+void FollowWall (void);\r
+\r
+void NewScene (void);\r
+void BuildTables (void);\r
+\r
+//==========================================================================\r
+\r
+\r
+#if 0\r
+/*\r
+==================\r
+=\r
+= DrawLine\r
+=\r
+= Must be in write mode 2 with all planes enabled\r
+= The bit mask is left set to the end value, so clear it after all lines are\r
+= drawn\r
+=\r
+= draws a black dot at the left edge of the line\r
+=\r
+==================\r
+*/\r
+\r
+unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1};\r
+unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};\r
+unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};\r
+\r
+void DrawLine (int xl, int xh, int y,int color)\r
+{\r
+ unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;\r
+\r
+ xlb=xl/8;\r
+ xhb=xh/8;\r
+\r
+ if (xh<xl)\r
+ Quit("DrawLine: xh<xl");\r
+ if (y<VIEWY)\r
+ Quit("DrawLine: y<VIEWY");\r
+ if (y>VIEWYH)\r
+ Quit("DrawLine: y>VIEWYH");\r
+\r
+ xlp = xl&7;\r
+ maskleft = leftmask[xlp];\r
+ maskright = rightmask[xh&7];\r
+\r
+ mid = xhb-xlb-1;\r
+ dest = bufferofs+ylookup[y]+xlb;\r
+\r
+ //\r
+ // set the GC index register to point to the bit mask register\r
+ //\r
+ asm mov al,GC_BITMASK\r
+ asm mov dx,GC_INDEX\r
+ asm out dx,al\r
+\r
+ if (xlb==xhb)\r
+ {\r
+ //\r
+ // entire line is in one byte\r
+ //\r
+\r
+ maskleft&=maskright;\r
+\r
+ asm mov es,[screenseg]\r
+ asm mov di,[dest]\r
+ asm mov dx,GC_INDEX+1\r
+\r
+ asm mov al,[BYTE PTR maskleft]\r
+ asm out dx,al // mask off pixels\r
+\r
+ asm mov al,[BYTE PTR color]\r
+ asm xchg al,[es:di] // load latches and write pixels\r
+\r
+ return;\r
+ }\r
+\r
+asm mov es,[screenseg]\r
+asm mov di,[dest]\r
+asm mov dx,GC_INDEX+1\r
+asm mov bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm mov al,[BYTE PTR maskleft]\r
+asm out dx,al // mask off pixels\r
+\r
+asm mov al,bh\r
+asm xchg al,[es:di] // load latches and write pixels\r
+asm inc di\r
+\r
+//\r
+// draw middle\r
+//\r
+asm mov al,255\r
+asm out dx,al // no masking\r
+\r
+asm mov al,bh\r
+asm mov cx,[mid]\r
+asm rep stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm mov al,[BYTE PTR maskright]\r
+asm out dx,al // mask off pixels\r
+asm xchg bh,[es:di] // load latches and write pixels\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+void DrawLineDot (int xl, int xh, int y,int color)\r
+{\r
+ unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;\r
+\r
+ xlb=xl/8;\r
+ xhb=xh/8;\r
+\r
+ if (xh<xl)\r
+ Quit("DrawLine: xh<xl");\r
+ if (y<VIEWY)\r
+ Quit("DrawLine: y<VIEWY");\r
+ if (y>VIEWYH)\r
+ Quit("DrawLine: y>VIEWYH");\r
+\r
+ xlp = xl&7;\r
+ maskdot = dotmask[xlp];\r
+ maskleft = leftmask[xlp];\r
+ maskright = rightmask[xh&7];\r
+\r
+ mid = xhb-xlb-1;\r
+ dest = bufferofs+ylookup[y]+xlb;\r
+\r
+ //\r
+ // set the GC index register to point to the bit mask register\r
+ //\r
+ asm mov al,GC_BITMASK\r
+ asm mov dx,GC_INDEX\r
+ asm out dx,al\r
+\r
+ if (xlb==xhb)\r
+ {\r
+ //\r
+ // entire line is in one byte\r
+ //\r
+\r
+ maskleft&=maskright;\r
+\r
+ asm mov es,[screenseg]\r
+ asm mov di,[dest]\r
+ asm mov dx,GC_INDEX+1\r
+\r
+ asm mov al,[BYTE PTR maskleft]\r
+ asm out dx,al // mask off pixels\r
+\r
+ asm mov al,[BYTE PTR color]\r
+ asm xchg al,[es:di] // load latches and write pixels\r
+\r
+\r
+ //\r
+ // write the black dot at the start\r
+ //\r
+ asm mov al,[BYTE PTR maskdot]\r
+ asm out dx,al // mask off pixels\r
+\r
+ asm xor al,al\r
+ asm xchg al,[es:di] // load latches and write pixels\r
+\r
+\r
+ return;\r
+ }\r
+\r
+asm mov es,[screenseg]\r
+asm mov di,[dest]\r
+asm mov dx,GC_INDEX+1\r
+asm mov bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm mov al,[BYTE PTR maskleft]\r
+asm out dx,al // mask off pixels\r
+\r
+asm mov al,bh\r
+asm xchg al,[es:di] // load latches and write pixels\r
+\r
+//\r
+// write the black dot at the start\r
+//\r
+asm mov al,[BYTE PTR maskdot]\r
+asm out dx,al // mask off pixels\r
+asm xor al,al\r
+asm xchg al,[es:di] // load latches and write pixels\r
+asm inc di\r
+\r
+//\r
+// draw middle\r
+//\r
+asm mov al,255\r
+asm out dx,al // no masking\r
+\r
+asm mov al,bh\r
+asm mov cx,[mid]\r
+asm rep stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm mov al,[BYTE PTR maskright]\r
+asm out dx,al // mask off pixels\r
+asm xchg bh,[es:di] // load latches and write pixels\r
+\r
+}\r
+\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+long wallscalesource;\r
+\r
+#ifdef DRAWEACH\r
+/*\r
+====================\r
+=\r
+= ScaleOneWall\r
+=\r
+====================\r
+*/\r
+\r
+void near ScaleOneWall (int xl, int xh)\r
+{\r
+ int x,pixwidth,height;\r
+\r
+ *(((unsigned *)&wallscalesource)+1) = wallseg[xl];\r
+\r
+ for (x=xl;x<=xh;x+=pixwidth)\r
+ {\r
+ height = wallheight[x];\r
+ pixwidth = wallwidth[x];\r
+ (unsigned)wallscalesource = wallofs[x];\r
+\r
+ *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height];\r
+ (unsigned)scaletablecall = scaledirectory[height]->codeofs[0];\r
+\r
+ //\r
+ // scale a byte wide strip of wall\r
+ //\r
+ asm mov bx,[x]\r
+ asm mov di,bx\r
+ asm shr di,1\r
+ asm shr di,1\r
+ asm shr di,1 // X in bytes\r
+ asm add di,[bufferofs]\r
+ asm and bx,7\r
+ asm shl bx,1\r
+ asm shl bx,1\r
+ asm shl bx,1\r
+ asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1\r
+ asm dec bx\r
+ asm mov al,BYTE PTR [bitmasks1+bx]\r
+ asm mov dx,GC_INDEX+1\r
+ asm out dx,al // set bit mask register\r
+ asm mov es,[screenseg]\r
+ asm lds si,[wallscalesource]\r
+ asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels\r
+\r
+ asm mov al,BYTE PTR [ss:bitmasks2+bx]\r
+ asm or al,al\r
+ asm jz nosecond\r
+\r
+ //\r
+ // draw a second byte for vertical strips that cross two bytes\r
+ //\r
+ asm inc di\r
+ asm out dx,al // set bit mask register\r
+ asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels\r
+ nosecond:\r
+ asm mov ax,ss\r
+ asm mov ds,ax\r
+ }\r
+}\r
+\r
+#endif\r
+\r
+char wall_anim_pos[NUMFLOORS];\r
+\r
+// EAST / WEST WALLS\r
+//\r
+int far walllight1[NUMFLOORS] = {0,\r
+\r
+ CRYSTAL_LIGHT_1PIC,\r
+ CRYSTAL_LIGHT_2PIC,\r
+ CRYSTAL_LIGHT_3PIC,\r
+ CRYSTAL_LIGHT_4PIC, //4\r
+\r
+ FIRE_WALL_1PIC,\r
+ FIRE_WALL_2PIC,\r
+ FIRE_WALL_3PIC,\r
+ FIRE_WALL_4PIC, //8\r
+\r
+ BRN_STONE_GATEPIC,\r
+ BRN_STONE_WALL_1PIC,\r
+ KUDZU_WEAK_LIGHTPIC,\r
+ KUDZU_LIGHT_WALLPIC,\r
+ HEDGE_WALLPIC,\r
+ HEDGE_EYESPIC, //14\r
+\r
+ W_GEN_DOOR1PIC, //15\r
+ BRN_WINDOW_LIGHTPIC,\r
+\r
+ ALTAR_LEFTPIC,\r
+ ALTAR_RIGHTPIC,\r
+ GRAY_LIGHT_WALLPIC,\r
+ GRAY_LIGHT_SIGNPIC, //20\r
+\r
+ MANICLE_LIGHT_WALLPIC,\r
+ MANICLE_LIGHT_BLOODYPIC,\r
+\r
+ LIGHT_CURTAIN_WINDOWPIC,\r
+ LIGHT_CURTAIN_WALLPIC,\r
+ BRN_LIGHT_SIGNPIC, //25\r
+\r
+ LIGHT_STONE_WALLPIC,\r
+\r
+ W_GEN_DOOR2PIC, //27\r
+\r
+ TROLL_LIGHT_STONEPIC,\r
+\r
+ BRN_FLAGSTONE_LIGHT_2PIC,\r
+\r
+ W_CRYSTAL_DOORPIC,\r
+\r
+ DMG_BRN_FSTN_LTPIC,\r
+\r
+ RUST_METAL_LIGHTPIC,\r
+ GRAY_METAL_LIGHTPIC, //33\r
+\r
+ WEAK_STONE_LIGHTPIC,\r
+\r
+ DMG_FIN_FSTN_LTPIC,\r
+\r
+ WEAK_GRAY_RFGSTN_LIGHTPIC,\r
+ 0,\r
+\r
+ WEAK_CRYSTAL_LIGHTPIC,\r
+\r
+ RED_MUD_LIGHTPIC,\r
+\r
+ STEEL_DOOR1PIC, //40\r
+\r
+ RED_MUD_WEAK_LIGHTPIC,\r
+\r
+ STEEL_DOOR2PIC, //42\r
+\r
+ HORN_DOORPIC,\r
+ TROLL_BLOODY_LT_STONEPIC,\r
+ CLOSED_DOOR_1PIC,\r
+\r
+ GRY_DOOR_LTPIC, //46\r
+\r
+ BRN_DOOR_LTPIC, //47\r
+\r
+ GRY_FGSTN_LTPIC, //48\r
+ DOOR_2PIC,\r
+\r
+ WATER_LIGHT_WEAK_1PIC,\r
+ WATER_LIGHT_WEAK_2PIC,\r
+ WATER_LIGHT_WEAK_3PIC, //52\r
+\r
+ WATER_LIGHT_1PIC,\r
+ WATER_LIGHT_2PIC,\r
+ WATER_LIGHT_3PIC,\r
+\r
+ LIGHT_BREATH_1PIC,\r
+ LIGHT_BREATH_2PIC,\r
+ LIGHT_BREATH_3PIC, //58\r
+\r
+ EXP_WALL_1PIC,\r
+ EXP_WALL_2PIC,\r
+ EXP_WALL_3PIC,\r
+\r
+ WATER_EXP_WALL_1PIC,\r
+ WATER_EXP_WALL_2PIC,\r
+ WATER_EXP_WALL_3PIC, //64\r
+\r
+ FINALWALLPIC,\r
+\r
+ LT_SKEL1PIC,\r
+ DK_SKEL1PIC,\r
+ LT_SKEL2PIC,\r
+ DK_SKEL2PIC,\r
+\r
+ 0,\r
+\r
+ TAP_1PIC,\r
+ TAP_2PIC,\r
+ TAP_3PIC,\r
+ TAP_4PIC,\r
+ TAP_5PIC,\r
+\r
+ WATER_DOOR1_PIC,\r
+ WATER_DOOR2_PIC,\r
+ };\r
+\r
+// NORTH / SOUTH WALLS\r
+//\r
+int far walldark1[NUMFLOORS] = {0,\r
+\r
+ CRYSTAL_DARK_1PIC,\r
+ CRYSTAL_DARK_2PIC,\r
+ CRYSTAL_DARK_3PIC,\r
+ CRYSTAL_DARK_4PIC, //4\r
+\r
+ FIRE_WALL_1PIC,\r
+ FIRE_WALL_2PIC,\r
+ FIRE_WALL_3PIC,\r
+ FIRE_WALL_4PIC, //8\r
+\r
+ BRN_STONE_GATEPIC,\r
+ BRN_STONE_WALL_2PIC,\r
+ KUDZU_WEAK_DARKPIC,\r
+ KUDZU_DARK_WALLPIC,\r
+ HEDGE_WALLPIC,\r
+ HEDGE_EYESPIC, //14\r
+\r
+ W_GEN_DOOR1PIC, //15\r
+ BRN_WINDOW_DARKPIC,\r
+\r
+ ALTAR_LEFTPIC,\r
+ ALTAR_RIGHTPIC,\r
+ GRAY_DARK_WALLPIC,\r
+ GRAY_DARK_SIGNPIC, //20\r
+\r
+ MANICLE_DARK_WALLPIC,\r
+ MANICLE_DARK_BLOODYPIC,\r
+\r
+ DARK_CURTAIN_WINDOWPIC,\r
+ DARK_CURTAIN_WALLPIC,\r
+ BRN_DARK_SIGNPIC,\r
+\r
+ DARK_STONE_WALLPIC,\r
+\r
+ W_GEN_DOOR2PIC, //27\r
+\r
+ TROLL_DARK_STONEPIC,\r
+\r
+ BRN_FLAGSTONE_DARK_2PIC,\r
+\r
+ W_CRYSTAL_DOORPIC, //30\r
+\r
+ DMG_BRN_FSTN_DKPIC,\r
+\r
+ RUST_METAL_DARKPIC,\r
+ GRAY_METAL_DARKPIC,\r
+\r
+ WEAK_STONE_DARKPIC,\r
+\r
+ DMG_FIN_FSTN_DKPIC, //35\r
+\r
+ WEAK_GRAY_RFGSTN_DARKPIC,\r
+ 0,\r
+\r
+ WEAK_CRYSTAL_DARKPIC,\r
+\r
+ BRN_MUD_DARKPIC,\r
+\r
+ STEEL_DOOR1PIC, //40\r
+\r
+ BRN_MUD_WEAK_DARKPIC,\r
+\r
+ STEEL_DOOR2PIC,\r
+\r
+ HORN_DOORPIC,\r
+ TROLL_BLOODY_DK_STONEPIC,\r
+\r
+ CLOSED_DOOR_1PIC,\r
+\r
+ GRY_DOOR_DKPIC, //46\r
+ BRN_DOOR_DKPIC, //47\r
+ GRY_FGSTN_DKPIC, //48\r
+ DOOR_2PIC,\r
+\r
+ WATER_DARK_WEAK_1PIC,\r
+ WATER_DARK_WEAK_2PIC,\r
+ WATER_DARK_WEAK_3PIC,\r
+\r
+ WATER_DARK_1PIC,\r
+ WATER_DARK_2PIC,\r
+ WATER_DARK_3PIC,\r
+\r
+ DARK_BREATH_1PIC,\r
+ DARK_BREATH_2PIC,\r
+ DARK_BREATH_3PIC,\r
+\r
+ EXP_WALL_1PIC,\r
+ EXP_WALL_2PIC,\r
+ EXP_WALL_3PIC,\r
+\r
+ WATER_EXP_WALL_1PIC,\r
+ WATER_EXP_WALL_2PIC,\r
+ WATER_EXP_WALL_3PIC,\r
+\r
+ FINALWALLPIC,\r
+\r
+ LT_SKEL1PIC,\r
+ DK_SKEL1PIC,\r
+ LT_SKEL2PIC,\r
+ DK_SKEL2PIC,\r
+\r
+ 0,\r
+\r
+ TAP_1PIC,\r
+ TAP_2PIC,\r
+ TAP_3PIC,\r
+ TAP_4PIC,\r
+ TAP_5PIC,\r
+\r
+ WATER_DOOR1_PIC,\r
+ WATER_DOOR2_PIC,\r
+ };\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= DrawVWall\r
+=\r
+= Draws a wall by vertical segments, for texture mapping!\r
+=\r
+= wallptr->side is true for east/west walls (constant x)\r
+=\r
+= fracheight and fracstep are 16.16 bit fractions\r
+=\r
+=====================\r
+*/\r
+\r
+void DrawVWall (walltype *wallptr)\r
+{\r
+ int x,i;\r
+ unsigned source;\r
+ unsigned width,sourceint;\r
+ unsigned wallpic,wallpicseg;\r
+ unsigned skip;\r
+ long fracheight,fracstep,longheightchange;\r
+ unsigned height;\r
+ int heightchange;\r
+ unsigned slope,distance;\r
+ int traceangle,angle;\r
+ int mapadd;\r
+ unsigned lastpix,lastsource,lastwidth;\r
+\r
+ if (wallptr->rightclip < wallptr->leftclip)\r
+ Quit ("DrawVWall: Right < Left");\r
+\r
+//\r
+// setup for height calculation\r
+//\r
+ wallptr->height1 >>= 1;\r
+ wallptr->height2 >>= 1;\r
+ wallptr->planecoord>>=10; // remove non significant bits\r
+\r
+ width = wallptr->x2 - wallptr->x1;\r
+ if (width)\r
+ {\r
+ heightchange = wallptr->height2 - wallptr->height1;\r
+ asm mov ax,[heightchange]\r
+ asm mov WORD PTR [longheightchange+2],ax\r
+ asm mov WORD PTR [longheightchange],0 // avoid long shift by 16\r
+ fracstep = longheightchange/width;\r
+ }\r
+\r
+ fracheight = ((long)wallptr->height1<<16)+0x8000;\r
+ skip = wallptr->leftclip - wallptr->x1;\r
+ if (skip)\r
+ fracheight += fracstep*skip;\r
+\r
+//\r
+// setup for texture mapping\r
+//\r
+// mapadd is 64*64 (to keep source positive) + the origin wall intercept\r
+// distance has 6 unit bits, and 6 frac bits\r
+// traceangle is the center view angle in FINEANGLES, moved to be in\r
+// the +-90 degree range (to thew right of origin)\r
+//\r
+ traceangle = fineviewangle;\r
+ //\r
+ // find wall picture to map from\r
+ //\r
+ if (wallptr->side)\r
+ { // east or west wall\r
+\r
+ wallpic = walllight1[wallptr->color+wall_anim_pos[wallptr->color]];\r
+ if (wallptr->planecoord < viewxpix)\r
+ {\r
+ distance = viewxpix-wallptr->planecoord;\r
+ traceangle -= FINEANGLES/2;\r
+ mapadd = (64-viewypix&63); // the pixel spot of the origin\r
+ }\r
+ else\r
+ {\r
+ distance = wallptr->planecoord-viewxpix;\r
+ // traceangle is correct\r
+ mapadd = viewypix&63; // the pixel spot of the origin\r
+ }\r
+ }\r
+ else\r
+ { // north or south wall\r
+\r
+ wallpic = walldark1[wallptr->color+wall_anim_pos[wallptr->color]];\r
+ if (wallptr->planecoord < viewypix)\r
+ {\r
+ distance = viewypix-wallptr->planecoord;\r
+ traceangle -= FINEANGLES/4;\r
+ mapadd = viewxpix&63; // the pixel spot of the origin\r
+ }\r
+ else\r
+ {\r
+ distance = wallptr->planecoord-viewypix;\r
+ traceangle -= FINEANGLES*3/4;\r
+ mapadd = (64-viewxpix&63); // the pixel spot of the origin\r
+ }\r
+ }\r
+\r
+ mapadd = 64*64-mapadd; // make sure it stays positive\r
+\r
+ wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC];\r
+ if (traceangle > FINEANGLES/2)\r
+ traceangle -= FINEANGLES;\r
+\r
+//\r
+// calculate everything\r
+//\r
+// IMPORTANT! This loop is executed around 5000 times / second!\r
+//\r
+ lastpix = lastsource = (unsigned)-1;\r
+\r
+ for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++)\r
+ {\r
+ //\r
+ // height\r
+ //\r
+ asm mov ax,WORD PTR [fracheight]\r
+ asm mov dx,WORD PTR [fracheight+2]\r
+ asm mov cx,dx\r
+ asm add ax,WORD PTR [fracstep]\r
+ asm adc dx,WORD PTR [fracstep+2]\r
+ asm mov WORD PTR [fracheight],ax\r
+ asm mov WORD PTR [fracheight+2],dx\r
+ asm mov bx,[x]\r
+ asm shl bx,1\r
+ asm cmp cx,MAXSCALEHEIGHT\r
+ asm jbe storeheight\r
+ asm mov cx,MAXSCALEHEIGHT\r
+storeheight:\r
+ asm mov WORD PTR [wallheight+bx],cx\r
+ asm mov WORD PTR [zbuffer+bx],cx\r
+\r
+// height = fracheight>>16;\r
+// fracheight += fracstep;\r
+// if (height > MAXSCALEHEIGHT)\r
+// height = MAXSCALEHEIGHT;\r
+// wallheight[x] = zbuffer[x] = height;\r
+\r
+ //\r
+ // texture map\r
+ //\r
+ angle = pixelangle[x]+traceangle;\r
+ if (angle<0)\r
+ angle+=FINEANGLES;\r
+\r
+ slope = finetangent[angle];\r
+\r
+//\r
+// distance is an unsigned 6.6 bit number (12 pixel bits)\r
+// slope is a signed 5.10 bit number\r
+// result is a signed 11.16 bit number\r
+//\r
+\r
+#if 0\r
+ source = distance*slope;\r
+ source >>=20;\r
+\r
+ source += mapadd;\r
+ source &= 63; // mask off the unused units\r
+ source = 63-source;\r
+ source <<= 6; // multiply by 64 for offset into pic\r
+#endif\r
+ asm mov ax,[distance]\r
+ asm imul [slope] // ax is the source pixel\r
+ asm mov al,ah\r
+ asm shr al,1\r
+ asm shr al,1 // low 6 bits is now pixel number\r
+ asm add ax,[mapadd]\r
+ asm and ax,63\r
+ asm mov dx,63\r
+ asm sub dx,ax // otherwise it is backwards\r
+ asm shl dx,1\r
+ asm shl dx,1\r
+ asm shl dx,1\r
+ asm shl dx,1\r
+ asm shl dx,1\r
+ asm shl dx,1 // *64 to index into shape\r
+ asm mov [source],dx\r
+\r
+ if (source != lastsource)\r
+ {\r
+ if (lastpix != (unsigned)-1)\r
+ {\r
+ wallofs[lastpix] = lastsource;\r
+ wallseg[lastpix] = wallpicseg;\r
+ wallwidth[lastpix] = lastwidth;\r
+ }\r
+ lastpix = x;\r
+ lastsource = source;\r
+ lastwidth = 1;\r
+ }\r
+ else\r
+ lastwidth++; // optimized draw, same map as last one\r
+ }\r
+ wallofs[lastpix] = lastsource;\r
+ wallseg[lastpix] = wallpicseg;\r
+ wallwidth[lastpix] = lastwidth;\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= TraceRay\r
+=\r
+= Used to find the left and rightmost tile in the view area to be traced from\r
+= Follows a ray of the given angle from viewx,viewy in the global map until\r
+= it hits a solid tile\r
+= sets:\r
+= tile.x,tile.y : tile coordinates of contacted tile\r
+= tilecolor : solid tile's color\r
+=\r
+==================\r
+*/\r
+\r
+int tilecolor;\r
+\r
+void TraceRay (unsigned angle)\r
+{\r
+ long tracex,tracey,tracexstep,traceystep,searchx,searchy;\r
+ fixed fixtemp;\r
+ int otx,oty,searchsteps;\r
+\r
+ tracexstep = costable[angle];\r
+ traceystep = sintable[angle];\r
+\r
+//\r
+// advance point so it is even with the view plane before we start checking\r
+//\r
+ fixtemp = FixedByFrac(prestep,tracexstep);\r
+ tracex = viewx+fixtemp;\r
+ fixtemp = FixedByFrac(prestep,traceystep);\r
+ tracey = viewy-fixtemp;\r
+\r
+ tile.x = tracex>>TILESHIFT; // starting point in tiles\r
+ tile.y = tracey>>TILESHIFT;\r
+\r
+\r
+ if (tracexstep<0) // use 2's complement, not signed magnitude\r
+ tracexstep = -(tracexstep&0x7fffffff);\r
+\r
+ if (traceystep<0) // use 2's complement, not signed magnitude\r
+ traceystep = -(traceystep&0x7fffffff);\r
+\r
+//\r
+// we assume viewx,viewy is not inside a solid tile, so go ahead one step\r
+//\r
+\r
+ do // until a solid tile is hit\r
+ {\r
+ otx = tile.x;\r
+ oty = tile.y;\r
+ spotvis[otx][oty] = true;\r
+ tracex += tracexstep;\r
+ tracey -= traceystep;\r
+ tile.x = tracex>>TILESHIFT;\r
+ tile.y = tracey>>TILESHIFT;\r
+\r
+ if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) )\r
+ {\r
+ //\r
+ // trace crossed two solid tiles, so do a binary search along the line\r
+ // to find a spot where only one tile edge is crossed\r
+ //\r
+ searchsteps = 0;\r
+ searchx = tracexstep;\r
+ searchy = traceystep;\r
+ do\r
+ {\r
+ searchx/=2;\r
+ searchy/=2;\r
+ if (tile.x!=otx && tile.y!=oty)\r
+ {\r
+ // still too far\r
+ tracex -= searchx;\r
+ tracey += searchy;\r
+ }\r
+ else\r
+ {\r
+ // not far enough, no tiles crossed\r
+ tracex += searchx;\r
+ tracey -= searchy;\r
+ }\r
+\r
+ //\r
+ // if it is REAL close, go for the most clockwise intersection\r
+ //\r
+ if (++searchsteps == 16)\r
+ {\r
+ tracex = (long)otx<<TILESHIFT;\r
+ tracey = (long)oty<<TILESHIFT;\r
+ if (tracexstep>0)\r
+ {\r
+ if (traceystep<0)\r
+ {\r
+ tracex += TILEGLOBAL-1;\r
+ tracey += TILEGLOBAL;\r
+ }\r
+ else\r
+ {\r
+ tracex += TILEGLOBAL;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (traceystep<0)\r
+ {\r
+ tracex --;\r
+ tracey += TILEGLOBAL-1;\r
+ }\r
+ else\r
+ {\r
+ tracey --;\r
+ }\r
+ }\r
+ }\r
+\r
+ tile.x = tracex>>TILESHIFT;\r
+ tile.y = tracey>>TILESHIFT;\r
+\r
+ } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) );\r
+ }\r
+ } while (!(tilecolor = tilemap[tile.x][tile.y]) );\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= FixedByFrac\r
+=\r
+= multiply a 16/16 bit, 2's complement fixed point number by a 16 bit\r
+= fraction, passed as a signed magnitude 32 bit number\r
+=\r
+========================\r
+*/\r
+\r
+#pragma warn -rvl // I stick the return value in with ASMs\r
+\r
+fixed FixedByFrac (fixed a, fixed b)\r
+{\r
+ fixed value;\r
+\r
+//\r
+// setup\r
+//\r
+asm mov si,[WORD PTR b+2] // sign of result = sign of fraction\r
+\r
+asm mov ax,[WORD PTR a]\r
+asm mov cx,[WORD PTR a+2]\r
+\r
+asm or cx,cx\r
+asm jns aok: // negative?\r
+asm not ax\r
+asm not cx\r
+asm add ax,1\r
+asm adc cx,0\r
+asm xor si,0x8000 // toggle sign of result\r
+aok:\r
+\r
+//\r
+// multiply cx:ax by bx\r
+//\r
+asm mov bx,[WORD PTR b]\r
+asm mul bx // fraction*fraction\r
+asm mov di,dx // di is low word of result\r
+asm mov ax,cx //\r
+asm mul bx // units*fraction\r
+asm add ax,di\r
+asm adc dx,0\r
+\r
+//\r
+// put result dx:ax in 2's complement\r
+//\r
+asm test si,0x8000 // is the result negative?\r
+asm jz ansok:\r
+asm not ax\r
+asm not dx\r
+asm add ax,1\r
+asm adc dx,0\r
+\r
+ansok:;\r
+\r
+}\r
+\r
+#pragma warn +rvl\r
+\r
+#if 0\r
+/*\r
+=========================\r
+=\r
+= FixedAdd\r
+=\r
+= add two 16 bit fixed point numbers\r
+= to subtract, invert the sign of B before invoking\r
+=\r
+=========================\r
+*/\r
+\r
+fixed FixedAdd (fixed a, fixed b)\r
+{\r
+ fixed value;\r
+\r
+asm mov ax,[WORD PTR a]\r
+asm mov dx,[WORD PTR a+2]\r
+\r
+asm mov bx,[WORD PTR b]\r
+asm mov cx,[WORD PTR b+2]\r
+\r
+asm or dx,dx\r
+asm jns aok: // negative?\r
+asm and dx,0x7fff\r
+asm not ax // convert a from signed magnitude to 2's compl\r
+asm not dx\r
+asm add ax,1\r
+asm adc dx,0\r
+aok:\r
+\r
+asm or cx,cx\r
+asm jns bok: // negative?\r
+asm and cx,0x7fff\r
+asm not bx // convert b from signed magnitude to 2's compl\r
+asm not cx\r
+asm add bx,1\r
+asm adc cx,0\r
+bok:\r
+\r
+asm add ax,bx // perform the addition\r
+asm adc dx,cx\r
+asm jns done\r
+\r
+asm and dx,0x7fff // value was negative\r
+asm not ax // back to signed magnitude\r
+asm not dx\r
+asm add ax,1\r
+asm adc dx,0\r
+\r
+done:\r
+\r
+asm mov [WORD PTR value],ax\r
+asm mov [WORD PTR value+2],dx\r
+\r
+ return value;\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= TransformPoint\r
+=\r
+= Takes paramaters:\r
+= gx,gy : globalx/globaly of point\r
+=\r
+= globals:\r
+= viewx,viewy : point of view\r
+= viewcos,viewsin : sin/cos of viewangle\r
+=\r
+=\r
+= defines:\r
+= CENTERX : pixel location of center of view window\r
+= TILEGLOBAL : size of one\r
+= FOCALLENGTH : distance behind viewx/y for center of projection\r
+= scale : conversion from global value to screen value\r
+=\r
+= returns:\r
+= screenx,screenheight: projected edge location and size\r
+=\r
+========================\r
+*/\r
+\r
+void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight)\r
+{\r
+ int ratio;\r
+ fixed gxt,gyt,nx,ny;\r
+\r
+//\r
+// translate point to view centered coordinates\r
+//\r
+ gx = gx-viewx;\r
+ gy = gy-viewy;\r
+\r
+//\r
+// calculate newx\r
+//\r
+ gxt = FixedByFrac(gx,viewcos);\r
+ gyt = FixedByFrac(gy,viewsin);\r
+ nx = gxt-gyt;\r
+\r
+//\r
+// calculate newy\r
+//\r
+ gxt = FixedByFrac(gx,viewsin);\r
+ gyt = FixedByFrac(gy,viewcos);\r
+ ny = gyt+gxt;\r
+\r
+//\r
+// calculate perspective ratio\r
+//\r
+ if (nx<0)\r
+ nx = 0;\r
+\r
+ ratio = nx*scale/FOCALLENGTH;\r
+\r
+ if (ratio<=MINRATIO)\r
+ ratio = MINRATIO;\r
+\r
+ *screenx = CENTERX + ny/ratio;\r
+\r
+ *screenheight = TILEGLOBAL/ratio;\r
+\r
+}\r
+\r
+\r
+//\r
+// transform actor\r
+//\r
+void TransformActor (objtype *ob)\r
+{\r
+ int ratio;\r
+ fixed gx,gy,gxt,gyt,nx,ny;\r
+\r
+//\r
+// translate point to view centered coordinates\r
+//\r
+ gx = ob->x-viewx;\r
+ gy = ob->y-viewy;\r
+\r
+//\r
+// calculate newx\r
+//\r
+ gxt = FixedByFrac(gx,viewcos);\r
+ gyt = FixedByFrac(gy,viewsin);\r
+ nx = gxt-gyt-ob->size;\r
+\r
+//\r
+// calculate newy\r
+//\r
+ gxt = FixedByFrac(gx,viewsin);\r
+ gyt = FixedByFrac(gy,viewcos);\r
+ ny = gyt+gxt;\r
+\r
+//\r
+// calculate perspective ratio\r
+//\r
+ if (nx<0)\r
+ nx = 0;\r
+\r
+ ratio = nx*scale/FOCALLENGTH;\r
+\r
+ if (ratio<=MINRATIO)\r
+ ratio = MINRATIO;\r
+\r
+ ob->viewx = CENTERX + ny/ratio;\r
+\r
+ ob->viewheight = TILEGLOBAL/ratio;\r
+}\r
+\r
+//==========================================================================\r
+\r
+fixed TransformX (fixed gx, fixed gy)\r
+{\r
+ int ratio;\r
+ fixed gxt,gyt,nx,ny;\r
+\r
+//\r
+// translate point to view centered coordinates\r
+//\r
+ gx = gx-viewx;\r
+ gy = gy-viewy;\r
+\r
+//\r
+// calculate newx\r
+//\r
+ gxt = FixedByFrac(gx,viewcos);\r
+ gyt = FixedByFrac(gy,viewsin);\r
+\r
+ return gxt-gyt;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= BuildTables\r
+=\r
+= Calculates:\r
+=\r
+= scale projection constant\r
+= sintable/costable overlapping fractional tables\r
+= firstangle/lastangle angles from focalpoint to left/right view edges\r
+= prestep distance from focal point before checking for tiles\r
+=\r
+==================\r
+*/\r
+\r
+void BuildTables (void)\r
+{\r
+ int i;\r
+ long intang;\r
+ long x;\r
+ float angle,anglestep,radtoint;\r
+ double tang;\r
+ fixed value;\r
+\r
+//\r
+// calculate the angle offset from view angle of each pixel's ray\r
+//\r
+ radtoint = (float)FINEANGLES/2/PI;\r
+ for (i=0;i<VIEWWIDTH/2;i++)\r
+ {\r
+ // start 1/2 pixel over, so viewangle bisects two middle pixels\r
+ x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;\r
+ tang = (float)x/(FOCALLENGTH+MINDIST);\r
+ angle = atan(tang);\r
+ intang = angle*radtoint;\r
+ pixelangle[VIEWWIDTH/2-1-i] = intang;\r
+ pixelangle[VIEWWIDTH/2+i] = -intang;\r
+ }\r
+\r
+//\r
+// calculate fine tangents\r
+// 1 sign bit, 5 units (clipped to), 10 fracs\r
+//\r
+#define MININT (-MAXINT)\r
+\r
+ for (i=0;i<FINEANGLES/4;i++)\r
+ {\r
+ intang = tan(i/radtoint)*(1l<<10);\r
+\r
+ //\r
+ // if the tangent is not reprentable in this many bits, bound the\r
+ // units part ONLY\r
+ //\r
+ if (intang>MAXINT)\r
+ intang = 0x8f00 | (intang & 0xff);\r
+ else if (intang<MININT)\r
+ intang = 0xff00 | (intang & 0xff);\r
+\r
+ finetangent[i] = intang;\r
+// finetangent[FINEANGLES/2+i] = intang;\r
+// finetangent[FINEANGLES/2-i-1] = -intang;\r
+ finetangent[FINEANGLES-i-1] = -intang;\r
+ }\r
+\r
+//\r
+// calculate scale value so one tile at mindist allmost fills the view horizontally\r
+//\r
+ scale = GLOBAL1/VIEWWIDTH;\r
+ scale *= focallength;\r
+ scale /= (focallength+mindist);\r
+\r
+//\r
+// costable overlays sintable with a quarter phase shift\r
+// ANGLES is assumed to be divisable by four\r
+//\r
+// The low word of the value is the fraction, the high bit is the sign bit,\r
+// bits 16-30 should be 0\r
+//\r
+\r
+ angle = 0;\r
+ anglestep = PI/2/ANGLEQUAD;\r
+ for (i=0;i<=ANGLEQUAD;i++)\r
+ {\r
+ value=GLOBAL1*sin(angle);\r
+ sintable[i]=\r
+ sintable[i+ANGLES]=\r
+ sintable[ANGLES/2-i] = value;\r
+ sintable[ANGLES-i]=\r
+ sintable[ANGLES/2+i] = value | 0x80000000l;\r
+ angle += anglestep;\r
+ }\r
+\r
+//\r
+// figure trace angles for first and last pixel on screen\r
+//\r
+ angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);\r
+ angle *= ANGLES/(PI*2);\r
+\r
+ intang = (int)angle+1;\r
+ firstangle = intang;\r
+ lastangle = -intang;\r
+\r
+ prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);\r
+\r
+//\r
+// misc stuff\r
+//\r
+ walls[0].x2 = VIEWX-1;\r
+ walls[0].height2 = 32000;\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= ClearScreen\r
+=\r
+=====================\r
+*/\r
+\r
+void ClearScreen (void)\r
+{\r
+ unsigned topcolor=*skycolor, bottomcolor=*groundcolor;\r
+ unsigned topimage=topcolor&0xf0,bottomimage=bottomcolor&0xf0;\r
+ unsigned pfoffset=0;\r
+\r
+\r
+#if USE_STRIPS\r
+ if (topimage == 0x20) // special code for lightning\r
+ topimage = topcolor = 0;\r
+\r
+// Manually wipe screen with solid color.\r
+// If BOTH sky and ground are 'images' don't manually clear it!\r
+//\r
+ if ((!topimage) || (!bottomimage))\r
+ {\r
+#endif\r
+\r
+ //\r
+ // clear the screen\r
+ //\r
+asm mov dx,GC_INDEX\r
+asm mov ax,GC_MODE + 256*2 // read mode 0, write mode 2\r
+asm out dx,ax\r
+asm mov ax,GC_BITMASK + 255*256\r
+asm out dx,ax\r
+\r
+//asm mov dx,40-VIEWWIDTH/8 // dx = modulo\r
+asm mov bl,VIEWWIDTH/16\r
+asm mov bh,CENTERY+1\r
+\r
+asm mov ax,topcolor\r
+asm mov es,[screenseg]\r
+asm mov di,[bufferofs]\r
+asm add di,((SCREENWIDTH*VIEWY)+(VIEWX/8))\r
+\r
+toploop:\r
+asm mov cl,bl\r
+asm rep stosw\r
+asm stosb\r
+//asm add di,dx // no need to add "0" modulo\r
+asm dec bh\r
+asm jnz toploop\r
+\r
+asm mov bh,CENTERY+1\r
+asm mov ax,bottomcolor\r
+\r
+bottomloop:\r
+asm mov cl,bl\r
+asm rep stosw\r
+asm stosb\r
+//asm add di,dx // no need to add "0" modulo\r
+asm dec bh\r
+asm jnz bottomloop\r
+\r
+#if USE_STRIPS\r
+ }\r
+\r
+\r
+//\r
+// code to test parallax turning\r
+//\r
+\r
+ if (topimage)\r
+ {\r
+ topimage -= 16;\r
+ pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12);\r
+ while (pfoffset >= 640)\r
+ pfoffset -= 640;\r
+ LatchDrawPicStrip(0,0,SKY1PIC+topimage,pfoffset+8);\r
+ }\r
+\r
+ if (bottomimage)\r
+ {\r
+//// pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12)+320;\r
+// pfoffset += 320;\r
+// while (pfoffset >= 640)\r
+// pfoffset -= 640;\r
+// LatchDrawPicStrip(0,64,SKY1PIC+topimage,pfoffset+8);\r
+ bottomimage -= 16;\r
+ LatchDrawPic(0,64,GND1PIC+bottomimage);\r
+ }\r
+#endif\r
+\r
+\r
+asm mov dx,GC_INDEX\r
+asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2\r
+asm out dx,ax\r
+asm mov al,GC_BITMASK\r
+asm out dx,al\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= DrawWallList\r
+=\r
+= Clips and draws all the walls traced this refresh\r
+=\r
+=====================\r
+*/\r
+\r
+void DrawWallList (void)\r
+{\r
+ int i,leftx,newleft,rightclip;\r
+ walltype *wall, *check;\r
+\r
+asm mov ax,ds\r
+asm mov es,ax\r
+asm mov di,OFFSET wallwidth\r
+asm xor ax,ax\r
+asm mov cx,VIEWWIDTH/2\r
+asm rep stosw\r
+\r
+ ClearScreen ();\r
+\r
+ rightwall->x1 = VIEWXH+1;\r
+ rightwall->height1 = 32000;\r
+ (rightwall+1)->x1 = 32000;\r
+\r
+ leftx = -1;\r
+\r
+ for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)\r
+ {\r
+ if (leftx >= wall->x2)\r
+ continue;\r
+\r
+ rightclip = wall->x2;\r
+\r
+ check = wall+1;\r
+ while (check->x1 <= rightclip && check->height1 >= wall->height2)\r
+ {\r
+ rightclip = check->x1-1;\r
+ check++;\r
+ }\r
+\r
+ if (rightclip>VIEWXH)\r
+ rightclip=VIEWXH;\r
+\r
+ if (leftx < wall->x1 - 1)\r
+ newleft = wall->x1-1; // there was black space between walls\r
+ else\r
+ newleft = leftx;\r
+\r
+ if (rightclip > newleft)\r
+ {\r
+ wall->leftclip = newleft+1;\r
+ wall->rightclip = rightclip;\r
+ DrawVWall (wall);\r
+ leftx = rightclip;\r
+ }\r
+ }\r
+\r
+#ifndef DRAWEACH\r
+ ScaleWalls (); // draw all the walls\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= DrawScaleds\r
+=\r
+= Draws all objects that are visable\r
+=\r
+=====================\r
+*/\r
+\r
+objtype *depthsort[MAXACTORS];\r
+\r
+void DrawScaleds (void)\r
+{\r
+#if USE_INERT_LIST\r
+ extern inertobjtype inertobjlist[], *inert;\r
+\r
+ boolean inertlist=false;\r
+#endif\r
+ int i,j,least,numvisable,height;\r
+ objtype *obj,**vislist,*farthest;\r
+ memptr shape;\r
+ byte *tilespot,*visspot;\r
+\r
+ numvisable = 0;\r
+\r
+//\r
+// calculate base positions of all objects\r
+//\r
+ vislist = &depthsort[0];\r
+\r
+ obj = player->next;\r
+ while (obj)\r
+ {\r
+ tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley;\r
+ visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley;\r
+ //\r
+ // could be in any of the nine surrounding tiles\r
+ //\r
+ if (*visspot\r
+ || ( *(visspot-1) && !*(tilespot-1) )\r
+ || ( *(visspot+1) && !*(tilespot+1) )\r
+ || ( *(visspot-65) && !*(tilespot-65) )\r
+ || ( *(visspot-64) && !*(tilespot-64) )\r
+ || ( *(visspot-63) && !*(tilespot-63) )\r
+ || ( *(visspot+65) && !*(tilespot+65) )\r
+ || ( *(visspot+64) && !*(tilespot+64) )\r
+ || ( *(visspot+63) && !*(tilespot+63) ) )\r
+ {\r
+#if USE_INERT_LIST\r
+ if (!inertlist)\r
+#endif\r
+ if ((obj->active == noalways) || (obj->active == always))\r
+ obj->active = always;\r
+ else\r
+ obj->active = yes;\r
+ TransformActor (obj);\r
+ if (!obj->viewheight || obj->viewheight > VIEWWIDTH)\r
+ goto cont; // too close or far away\r
+\r
+ if (!obj->state->shapenum)\r
+ goto cont;\r
+\r
+ *vislist++ = obj;\r
+ numvisable++;\r
+ }\r
+ else\r
+#if USE_INERT_LIST\r
+ if (!inertlist)\r
+#endif\r
+ if ((obj->active != always) && (obj->active != noalways))\r
+ obj->active = no;\r
+\r
+cont:;\r
+ obj = obj->next;\r
+#if USE_INERT_LIST\r
+ if ((!obj) && (!inertlist))\r
+ {\r
+ if (inert != inertobjlist)\r
+ obj = (objtype *)inertobjlist;\r
+ inertlist = true;\r
+ }\r
+#endif\r
+ }\r
+\r
+ if (vislist == &depthsort[0])\r
+ return; // no visable objects\r
+\r
+//\r
+// draw from back to front\r
+//\r
+ for (i = 0; i<numvisable; i++)\r
+ {\r
+ least = 32000;\r
+ for (j=0;j<numvisable;j++)\r
+ {\r
+ height = depthsort[j]->viewheight;\r
+ if (height < least)\r
+ {\r
+ least = height;\r
+ farthest = depthsort[j];\r
+ }\r
+ }\r
+ //\r
+ // draw farthest\r
+ //\r
+ shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC];\r
+ ScaleShape(farthest->viewx,shape,farthest->viewheight);\r
+ farthest->viewheight = 32000;\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= CalcTics\r
+=\r
+=====================\r
+*/\r
+\r
+void CalcTics (void)\r
+{\r
+ long newtime,oldtimecount;\r
+\r
+\r
+#ifdef PROFILE\r
+ tics = 1;\r
+ return;\r
+#endif\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+ if (lasttimecount > TimeCount)\r
+ TimeCount = lasttimecount; // if the game was paused a LONG time\r
+\r
+#if 0\r
+ if (DemoMode) // demo recording and playback needs\r
+ { // to be constant\r
+//\r
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken\r
+//\r
+ oldtimecount = lasttimecount;\r
+ while (TimeCount<oldtimecount+DEMOTICS*2)\r
+ ;\r
+ lasttimecount = oldtimecount + DEMOTICS;\r
+ TimeCount = lasttimecount + DEMOTICS;\r
+ realtics = tics = DEMOTICS;\r
+ }\r
+ else\r
+#endif\r
+ {\r
+//\r
+// non demo, so report actual time\r
+//\r
+ newtime = TimeCount;\r
+ realtics = tics = newtime-lasttimecount;\r
+ lasttimecount = newtime;\r
+\r
+#ifdef FILEPROFILE\r
+ strcpy (scratch,"\tTics:");\r
+ itoa (tics,str,10);\r
+ strcat (scratch,str);\r
+ strcat (scratch,"\n");\r
+ write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+ if (tics>MAXTICS)\r
+ {\r
+ TimeCount -= (tics-MAXTICS);\r
+ tics = MAXTICS;\r
+ }\r
+\r
+ if (realtics>MAXREALTICS)\r
+ realtics = MAXREALTICS;\r
+ }\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= DrawHand\r
+=\r
+========================\r
+*/\r
+\r
+void DrawHand (void)\r
+{\r
+ #define HAND_X_POS ((VIEWWIDTH/16)-(10/2)) // "10" = hand width in bytes\r
+\r
+ #define picnum HAND1PICM\r
+\r
+ memptr source;\r
+ unsigned dest,width,height;\r
+\r
+// if (gamestate.shotpower || boltsleft)\r
+// picnum += (((unsigned)TimeCount>>3)&1);\r
+\r
+ source = grsegs[picnum];\r
+ dest = ylookup[VIEWHEIGHT-handheight]+HAND_X_POS+bufferofs; // 12\r
+ width = picmtable[picnum-STARTPICM].width;\r
+ height = picmtable[picnum-STARTPICM].height;\r
+\r
+ VW_MaskBlock(source,0,dest,width,handheight,width*height);\r
+ EGAMAPMASK(15);\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= ThreeDRefresh\r
+=\r
+========================\r
+*/\r
+\r
+void ThreeDRefresh (void)\r
+{\r
+ int tracedir;\r
+\r
+restart:\r
+ aborttrace = false;\r
+\r
+//\r
+// clear out the traced array\r
+//\r
+asm mov ax,ds\r
+asm mov es,ax\r
+asm mov di,OFFSET spotvis\r
+asm xor ax,ax\r
+asm mov cx,[mapwidth] // mapheight*32 words\r
+asm shl cx,1\r
+asm shl cx,1\r
+asm shl cx,1\r
+asm shl cx,1\r
+asm shl cx,1\r
+asm rep stosw\r
+\r
+\r
+//\r
+// set up variables for this view\r
+//\r
+\r
+ viewangle = player->angle;\r
+ fineviewangle = viewangle*(FINEANGLES/ANGLES);\r
+ viewsin = sintable[viewangle];\r
+ viewcos = costable[viewangle];\r
+ viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos);\r
+ viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin);\r
+ viewx &= 0xfffffc00; // stop on a pixel boundary\r
+ viewy &= 0xfffffc00;\r
+ viewx += 0x180;\r
+ viewy += 0x180;\r
+ viewxpix = viewx>>10;\r
+ viewypix = viewy>>10;\r
+\r
+ focal.x = viewx>>TILESHIFT;\r
+ focal.y = viewy>>TILESHIFT;\r
+\r
+//\r
+// find the rightmost visable tile in view\r
+//\r
+ tracedir = viewangle + lastangle;\r
+ if (tracedir<0)\r
+ tracedir+=ANGLES;\r
+ else if (tracedir>=ANGLES)\r
+ tracedir-=ANGLES;\r
+ TraceRay( tracedir );\r
+ right.x = tile.x;\r
+ right.y = tile.y;\r
+\r
+//\r
+// find the leftmost visable tile in view\r
+//\r
+ tracedir = viewangle + firstangle;\r
+ if (tracedir<0)\r
+ tracedir+=ANGLES;\r
+ else if (tracedir>=ANGLES)\r
+ tracedir-=ANGLES;\r
+ TraceRay( tracedir );\r
+\r
+//\r
+// follow the walls from there to the right\r
+//\r
+ rightwall = &walls[1];\r
+ FollowWalls ();\r
+\r
+ if (aborttrace)\r
+ goto restart;\r
+\r
+//\r
+// actually draw stuff\r
+//\r
+ if (++screenpage == 3)\r
+ screenpage = 0;\r
+\r
+ bufferofs = screenloc[screenpage];\r
+\r
+ EGAWRITEMODE(2);\r
+ EGAMAPMASK(15);\r
+\r
+//\r
+// draw the wall list saved be FollowWalls ()\r
+//\r
+// animframe = (TimeCount&8)>>3;\r
+\r
+//\r
+// draw all the scaled images\r
+//\r
+ asm mov dx,GC_INDEX\r
+\r
+ asm mov ax,GC_COLORDONTCARE\r
+ asm out dx,ax // don't look at any of the planes\r
+\r
+ asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2\r
+ asm out dx,ax\r
+\r
+ asm mov al,GC_BITMASK\r
+ asm out dx,al\r
+\r
+ AnimateWallList();\r
+ DrawWallList();\r
+ DrawScaleds();\r
+\r
+ EGAWRITEMODE(0);\r
+ EGABITMASK(0xff);\r
+\r
+//\r
+// draw hand\r
+//\r
+ if (handheight)\r
+ DrawHand ();\r
+\r
+//\r
+// show screen and time last cycle\r
+//\r
+ if (fizzlein)\r
+ {\r
+ fizzlein = false;\r
+ FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true);\r
+ lasttimecount = TimeCount;\r
+ if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement\r
+ }\r
+\r
+asm cli\r
+asm mov cx,[bufferofs]\r
+asm mov dx,3d4h // CRTC address register\r
+asm mov al,0ch // start address high register\r
+asm out dx,al\r
+asm inc dx\r
+asm mov al,ch\r
+asm out dx,al // set the high byte\r
+asm dec dx\r
+asm mov al,0dh // start address low register\r
+asm out dx,al\r
+asm inc dx\r
+asm mov al,cl\r
+asm out dx,al // set the low byte\r
+asm sti\r
+\r
+ displayofs = bufferofs;\r
+\r
+ CalcTics ();\r
+\r
+}\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_GAME.C\r
+\r
+#include <stdlib.h>\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#pragma hdrstop\r
+\r
+#ifdef PROFILE\r
+#include "TIME.H"\r
+#endif\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define NUMLUMPS 61\r
+\r
+#define SUCCUBUSLUMP 0\r
+#define FATDEMONLUMP 1\r
+#define BOLTLUMP 2\r
+#define NUKELUMP 3\r
+#define POTIONLUMP 4\r
+#define RKEYLUMP 5\r
+#define YKEYLUMP 6\r
+#define GKEYLUMP 7\r
+#define BKEYLUMP 8\r
+//#define SCROLLLUMP 9\r
+#define CHESTLUMP 10\r
+#define PLAYERLUMP 11\r
+#define WALL1LUMP 12\r
+#define WALL2LUMP 13\r
+#define BDOORLUMP 14\r
+#define GODESSLUMP 15\r
+#define MAGELUMP 16\r
+#define BATLUMP 17\r
+#define GRELLUMP 18\r
+#define TOMBSTONESLUMP 19\r
+#define ZOMBIELUMP 20\r
+#define ANTLUMP 21\r
+#define SKELETONLUMP 22\r
+#define RGEMLUMP 23\r
+#define GGEMLUMP 24\r
+#define BGEMLUMP 25\r
+#define YGEMLUMP 26\r
+#define PGEMLUMP 27\r
+//#define RKEY2LUMP 28\r
+#define DRAGONLUMP 29\r
+#define OBJ_WARPLUMP 30\r
+#define EYELUMP 31\r
+#define REDDEMONLUMP 32\r
+//#define PITLUMP 33\r
+#define FTIMELUMP 34\r
+#define WATERCHESTLUMP 35\r
+#define TREELUMP 36\r
+#define ARCH1LUMP 37\r
+#define BUNNYLUMP 38\r
+#define ANTHILLLUMP 39\r
+#define COLUMNLUMP 40\r
+#define SULPHURGASLUMP 41\r
+#define FIREPOTLUMP 42\r
+//#define WHIRLPOOLLUMP 43\r
+#define FOUNTAINLUMP 44\r
+#define FORCEFIELDLUMP 45\r
+#define ARCH2LUMP 46\r
+#define ARCH3LUMP 47\r
+#define ARCH4LUMP 48\r
+#define ARCH5LUMP 49\r
+#define ARCH6LUMP 50\r
+#define SKELHANGLUMP 51\r
+//#define SKELPILELUMP 52\r
+#define ARCH7LUMP 53\r
+#define ARCH8LUMP 54\r
+#define ARCH9LUMP 55\r
+#define ARCH10LUMP 56\r
+#define ARCH11LUMP 57\r
+#define ARCH12LUMP 58\r
+#define ARCH13LUMP 59\r
+\r
+int lumpstart[NUMLUMPS] = {\r
+SUCCUBUS_LUMP_START,\r
+FATDEMON_LUMP_START,\r
+BOLT_LUMP_START,\r
+NUKE_LUMP_START,\r
+POTION_LUMP_START,\r
+RKEY_LUMP_START,\r
+YKEY_LUMP_START,\r
+GKEY_LUMP_START,\r
+BKEY_LUMP_START,\r
+0,\r
+//SCROLL_LUMP_START,\r
+CHEST_LUMP_START,\r
+PLAYER_LUMP_START,\r
+//WALL1_LUMP_START,\r
+//WALL2_LUMP_START,\r
+//BDOOR_LUMP_START,\r
+0,0,0,\r
+GODESS_LUMP_START,\r
+MAGE_LUMP_START,\r
+BAT_LUMP_START,\r
+GREL_LUMP_START,\r
+TOMBSTONES_LUMP_START,\r
+ZOMBIE_LUMP_START,\r
+ANT_LUMP_START,\r
+SKELDUDE_LUMP_START,\r
+RGEM_LUMP_START,\r
+GGEM_LUMP_START,\r
+BGEM_LUMP_START,\r
+YGEM_LUMP_START,\r
+PGEM_LUMP_START,\r
+0, //RKEY2_LUMP_START,\r
+DRAGON_LUMP_START,\r
+OBJ_WARP_LUMP_START,\r
+EYE_LUMP_START,\r
+REDDEMON_LUMP_START,\r
+0, //PIT_LUMP_START,\r
+TIME_LUMP_START,\r
+O_WATER_CHEST_LUMP_START,\r
+TREE_LUMP_START,\r
+ARCH1_LUMP_START,\r
+BUNNY_LUMP_START,\r
+ANTHILL_LUMP_START,\r
+COLUMN_LUMP_START,\r
+SULPHURGAS_LUMP_START,\r
+FIREPOT_LUMP_START,\r
+0, //WHIRLPOOL_LUMP_START,\r
+FOUNTAIN_LUMP_START,\r
+FORCEFIELD_LUMP_START,\r
+ARCH2_LUMP_START,\r
+ARCH3_LUMP_START,\r
+ARCH4_LUMP_START,\r
+ARCH5_LUMP_START,\r
+ARCH6_LUMP_START,\r
+SKELHANG_LUMP_START,\r
+0, //SKELPILE_LUMP_START,\r
+ARCH7_LUMP_START,\r
+ARCH8_LUMP_START,\r
+ARCH9_LUMP_START,\r
+ARCH10_LUMP_START,\r
+ARCH11_LUMP_START,\r
+ARCH12_LUMP_START,\r
+ARCH13_LUMP_START,\r
+};\r
+\r
+\r
+int lumpend[NUMLUMPS] = {\r
+SUCCUBUS_LUMP_END,\r
+FATDEMON_LUMP_END,\r
+BOLT_LUMP_END,\r
+NUKE_LUMP_END,\r
+POTION_LUMP_END,\r
+RKEY_LUMP_END,\r
+YKEY_LUMP_END,\r
+GKEY_LUMP_END,\r
+BKEY_LUMP_END,\r
+0,\r
+//SCROLL_LUMP_END,\r
+CHEST_LUMP_END,\r
+PLAYER_LUMP_END,\r
+0,0,0,\r
+GODESS_LUMP_END,\r
+MAGE_LUMP_END,\r
+BAT_LUMP_END,\r
+GREL_LUMP_END,\r
+TOMBSTONES_LUMP_END,\r
+ZOMBIE_LUMP_END,\r
+ANT_LUMP_END,\r
+SKELDUDE_LUMP_END,\r
+RGEM_LUMP_END,\r
+GGEM_LUMP_END,\r
+BGEM_LUMP_END,\r
+YGEM_LUMP_END,\r
+PGEM_LUMP_END,\r
+0, //RKEY2_LUMP_END,\r
+DRAGON_LUMP_END,\r
+OBJ_WARP_LUMP_END,\r
+EYE_LUMP_END,\r
+REDDEMON_LUMP_END,\r
+0, //PIT_LUMP_END,\r
+TIME_LUMP_END,\r
+O_WATER_CHEST_LUMP_END,\r
+TREE_LUMP_END,\r
+ARCH1_LUMP_END,\r
+BUNNY_LUMP_END,\r
+ANTHILL_LUMP_END,\r
+COLUMN_LUMP_END,\r
+SULPHURGAS_LUMP_END,\r
+FIREPOT_LUMP_END,\r
+0, //WHIRLPOOL_LUMP_END,\r
+FOUNTAIN_LUMP_END,\r
+FORCEFIELD_LUMP_END,\r
+ARCH2_LUMP_END,\r
+ARCH3_LUMP_END,\r
+ARCH4_LUMP_END,\r
+ARCH5_LUMP_END,\r
+ARCH6_LUMP_END,\r
+SKELHANG_LUMP_END,\r
+0, //SKELPILE_LUMP_END,\r
+ARCH7_LUMP_END,\r
+ARCH8_LUMP_END,\r
+ARCH9_LUMP_END,\r
+ARCH10_LUMP_END,\r
+ARCH11_LUMP_END,\r
+ARCH12_LUMP_END,\r
+ARCH13_LUMP_END,\r
+};\r
+\r
+\r
+//extern unsigned scolor,gcolor;\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+unsigned latchpics[NUMLATCHPICS];\r
+unsigned tileoffsets[NUMTILE16];\r
+unsigned textstarts[27];\r
+\r
+boolean splitscreen=false;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean lumpneeded[NUMLUMPS];\r
+\r
+\r
+//===========================================================================\r
+\r
+//==========================================================================\r
+//\r
+//\r
+// LOCAL PROTOTYPES\r
+//\r
+//\r
+//==========================================================================\r
+\r
+void CashPoints(void);\r
+\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= ScanInfoPlane\r
+=\r
+= Spawn all actors and mark down special places\r
+=\r
+==========================\r
+*/\r
+\r
+void ScanInfoPlane (void)\r
+{\r
+ unsigned char hibyte;\r
+ unsigned x,y,i,j;\r
+ unsigned int tile;\r
+ unsigned far *start;\r
+\r
+ InitObjList(); // start spawning things with a clean slate\r
+\r
+ scolor = gcolor = 0;\r
+ skycolor = &scolor;\r
+ groundcolor = &gcolor;\r
+\r
+\r
+ memset (lumpneeded,0,sizeof(lumpneeded));\r
+\r
+ start = mapsegs[2];\r
+ for (y=0;y<mapheight;y++)\r
+ for (x=0;x<mapwidth;x++)\r
+ {\r
+ tile = *start++;\r
+ hibyte = tile >> 8;\r
+ tile &= 0xff;\r
+\r
+ switch (hibyte)\r
+ {\r
+ char hi;\r
+\r
+ case 0xFB:\r
+ wall_anim_time = tile;\r
+ tile = 0;\r
+ break;\r
+\r
+ case 0xfa: // sky/ground color\r
+ case 0xf9: // sky/ground 'strip'\r
+ x++;\r
+ tile = *start++;\r
+ hi = tile >> 8;\r
+ tile &= 0xff;\r
+ switch (hibyte)\r
+ {\r
+ case 0xfa: // sky / ground color\r
+ scolor = ((hi)|(hi<<8));\r
+ gcolor = ((tile)|(tile<<8));\r
+ skycolor = &scolor;\r
+ groundcolor = &gcolor;\r
+ break;\r
+\r
+ case 0xf9: // sky / ground 'strip'\r
+ break;\r
+ }\r
+ break;\r
+ }\r
+\r
+ if ((!tile) || (hibyte))\r
+ continue;\r
+\r
+ switch (tile)\r
+ {\r
+ case 1:\r
+ case 2:\r
+ case 3:\r
+ case 4:\r
+ lumpneeded[PLAYERLUMP] = true;\r
+ SpawnPlayer(x,y,NORTH+tile-1);\r
+ break;\r
+\r
+ case 5:\r
+ case 6:\r
+ case 7:\r
+ case 8:\r
+ case 9:\r
+ case 10:\r
+ case 11:\r
+ lumpneeded[tile-5+BOLTLUMP] = true;\r
+ SpawnBonus(x,y,tile-5);\r
+ break;\r
+\r
+#if 0\r
+ case 12:\r
+ case 13:\r
+ case 14:\r
+ case 15:\r
+ case 16:\r
+ case 17:\r
+ case 18:\r
+ case 19:\r
+ lumpneeded[SCROLLLUMP] = true;\r
+ SpawnBonus(x,y,B_SCROLL1+tile-12);\r
+ break;\r
+#endif\r
+\r
+ case 20:\r
+ lumpneeded[REDDEMONLUMP] = true;\r
+ SpawnRedDemon (x,y);\r
+ break;\r
+\r
+#if 0\r
+ case 20: // goal\r
+ lumpneeded[GOALLUMP] = true;\r
+ SpawnBonus(x,y,B_GOAL);\r
+ break;\r
+#endif\r
+\r
+ case 21:\r
+ lumpneeded[GODESSLUMP] = true;\r
+ SpawnGodess (x,y);\r
+ break;\r
+\r
+ case 22:\r
+ lumpneeded[FATDEMONLUMP] = true;\r
+ SpawnFatDemon (x,y);\r
+ break;\r
+\r
+ case 23:\r
+ lumpneeded[SUCCUBUSLUMP] = true;\r
+ SpawnSuccubus (x,y);\r
+ break;\r
+\r
+ case 24:\r
+ lumpneeded[DRAGONLUMP] = true;\r
+ SpawnDragon(x,y);\r
+ break;\r
+\r
+ case 25:\r
+ lumpneeded[BATLUMP] = true;\r
+ SpawnBat (x,y);\r
+ break;\r
+\r
+ case 26:\r
+ lumpneeded[EYELUMP] = true;\r
+ SpawnEye(x,y);\r
+ break;\r
+\r
+ case 27:\r
+ lumpneeded[MAGELUMP] = true;\r
+ SpawnMage (x,y);\r
+ break;\r
+\r
+ case 28:\r
+ lumpneeded[RKEYLUMP] = lumpneeded[GRELLUMP] = true;\r
+ SpawnGrelminar (x,y);\r
+ break;\r
+\r
+ case 30:\r
+ lumpneeded[ANTLUMP] = true;\r
+ SpawnAnt(x,y);\r
+ break;\r
+\r
+ case 31:\r
+ case 32:\r
+ case 33:\r
+ case 34:\r
+ case 35:\r
+ lumpneeded[OBJ_WARPLUMP] = true;\r
+ SpawnWarp (x,y,tile-30);\r
+ break;\r
+\r
+ case 36:\r
+ lumpneeded[ZOMBIELUMP] = true;\r
+ SpawnZombie(x,y);\r
+ break;\r
+\r
+ case 37:\r
+ lumpneeded[SKELETONLUMP] = true;\r
+ SpawnSkeleton(x,y);\r
+ break;\r
+\r
+ case 38:\r
+ lumpneeded[SKELETONLUMP] = true;\r
+ SpawnWallSkeleton(x,y);\r
+ break;\r
+\r
+ case 39:\r
+ lumpneeded[FTIMELUMP] = true;\r
+ SpawnFTime(x,y);\r
+ break;\r
+\r
+ case 40:\r
+ case 41:\r
+ case 42:\r
+ case 43:\r
+ case 44:\r
+ lumpneeded[tile-40+RGEMLUMP] = true;\r
+ SpawnBonus(x,y,tile-40+B_RGEM);\r
+ break;\r
+\r
+ case 45:\r
+ case 46:\r
+ case 47:\r
+ lumpneeded[TOMBSTONESLUMP] = true;\r
+ SpawnTombstone(x,y,tile-45);\r
+ break;\r
+\r
+#if 0\r
+ case 48:\r
+ lumpneeded[PITLUMP] = true;\r
+ SpawnWarp(x,y,0);\r
+ break;\r
+#endif\r
+ case 49: // chest\r
+ if (gcolor == 0x0101)\r
+ lumpneeded[WATERCHESTLUMP] = true;\r
+ else\r
+ lumpneeded[CHESTLUMP] = true;\r
+ SpawnBonus(x,y,B_CHEST);\r
+ break;\r
+\r
+ case 50:\r
+ lumpneeded[TREELUMP] = true;\r
+ SpawnTree(x,y);\r
+ break;\r
+\r
+ case 51:\r
+ lumpneeded[BUNNYLUMP] = true;\r
+ SpawnBunny(x,y);\r
+ break;\r
+\r
+ case 52:\r
+ lumpneeded[ARCH1LUMP] = true;\r
+ SpawnArch(x,y,1);\r
+ break;\r
+\r
+ case 53:\r
+ lumpneeded[ANTHILLLUMP] = true;\r
+ SpawnWarp(x,y,0);\r
+ break;\r
+\r
+ case 54:\r
+ lumpneeded[COLUMNLUMP] = true;\r
+ SpawnMiscObjects(x,y,1); //1=column,2=sulphur hole,3=fire pot,4=fountain\r
+ break;\r
+\r
+ case 55:\r
+ lumpneeded[SULPHURGASLUMP] = true;\r
+ SpawnMiscObjects(x,y,2);\r
+ break;\r
+\r
+ case 56:\r
+ lumpneeded[FIREPOTLUMP] = true;\r
+ SpawnMiscObjects(x,y,3);\r
+ break;\r
+\r
+ case 57:\r
+ lumpneeded[ARCH13LUMP] = true;\r
+ SpawnArch(x,y,13);\r
+ break;\r
+\r
+ case 58:\r
+ lumpneeded[FOUNTAINLUMP] = true;\r
+ SpawnMiscObjects(x,y,4);\r
+ break;\r
+\r
+ case 59:\r
+ lumpneeded[FORCEFIELDLUMP] = true;\r
+ SpawnForceField(x,y);\r
+ break;\r
+\r
+ case 60:\r
+ lumpneeded[ARCH2LUMP] = true;\r
+ SpawnArch(x,y,2);\r
+ break;\r
+\r
+ case 61:\r
+ lumpneeded[ARCH3LUMP] = true;\r
+ SpawnArch(x,y,3);\r
+ break;\r
+\r
+ case 62:\r
+ lumpneeded[ARCH4LUMP] = true;\r
+ SpawnArch(x,y,4);\r
+ break;\r
+\r
+ case 63:\r
+ lumpneeded[ARCH5LUMP] = true;\r
+ SpawnArch(x,y,5);\r
+ break;\r
+\r
+ case 64:\r
+ lumpneeded[ARCH6LUMP] = true;\r
+ SpawnArch(x,y,6);\r
+ break;\r
+\r
+ case 65:\r
+ lumpneeded[SKELHANGLUMP] = true;\r
+ lumpneeded[SKELETONLUMP] = true;\r
+ SpawnSkeletonHanging(x,y);\r
+ break;\r
+\r
+ case 66:\r
+ lumpneeded[ARCH12LUMP] = true;\r
+ SpawnArch(x,y,12);\r
+ break;\r
+\r
+ case 67:\r
+ lumpneeded[ARCH7LUMP] = true;\r
+ SpawnArch(x,y,7);\r
+ break;\r
+\r
+ case 68:\r
+ lumpneeded[ARCH8LUMP] = true;\r
+ SpawnArch(x,y,8);\r
+ break;\r
+\r
+ case 69:\r
+ lumpneeded[ARCH9LUMP] = true;\r
+ SpawnArch(x,y,9);\r
+ break;\r
+\r
+ case 70:\r
+ lumpneeded[ARCH10LUMP] = true;\r
+ SpawnArch(x,y,10);\r
+ break;\r
+\r
+ case 71:\r
+ lumpneeded[ARCH11LUMP] = true;\r
+ SpawnArch(x,y,11);\r
+ break;\r
+ }\r
+ }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= ScanText\r
+=\r
+==================\r
+*/\r
+\r
+void ScanText (void)\r
+{\r
+ int i;\r
+ char far *text;\r
+\r
+ text = (char _seg *)grsegs[LEVEL1TEXT+mapon];\r
+\r
+ textstarts[0] = 0;\r
+\r
+ for (i=1;i<=26;i++)\r
+ {\r
+ while (*text != '\n')\r
+ {\r
+ if (*text == '\r')\r
+ *text = 0;\r
+ text++;\r
+ }\r
+ text++;\r
+ textstarts[i] = FP_OFF(text);\r
+ }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= DrawEnterScreen\r
+=\r
+==================\r
+*/\r
+#if 0\r
+static char *levelnames[] =\r
+ {\r
+ "Programmers Test Map",\r
+ "The Garden of Tears",\r
+ "The Den of Zombies",\r
+ "The Mausoleum Grounds",\r
+ "The Main Floor of the Mausoleum",\r
+ "Mike's Blastable Passage",\r
+ "The Crypt of Nemesis the Undead",\r
+ "The Subterranean Vault",\r
+ "The Ancient Aqueduct",\r
+ "The Orc Mines",\r
+ "The Lair of the Troll",\r
+ "The Demon's Inferno",\r
+ "The Battleground of the Titans",\r
+ "The Coven of Mages",\r
+ "The Inner Sanctum",\r
+ "The Haunt of Nemesis",\r
+ "The Passage to the Surface",\r
+ "Big Jim's Domain",\r
+ "Nolan",\r
+ "19",\r
+ "20",\r
+ "21",\r
+ "22",\r
+ "23",\r
+ "24",\r
+ "25",\r
+ "26",\r
+ "27",\r
+ "28",\r
+ "29",\r
+ "30",\r
+ "31",\r
+ "32",\r
+ "33",\r
+ "34",\r
+ "35",\r
+ "36",\r
+ "37",\r
+ "38",\r
+ "39",\r
+ };\r
+#endif\r
+\r
+void DrawEnterScreen (void)\r
+{\r
+ int width;\r
+\r
+ bufferofs = displayofs = screenloc[screenpage];\r
+ VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0);\r
+// width = strlen(levelnames[gamestate.mapon]);\r
+ width = strlen("You enter a new area ...");\r
+ if (width < 20)\r
+ width = 20;\r
+ CenterWindow(width,3);\r
+ US_CPrint("\nYou enter a new area ...\n");\r
+// US_CPrint(levelnames[gamestate.mapon]);\r
+}\r
+\r
+//==========================================================================\r
+\r
+boolean tileneeded[NUMFLOORS];\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= CacheScaleds\r
+=\r
+==================\r
+*/\r
+\r
+void CacheScaleds (void)\r
+{\r
+ int i,j;\r
+ unsigned source,dest;\r
+\r
+ FreeUpMemory ();\r
+ CA_CacheGrChunk(LEVEL1TEXT+mapon);\r
+ ScanText ();\r
+\r
+//\r
+// make sure we are displaying screenpage 0\r
+//\r
+ if (screenpage)\r
+ {\r
+ source = screenloc[screenpage];\r
+ dest = screenloc[0];\r
+ VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);\r
+ screenpage = 0;\r
+ VW_SetScreen (dest,0);\r
+ displayofs = dest;\r
+ }\r
+\r
+//\r
+// cache wall pictures\r
+//\r
+ for (i=1;i<NUMFLOORS;i++)\r
+ if (tileneeded[i])\r
+ {\r
+ SetupScaleWall (walllight1[i]);\r
+ SetupScaleWall (walldark1[i]);\r
+ }\r
+\r
+//\r
+// cache the actor pictures\r
+//\r
+ for (i=0;i<NUMLUMPS;i++)\r
+ if (lumpneeded[i])\r
+ for (j=lumpstart[i];j<=lumpend[i];j++)\r
+ SetupScalePic(j);\r
+\r
+ source = screenloc[0];\r
+ for (i=1;i<=2;i++)\r
+ {\r
+ dest = screenloc[i];\r
+ VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);\r
+ }\r
+\r
+ screenpage = 1;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= SetupGameLevel\r
+=\r
+==================\r
+*/\r
+\r
+void SetupGameLevel ()\r
+{\r
+ int x,y,i,loop;\r
+ unsigned far *map,tile,far *spotptr,spot;\r
+ unsigned search_tile;\r
+ boolean exploding_walls_present = false;\r
+\r
+ memset (tileneeded,0,sizeof(tileneeded));\r
+//\r
+// randomize if not a demo\r
+//\r
+#if 0\r
+ if (DemoMode)\r
+ {\r
+ US_InitRndT(false);\r
+ gamestate.difficulty = gd_Normal;\r
+ }\r
+ else\r
+#endif\r
+ US_InitRndT(true);\r
+\r
+//\r
+// load the level\r
+//\r
+ CA_CacheMap (gamestate.mapon);\r
+\r
+ mapwidth = mapheaderseg[mapon]->width;\r
+ mapheight = mapheaderseg[mapon]->height;\r
+\r
+//\r
+// make a lookup table for the maps left edge\r
+//\r
+ spot = 0;\r
+ for (y=0;y<mapheight;y++)\r
+ {\r
+ farmapylookup[y] = spot;\r
+ spot += mapwidth;\r
+ }\r
+\r
+\r
+//\r
+// copy the wall data to a data segment array\r
+//\r
+ memset (tilemap,0,sizeof(tilemap));\r
+ memset (actorat,0,sizeof(actorat));\r
+ map = mapsegs[0];\r
+ spotptr = mapsegs[2];\r
+ for (y=0;y<mapheight;y++)\r
+ for (x=0;x<mapwidth;x++)\r
+ {\r
+ tile = *map++;\r
+\r
+ if (((*spotptr)>>8) == EXP_WALL_CODE)\r
+ {\r
+ exploding_walls_present = true;\r
+ }\r
+\r
+ if (tile<NUMFLOORS)\r
+ {\r
+#if 0\r
+ if (tile == WALL_SKELETON_CODE)\r
+ {\r
+ tileneeded[tile+1] = tileneeded[tile+2] = true;\r
+ tilemap[x][y] = tile;\r
+ }\r
+#endif\r
+ if ((tile == 66) || (tile == 67) || (tile == 68) || (tile == 69))\r
+ {\r
+ if ((tile == 66) || (tile == 67))\r
+ tileneeded[tile+2] = true;\r
+ tileneeded[21] = tileneeded[tile] = true;\r
+ tilemap[x][y] = tile;\r
+ }\r
+ else\r
+ if (tile != INVISIBLEWALL)\r
+ {\r
+ tileneeded[tile] = true;\r
+ tilemap[x][y] = tile;\r
+ if (ANIM_FLAGS(tile))\r
+ {\r
+ search_tile = tile+(char signed)ANIM_FLAGS(tile);\r
+\r
+ if (!tileneeded[search_tile])\r
+ while (search_tile != tile)\r
+ {\r
+ tileneeded[search_tile] = true;\r
+ if (ANIM_FLAGS(search_tile))\r
+ search_tile += (char signed)ANIM_FLAGS(search_tile);\r
+ else\r
+ TrashProg("Unending Tile Animation!");\r
+ }\r
+ }\r
+\r
+ }\r
+ if (tile>0)\r
+ (unsigned)actorat[x][y] = tile;\r
+ }\r
+ spotptr++;\r
+ }\r
+\r
+\r
+ //\r
+ // Mark any gfx chunks needed\r
+ //\r
+\r
+// CA_MarkGrChunk(NORTHICONSPR);\r
+// CA_CacheMarks(NULL);\r
+\r
+\r
+//\r
+// decide which graphics are needed and spawn actors\r
+//\r
+ zombie_base_delay = 0; // (1*60) + random(1*60);\r
+ ScanInfoPlane ();\r
+ _fmemset(wall_anim_pos,0,sizeof(wall_anim_pos));\r
+\r
+\r
+//\r
+// mark which exploding walls are needed ---- the check for floor color\r
+// is preformed in ScanInfoPlane.\r
+//\r
+\r
+ if (exploding_walls_present)\r
+ {\r
+ extern unsigned gnd_colors[];\r
+\r
+ if (gcolor == 0x0101)\r
+ tileneeded[WATEREXP] = tileneeded[WATEREXP+1] = tileneeded[WATEREXP+2] = true;\r
+ else\r
+ tileneeded[WALLEXP] = tileneeded[WALLEXP+1] = tileneeded[WALLEXP+2] = true;\r
+\r
+ }\r
+\r
+\r
+//\r
+// have the caching manager load and purge stuff to make sure all marks\r
+// are in memory\r
+//\r
+ CA_LoadAllSounds ();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= LatchDrawPic\r
+=\r
+=====================\r
+*/\r
+\r
+void LatchDrawPic (unsigned x, unsigned y, unsigned picnum)\r
+{\r
+ unsigned height, source, dest;\r
+ unsigned wide;\r
+\r
+ wide = pictable[picnum-STARTPICS].width;\r
+ height = pictable[picnum-STARTPICS].height;\r
+ dest = bufferofs + ylookup[y]+x;\r
+ source = latchpics[picnum-FIRSTLATCHPIC];\r
+\r
+ EGAWRITEMODE(1);\r
+ EGAMAPMASK(15);\r
+\r
+asm mov bx,[linewidth]\r
+asm sub bx,[wide]\r
+\r
+asm mov ax,[screenseg]\r
+asm mov es,ax\r
+asm mov ds,ax\r
+\r
+asm mov si,[source]\r
+asm mov di,[dest]\r
+asm mov dx,[height] // scan lines to draw\r
+asm mov ax,[wide]\r
+\r
+lineloop:\r
+asm mov cx,ax\r
+asm rep movsb\r
+asm add di,bx\r
+\r
+asm dec dx\r
+asm jnz lineloop\r
+\r
+asm mov ax,ss\r
+asm mov ds,ax // restore turbo's data segment\r
+\r
+ EGAWRITEMODE(0);\r
+}\r
+\r
+#if USE_STRIPS\r
+\r
+//--------------------------------------------------------------------------\r
+// LatchDrawPicStrip() - srcoff is distance into source file (in PIXELS!)\r
+//--------------------------------------------------------------------------\r
+void LatchDrawPicStrip (unsigned x, unsigned y, unsigned picnum, unsigned srcoff)\r
+{\r
+ unsigned wide, height, source, dest, shift, srcmod;\r
+\r
+ shift = (srcoff & 7) >> 1;\r
+ srcoff >>= 3;\r
+ wide = pictable[picnum-STARTPICS].width;\r
+ srcmod = wide - linewidth + (shift != 3);\r
+ if (wide > linewidth)\r
+ wide = linewidth;\r
+ height = pictable[picnum-STARTPICS].height;\r
+ dest = bufferofs + ylookup[y]+x;\r
+\r
+ picnum = ((picnum - (FIRSTSTRIPPIC+1)) >> 2) + (shift);\r
+ source = latchpics[(FIRSTSTRIPPIC-FIRSTLATCHPIC+1)+picnum];\r
+\r
+ EGAWRITEMODE(1);\r
+ EGAMAPMASK(15);\r
+\r
+asm mov bx,[linewidth]\r
+asm sub bx,[wide]\r
+\r
+asm mov ax,[screenseg]\r
+asm mov es,ax\r
+asm mov ds,ax\r
+\r
+asm mov si,[source]\r
+asm add si,[srcoff]\r
+asm mov di,[dest]\r
+asm mov dx,[height] // scan lines to draw\r
+asm mov ax,[wide]\r
+\r
+lineloop:\r
+asm mov cx,ax\r
+asm rep movsb\r
+asm add di,bx\r
+asm add si,[srcmod]\r
+\r
+asm dec dx\r
+asm jnz lineloop\r
+\r
+asm mov ax,ss\r
+asm mov ds,ax // restore turbo's data segment\r
+\r
+ EGAWRITEMODE(0);\r
+}\r
+\r
+#endif\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= Victory\r
+=\r
+=====================\r
+*/\r
+\r
+void Victory (boolean playsounds)\r
+{\r
+ struct Shape shape;\r
+\r
+ if (playsounds)\r
+ {\r
+ SD_PlaySound (GETBOLTSND);\r
+ SD_WaitSoundDone ();\r
+ SD_PlaySound (GETNUKESND);\r
+ SD_WaitSoundDone ();\r
+ SD_PlaySound (GETPOTIONSND);\r
+ SD_WaitSoundDone ();\r
+ SD_PlaySound (GETKEYSND);\r
+ SD_WaitSoundDone ();\r
+ SD_PlaySound (GETSCROLLSND);\r
+ SD_WaitSoundDone ();\r
+ SD_PlaySound (GETPOINTSSND);\r
+ }\r
+\r
+ FreeUpMemory();\r
+\r
+ if (!screenfaded)\r
+ VW_FadeOut();\r
+\r
+ NormalScreen ();\r
+\r
+ screenpage = bufferofs = 0;\r
+\r
+ CA_CacheGrChunk (FINALEPIC);\r
+ UNMARKGRCHUNK(FINALEPIC);\r
+ VW_DrawPic(0, 0, FINALEPIC);\r
+\r
+ VW_FadeIn();\r
+}\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+===================\r
+=\r
+= Died\r
+=\r
+===================\r
+*/\r
+\r
+void Died (void)\r
+{\r
+ unsigned page1,page2;\r
+//\r
+// fizzle fade screen to grey\r
+//\r
+ FreeUpMemory ();\r
+ SD_PlaySound (GAMEOVERSND);\r
+ bufferofs = screenloc[(screenpage+1)%3];\r
+ DisplayMsg("Though fallen, your Spirit ...",NULL);\r
+// LatchDrawPic(0,0,DEADPIC);\r
+// FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);\r
+ IN_ClearKeysDown();\r
+ while (!Keyboard[sc_Enter]);\r
+// IN_Ack();\r
+ VW_SetScreen (bufferofs,0);\r
+ VW_ColorBorder(0);\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= NormalScreen\r
+=\r
+===================\r
+*/\r
+\r
+void NormalScreen (void)\r
+{\r
+ VW_SetSplitScreen (200);\r
+ bufferofs = displayofs = SCREEN1START;\r
+ VW_Bar(0,0,320,200,0);\r
+ bufferofs = SCREEN2START;\r
+ VW_Bar(0,0,320,200,0);\r
+ VW_SetScreen (displayofs,0);\r
+ splitscreen = false;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= DrawPlayScreen\r
+=\r
+===================\r
+*/\r
+\r
+void DrawPlayScreen (void)\r
+{\r
+ int i,j,p,m;\r
+\r
+ screenpage = 0;\r
+\r
+ bufferofs = 0;\r
+ VW_Bar (0,0,320,STATUSLINES,0);\r
+ for (i=0;i<3;i++)\r
+ {\r
+ bufferofs = screenloc[i];\r
+ VW_Bar (0,0,320,VIEWHEIGHT,0);\r
+ }\r
+\r
+ splitscreen = true;\r
+ VW_SetSplitScreen(120);\r
+ VW_SetScreen(screenloc[0],0);\r
+\r
+ CA_CacheGrChunk (STATUSPIC);\r
+\r
+ bufferofs = 0;\r
+ VW_DrawPic (0,0,STATUSPIC);\r
+\r
+ grneeded[STATUSPIC] &= ~ca_levelbit;\r
+ MM_SetPurge(&grsegs[STATUSPIC],3);\r
+\r
+ RedrawStatusWindow ();\r
+ bufferofs = displayofs = screenloc[0];\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= LoadLatchMem\r
+=\r
+===================\r
+*/\r
+\r
+unsigned latchmemavail;\r
+\r
+void LoadLatchMem (void)\r
+{\r
+ static unsigned base_destoff=0;\r
+ static int base_numpics=0;\r
+ int i,j,p,m,numpics;\r
+ byte far *src, far *dest;\r
+ unsigned destoff;\r
+\r
+ EGAWRITEMODE(0);\r
+\r
+//\r
+// draw some pics into latch memory\r
+//\r
+\r
+ if (!base_numpics)\r
+ {\r
+\r
+//\r
+// tile 8s\r
+//\r
+ latchpics[0] = freelatch;\r
+ src = (byte _seg *)grsegs[STARTTILE8];\r
+ dest = MK_FP(0xa000,freelatch);\r
+\r
+ for (i=0;i<NUMTILE8;i++)\r
+ {\r
+ for (p=0;p<4;p++)\r
+ {\r
+ m = 1<<p;\r
+ asm mov dx,SC_INDEX\r
+ asm mov al,SC_MAPMASK\r
+ asm mov ah,[BYTE PTR m]\r
+ asm out dx,ax\r
+ for (j=0;j<8;j++)\r
+ *(dest+j)=*src++;\r
+ }\r
+ dest+=8;\r
+ }\r
+\r
+//\r
+// tile 16s\r
+//\r
+ src = (byte _seg *)grsegs[STARTTILE16];\r
+\r
+ for (i=0;i<NUMTILE16;i++)\r
+ {\r
+ CA_CacheGrChunk (STARTTILE16+i);\r
+ src = (byte _seg *)grsegs[STARTTILE16+i];\r
+ if (src)\r
+ {\r
+ tileoffsets[i] = FP_OFF(dest);\r
+ for (p=0;p<4;p++)\r
+ {\r
+ m = 1<<p;\r
+ asm mov dx,SC_INDEX\r
+ asm mov al,SC_MAPMASK\r
+ asm mov ah,[BYTE PTR m]\r
+ asm out dx,ax\r
+ for (j=0;j<32;j++)\r
+ *(dest+j)=*src++;\r
+ }\r
+ dest+=32;\r
+ MM_FreePtr (&grsegs[STARTTILE16+i]);\r
+ UNMARKGRCHUNK(STARTTILE16+i);\r
+ }\r
+ else\r
+ tileoffsets[i] = 0;\r
+ }\r
+\r
+\r
+//\r
+// pics\r
+//\r
+ numpics=1;\r
+ destoff = FP_OFF(dest);\r
+ for (i=FIRSTLATCHPIC+1;i<FIRSTGROUNDPIC;i++)\r
+ {\r
+ latchpics[numpics++] = destoff;\r
+ CA_CacheGrChunk (i);\r
+ j = pictable[i-STARTPICS].width * pictable[i-STARTPICS].height;\r
+ VW_MemToScreen (grsegs[i],destoff,j,1);\r
+ destoff+=j;\r
+ MM_FreePtr (&grsegs[i]);\r
+ UNMARKGRCHUNK(i);\r
+ }\r
+\r
+ base_numpics = numpics;\r
+ base_destoff = destoff;\r
+\r
+ }\r
+\r
+ numpics = base_numpics;\r
+ destoff = base_destoff;\r
+\r
+#if USE_STRIPS\r
+//\r
+// ground pics\r
+//\r
+ numpics++;\r
+ for (i=FIRSTGROUNDPIC+1;i<FIRSTSTRIPPIC;i++)\r
+ {\r
+ int shape = (*groundcolor & 0xf0) - 16;\r
+\r
+ // Is current shape needed?\r
+ //\r
+ if (shape != (i-(FIRSTGROUNDPIC+1)))\r
+ {\r
+ numpics++;\r
+ continue;\r
+ }\r
+\r
+ latchpics[numpics++] = destoff;\r
+ CA_CacheGrChunk (i);\r
+ j = pictable[i-STARTPICS].width * pictable[i-STARTPICS].height;\r
+ VW_MemToScreen (grsegs[i],destoff,j,1);\r
+ destoff+=j;\r
+ MM_FreePtr (&grsegs[i]);\r
+ UNMARKGRCHUNK(i);\r
+ }\r
+\r
+\r
+//\r
+// 'parallax' strips - used in place of a sky color\r
+//\r
+// Under current setup, each strip takes about 7k in latch memory.\r
+// To create 2 pixel scrolling, 4 strips are needed, that's 28k of\r
+// latch memory needed to produce this effect.\r
+//\r
+ numpics++;\r
+ for (i=FIRSTSTRIPPIC+1;i<FIRSTSCALEPIC;i++)\r
+ {\r
+ memptr work;\r
+ unsigned workdest,stripsize,planesize;\r
+ short loop,pic=i-STARTPICS;\r
+ int shape = (*skycolor & 0xf0) - 16;\r
+\r
+ // Is current shape needed?\r
+ //\r
+ if (shape != (i-(FIRSTSTRIPPIC+1)))\r
+ {\r
+ numpics++;\r
+ continue;\r
+ }\r
+\r
+ // CAL_ShiftSprite() works with the SRC and DST in the same\r
+ // segment. So we must allocate memory for two strips, and\r
+ // move the base strip into that segment. Then we can use the\r
+ // 2nd half of that memory for each shifted strip.\r
+ //\r
+ CA_CacheGrChunk (i);\r
+ planesize = (pictable[pic].width+1) * pictable[pic].height;\r
+ stripsize = planesize * 4;\r
+// MM_GetPtr(&work,(stripsize*2)+0000);\r
+ MM_GetPtr(&work,65536);\r
+ movedata((unsigned)grsegs[i],0,(unsigned)work,0,stripsize);\r
+ workdest = 32768; //(stripsize+15) & 0xFFF0;\r
+\r
+ // Free base strip\r
+ //\r
+ MM_FreePtr (&grsegs[i]);\r
+ UNMARKGRCHUNK(i);\r
+\r
+ // Create three shifted strips and move 'em to latch!\r
+ //\r
+ for (loop=3; loop; loop--)\r
+ {\r
+ // Produce current shift for this strip\r
+ //\r
+ latchpics[numpics++] = destoff;\r
+ CAL_ShiftSprite ((unsigned)work,0,workdest,pictable[pic].width,\r
+ pictable[pic].height,loop*2,false);\r
+\r
+ // Copy this shift to latch memory\r
+ //\r
+ VW_MemToScreen ((memptr)((unsigned)work+(workdest>>4)),destoff,planesize,1);\r
+ destoff+=planesize;\r
+ }\r
+\r
+ // Copy unshifted strip to latch\r
+ //\r
+ latchpics[numpics++] = destoff;\r
+ planesize = pictable[pic].width * pictable[pic].height;\r
+ VW_MemToScreen (work,destoff,planesize,1);\r
+ destoff+=planesize;\r
+\r
+ // Free work buffer\r
+ //\r
+ MM_FreePtr(&work);\r
+ }\r
+#endif\r
+\r
+// Keep track of how much latch memory we have...\r
+//\r
+ latchmemavail = 65535-destoff;\r
+\r
+ EGAMAPMASK(15);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= FizzleOut\r
+=\r
+===================\r
+*/\r
+\r
+void FizzleOut (int showlevel)\r
+{\r
+ unsigned page1,page2;\r
+//\r
+// fizzle fade screen to grey\r
+//\r
+ bufferofs = screenloc[(screenpage+1)%3];\r
+ if (showlevel)\r
+ DrawEnterScreen ();\r
+ FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= FreeUpMemory\r
+=\r
+====================\r
+*/\r
+\r
+void FreeUpMemory (void)\r
+{\r
+ int i;\r
+\r
+ for (i=0;i<NUMSCALEPICS;i++)\r
+ if (shapedirectory[i])\r
+ MM_SetPurge (&(memptr)shapedirectory[i],3);\r
+\r
+ for (i=0;i<NUMSCALEWALLS;i++)\r
+ if (walldirectory[i])\r
+ MM_SetPurge (&(memptr)walldirectory[i],3);\r
+}\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+\r
+/*\r
+==================\r
+=\r
+= DrawHighScores\r
+=\r
+==================\r
+*/\r
+\r
+void DrawHighScores(void)\r
+{\r
+ char buffer[16],*str;\r
+ word i,j,\r
+ w,h,\r
+ x,y;\r
+ HighScore *s;\r
+\r
+\r
+ CA_CacheGrChunk (HIGHSCORESPIC);\r
+ VWB_DrawPic (0,0,HIGHSCORESPIC);\r
+ MM_SetPurge (&grsegs[HIGHSCORESPIC],3);\r
+ UNMARKGRCHUNK(HIGHSCORESPIC);\r
+\r
+ for (i = 0,s = Scores;i < MaxScores;i++,s++)\r
+ {\r
+ PrintY = 68 + (16 * i);\r
+\r
+ //\r
+ // name\r
+ //\r
+ PrintX = 60;\r
+ US_Print(s->name);\r
+\r
+ //\r
+ // level\r
+ //\r
+ ultoa(s->completed,buffer,10);\r
+ for (str = buffer;*str;str++)\r
+ *str = *str + (129 - '0'); // Used fixed-width numbers (129...)\r
+ USL_MeasureString(buffer,&w,&h);\r
+ PrintX = (25 * 8) - 8 - w;\r
+ US_Print(buffer);\r
+\r
+ //\r
+ // score\r
+ //\r
+ ultoa(s->score,buffer,10);\r
+ for (str = buffer;*str;str++)\r
+ *str = *str + (129 - '0'); // Used fixed-width numbers (129...)\r
+ USL_MeasureString(buffer,&w,&h);\r
+ PrintX = (34 * 8) - 8 - w;\r
+ US_Print(buffer);\r
+ }\r
+\r
+ fontcolor = F_BLACK;\r
+}\r
+\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= CheckHighScore\r
+=\r
+=======================\r
+*/\r
+\r
+void CheckHighScore (long score,word other)\r
+{\r
+ word i,j;\r
+ int n;\r
+ HighScore myscore;\r
+\r
+ strcpy(myscore.name,"");\r
+ myscore.score = score;\r
+ myscore.completed = other;\r
+\r
+ for (i = 0,n = -1;i < MaxScores;i++)\r
+ {\r
+ if\r
+ (\r
+ (myscore.score > Scores[i].score)\r
+ || (\r
+ (myscore.score == Scores[i].score)\r
+ && (myscore.completed > Scores[i].completed)\r
+ )\r
+ )\r
+ {\r
+ for (j = MaxScores;--j > i;)\r
+ Scores[j] = Scores[j - 1];\r
+ Scores[i] = myscore;\r
+ n = i;\r
+ HighScoresDirty = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (n != -1)\r
+ {\r
+ //\r
+ // got a high score\r
+ //\r
+ DrawHighScores ();\r
+ PrintY = 68 + (16 * n);\r
+ PrintX = 60;\r
+ US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100);\r
+ }\r
+}\r
+\r
+#endif\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= GameLoop\r
+=\r
+===================\r
+*/\r
+\r
+void GameLoop (void)\r
+{\r
+ boolean wait = false;\r
+ int i,xl,yl,xh,yh;\r
+ char num[20];\r
+#ifdef PROFILE\r
+ clock_t start,end;\r
+#endif\r
+\r
+ DrawPlayScreen ();\r
+ IN_ClearKeysDown();\r
+\r
+restart:\r
+ if (!loadedgame)\r
+ {\r
+ gamestate.difficulty = restartgame;\r
+ restartgame = gd_Continue;\r
+ DrawEnterScreen ();\r
+ if (gamestate.mapon != 8)\r
+ fizzlein = true;\r
+ wait = true;\r
+ }\r
+\r
+ do\r
+ {\r
+ playstate = gd_Continue;\r
+ if (!loadedgame)\r
+ SetupGameLevel ();\r
+ else\r
+ loadedgame = false;\r
+\r
+ FreeUpMemory();\r
+ LoadLatchMem();\r
+ CacheScaleds ();\r
+\r
+ if (EASYMODEON)\r
+ DisplaySMsg("*** NOVICE ***", NULL);\r
+ else\r
+ DisplaySMsg("*** WARRIOR ***", NULL);\r
+\r
+ status_delay = 250;\r
+\r
+ RedrawStatusWindow();\r
+ if (wait)\r
+ {\r
+ VW_WaitVBL(120);\r
+ wait = false;\r
+ }\r
+\r
+#ifdef PROFILE\r
+start = clock();\r
+while (start == clock());\r
+start++;\r
+#endif\r
+\r
+ PlayLoop ();\r
+\r
+#ifdef PROFILE\r
+end = clock();\r
+itoa(end-start,str,10);\r
+ Quit (str);\r
+#endif\r
+\r
+\r
+ switch (playstate)\r
+ {\r
+ case ex_abort:\r
+ FreeUpMemory ();\r
+ return;\r
+ case ex_resetgame:\r
+ NewGame();\r
+ case ex_loadedgame:\r
+ case ex_warped:\r
+ FreeUpMemory();\r
+ if (playstate != ex_resetgame)\r
+ DisplayMsg(" ", NULL);\r
+ DisplaySMsg(" ", NULL);\r
+ goto restart;\r
+ case ex_victorious:\r
+ screenpage = 0;\r
+ bufferofs = 0;\r
+ status_flag = 0;\r
+ return;\r
+ }\r
+\r
+ } while (1);\r
+\r
+}\r
+\r
+\r
+#if 0\r
+//\r
+// make wall pictures purgable\r
+//\r
+ for (i=0;i<NUMSCALEWALLS;i++)\r
+ if (walldirectory[i])\r
+ MM_SetPurge (&(memptr)walldirectory[i],3);\r
+\r
+\r
+//\r
+// cache wall pictures back in\r
+//\r
+ for (i=1;i<NUMFLOORS;i++)\r
+ if (tileneeded[i])\r
+ {\r
+ SetupScaleWall (walllight1[i]);\r
+ SetupScaleWall (walllight2[i]);\r
+ SetupScaleWall (walldark1[i]);\r
+ SetupScaleWall (walldark2[i]);\r
+ }\r
+#endif\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_MAIN.C\r
+\r
+#define CATALOG\r
+\r
+\r
+#include <time.h>\r
+#include <stdarg.h>\r
+\r
+#include "DEF.H"\r
+#include "GELIB.H"\r
+#pragma hdrstop\r
+#include <dir.h>\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+PresenterInfo MainHelpText;\r
+\r
+GameDiff restartgame;\r
+boolean loadedgame,abortgame,ingame;\r
+\r
+\r
+memptr scalesegs[NUMPICS];\r
+char str[80],str2[20];\r
+unsigned tedlevelnum;\r
+boolean tedlevel;\r
+gametype gamestate;\r
+exittype playstate;\r
+char SlowMode = 0;\r
+int starting_level;\r
+\r
+//extern unsigned scolor,gcolor; //NPM\r
+\r
+short NumGames=0;\r
+unsigned Flags=0;\r
+\r
+boolean LoadShapes = true;\r
+boolean EASYMODEON = false;\r
+\r
+void DisplayIntroText(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+// JAB Hack begin\r
+#define MyInterrupt 0x60\r
+void interrupt (*intaddr)();\r
+void interrupt (*oldintaddr)();\r
+ char *JHParmStrings[] = {"no386",nil};\r
+\r
+void\r
+jabhack(void)\r
+{\r
+extern void far jabhack2(void);\r
+extern int far CheckIs386(void);\r
+\r
+ int i;\r
+\r
+ oldintaddr = getvect(MyInterrupt);\r
+\r
+ for (i = 1;i < _argc;i++)\r
+ if (US_CheckParm(_argv[i],JHParmStrings) == 0)\r
+ return;\r
+\r
+ if (CheckIs386())\r
+ {\r
+ jabhack2();\r
+ setvect(MyInterrupt,intaddr);\r
+ }\r
+}\r
+\r
+void\r
+jabunhack(void)\r
+{\r
+ setvect(MyInterrupt,oldintaddr);\r
+}\r
+// JAB Hack end\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= NewGame\r
+=\r
+= Set up new game to start from the beginning\r
+=\r
+=====================\r
+*/\r
+\r
+void NewGame (void)\r
+{\r
+ if (!loadedgame)\r
+ {\r
+ memset (&gamestate,0,sizeof(gamestate));\r
+ gamestate.mapon = starting_level;\r
+ gamestate.body = MAXBODY;\r
+ }\r
+\r
+ BGFLAGS = BGF_NOT_LIGHTNING;\r
+ Flags &= FL_CLEAR;\r
+\r
+ boltsleft = bolttimer = 0;\r
+\r
+// memset (gamestate.levels,-1,sizeof(gamestate.levels));\r
+}\r
+\r
+//===========================================================================\r
+\r
+#define RLETAG 0xABCD\r
+\r
+/*\r
+==================\r
+=\r
+= SaveTheGame\r
+=\r
+==================\r
+*/\r
+\r
+boolean SaveTheGame(int file)\r
+{\r
+ word i,compressed,expanded;\r
+ objtype *o;\r
+ memptr bigbuffer;\r
+\r
+ // save the sky and ground colors\r
+ if (!CA_FarWrite(file,(void far *)&skycolor,sizeof(skycolor)))\r
+ return(false);\r
+ if (!CA_FarWrite(file,(void far *)&groundcolor,sizeof(groundcolor)))\r
+ return(false);\r
+\r
+ if (!CA_FarWrite(file,(void far *)&FreezeTime,sizeof(FreezeTime)))\r
+ return(false);\r
+\r
+ if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate)))\r
+ return(false);\r
+\r
+ if (!CA_FarWrite(file,(void far *)&EASYMODEON,sizeof(EASYMODEON)))\r
+ return(false);\r
+\r
+ expanded = mapwidth * mapheight * 2;\r
+ MM_GetPtr (&bigbuffer,expanded);\r
+\r
+ for (i = 0;i < 3;i+=2) // Write planes 0 and 2\r
+ {\r
+//\r
+// leave a word at start of compressed data for compressed length\r
+//\r
+ compressed = (unsigned)CA_RLEWCompress ((unsigned huge *)mapsegs[i]\r
+ ,expanded,((unsigned huge *)bigbuffer)+1,RLETAG);\r
+\r
+ *(unsigned huge *)bigbuffer = compressed;\r
+\r
+ if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) )\r
+ {\r
+ MM_FreePtr (&bigbuffer);\r
+ return(false);\r
+ }\r
+ }\r
+\r
+ for (o = player;o;o = o->next)\r
+ if (!CA_FarWrite(file,(void far *)o,sizeof(objtype)))\r
+ {\r
+ MM_FreePtr (&bigbuffer);\r
+ return(false);\r
+ }\r
+\r
+ MM_FreePtr (&bigbuffer);\r
+\r
+ return(true);\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= LoadTheGame\r
+=\r
+==================\r
+*/\r
+\r
+boolean LoadTheGame(int file)\r
+{\r
+ unsigned i,x,y;\r
+ objtype *obj,*prev,*next,*followed;\r
+ unsigned compressed,expanded;\r
+ unsigned far *map,tile;\r
+ memptr bigbuffer;\r
+\r
+ screenpage = 0;\r
+ FreeUpMemory();\r
+\r
+ playstate = ex_loadedgame;\r
+ // load the sky and ground colors\r
+ if (!CA_FarRead(file,(void far *)&skycolor,sizeof(skycolor)))\r
+ return(false);\r
+ if (!CA_FarRead(file,(void far *)&groundcolor,sizeof(groundcolor)))\r
+ return(false);\r
+\r
+ if (!CA_FarRead(file,(void far *)&FreezeTime,sizeof(FreezeTime)))\r
+ return(false);\r
+\r
+ if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate)))\r
+ return(false);\r
+\r
+ if (!CA_FarRead(file,(void far *)&EASYMODEON,sizeof(EASYMODEON)))\r
+ return(false);\r
+\r
+ SetupGameLevel (); // load in and cache the base old level\r
+\r
+ if (!FindFile(Filename,"SAVE GAME",-1))\r
+ Quit("Error: Can't find saved game file!");\r
+\r
+ expanded = mapwidth * mapheight * 2;\r
+ MM_GetPtr (&bigbuffer,expanded);\r
+\r
+ for (i = 0;i < 3;i+=2) // Read planes 0 and 2\r
+ {\r
+ if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) )\r
+ {\r
+ MM_FreePtr (&bigbuffer);\r
+ return(false);\r
+ }\r
+\r
+ if (!CA_FarRead(file,(void far *)bigbuffer,compressed) )\r
+ {\r
+ MM_FreePtr (&bigbuffer);\r
+ return(false);\r
+ }\r
+\r
+ CA_RLEWexpand ((unsigned huge *)bigbuffer,\r
+ (unsigned huge *)mapsegs[i],expanded,RLETAG);\r
+ }\r
+\r
+ MM_FreePtr (&bigbuffer);\r
+//\r
+// copy the wall data to a data segment array again, to handle doors and\r
+// bomb walls that are allready opened\r
+//\r
+ memset (tilemap,0,sizeof(tilemap));\r
+ memset (actorat,0,sizeof(actorat));\r
+ map = mapsegs[0];\r
+ for (y=0;y<mapheight;y++)\r
+ for (x=0;x<mapwidth;x++)\r
+ {\r
+ tile = *map++;\r
+ if (tile<NUMFLOORS)\r
+ {\r
+ if (tile != INVISIBLEWALL)\r
+ tilemap[x][y] = tile;\r
+ if (tile>0)\r
+ (unsigned)actorat[x][y] = tile;\r
+ }\r
+ }\r
+\r
+\r
+ // Read the object list back in - assumes at least one object in list\r
+\r
+ InitObjList ();\r
+ new = player;\r
+ while (true)\r
+ {\r
+ prev = new->prev;\r
+ next = new->next;\r
+ if (!CA_FarRead(file,(void far *)new,sizeof(objtype)))\r
+ return(false);\r
+ followed = new->next;\r
+ new->prev = prev;\r
+ new->next = next;\r
+ actorat[new->tilex][new->tiley] = new; // drop a new marker\r
+\r
+ if (followed)\r
+ GetNewObj (false);\r
+ else\r
+ break;\r
+ }\r
+\r
+ return(true);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= ResetGame\r
+=\r
+==================\r
+*/\r
+\r
+void ResetGame(void)\r
+{\r
+ NewGame ();\r
+\r
+ ca_levelnum--;\r
+ ca_levelbit>>=1;\r
+ CA_ClearMarks();\r
+ ca_levelbit<<=1;\r
+ ca_levelnum++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= ShutdownId\r
+=\r
+= Shuts down all ID_?? managers\r
+=\r
+==========================\r
+*/\r
+\r
+void ShutdownId (void)\r
+{\r
+ US_Shutdown ();\r
+#ifndef PROFILE\r
+ SD_Shutdown ();\r
+ IN_Shutdown ();\r
+#endif\r
+ VW_Shutdown ();\r
+ CA_Shutdown ();\r
+ MM_Shutdown ();\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= InitGame\r
+=\r
+= Load a few things right away\r
+=\r
+==========================\r
+*/\r
+\r
+void InitGame (void)\r
+{\r
+ unsigned segstart,seglength;\r
+ int i,x,y;\r
+ unsigned *blockstart;\r
+\r
+// US_TextScreen();\r
+\r
+ MM_Startup ();\r
+ VW_Startup ();\r
+#ifndef PROFILE\r
+ IN_Startup ();\r
+ SD_Startup ();\r
+#endif\r
+ US_Startup ();\r
+\r
+// US_UpdateTextScreen();\r
+\r
+ CA_Startup ();\r
+ US_Setup ();\r
+\r
+ US_SetLoadSaveHooks(LoadTheGame,SaveTheGame,ResetGame);\r
+\r
+\r
+//\r
+// load in and lock down some basic chunks\r
+//\r
+\r
+ CA_ClearMarks ();\r
+\r
+ CA_MarkGrChunk(STARTFONT);\r
+ CA_MarkGrChunk(STARTTILE8);\r
+ CA_MarkGrChunk(STARTTILE8M);\r
+ CA_MarkGrChunk(HAND1PICM);\r
+\r
+ CA_MarkGrChunk(NORTHICONSPR);\r
+ CA_CacheMarks (NULL);\r
+\r
+ MM_SetLock (&grsegs[STARTFONT],true);\r
+ MM_SetLock (&grsegs[STARTTILE8],true);\r
+ MM_SetLock (&grsegs[STARTTILE8M],true);\r
+ MM_SetLock (&grsegs[HAND1PICM],true);\r
+\r
+ fontcolor = WHITE;\r
+\r
+\r
+//\r
+// build some tables\r
+//\r
+ for (i=0;i<MAPSIZE;i++)\r
+ nearmapylookup[i] = &tilemap[0][0]+MAPSIZE*i;\r
+\r
+ for (i=0;i<PORTTILESHIGH;i++)\r
+ uwidthtable[i] = UPDATEWIDE*i;\r
+\r
+ blockstart = &blockstarts[0];\r
+ for (y=0;y<UPDATEHIGH;y++)\r
+ for (x=0;x<UPDATEWIDE;x++)\r
+ *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+\r
+ BuildTables (); // 3-d tables\r
+\r
+ SetupScaling ();\r
+\r
+#ifndef PROFILE\r
+// US_FinishTextScreen();\r
+#endif\r
+\r
+#if 0\r
+//\r
+// reclaim the memory from the linked in text screen\r
+//\r
+ segstart = FP_SEG(&introscn);\r
+ seglength = 4000/16;\r
+ if (FP_OFF(&introscn))\r
+ {\r
+ segstart++;\r
+ seglength--;\r
+ }\r
+ MML_UseSpace (segstart,seglength);\r
+#endif\r
+\r
+ VW_SetScreenMode (GRMODE);\r
+ ge_textmode = false;\r
+// VW_ColorBorder (3);\r
+ VW_ClearVideo (BLACK);\r
+\r
+//\r
+// initialize variables\r
+//\r
+ updateptr = &update[0];\r
+ *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;\r
+ bufferofs = 0;\r
+ displayofs = 0;\r
+ VW_SetLineWidth(SCREENWIDTH);\r
+}\r
+\r
+//===========================================================================\r
+\r
+void clrscr (void); // can't include CONIO.H because of name conflicts...\r
+\r
+/*\r
+==========================\r
+=\r
+= Quit\r
+=\r
+==========================\r
+*/\r
+\r
+void Quit (char *error, ...)\r
+{\r
+ short exit_code=0;\r
+ unsigned finscreen;\r
+\r
+ va_list ap;\r
+\r
+ va_start(ap,error);\r
+\r
+#ifndef CATALOG\r
+ if (!error)\r
+ {\r
+ CA_SetAllPurge ();\r
+ CA_CacheGrChunk (PIRACY);\r
+ finscreen = (unsigned)grsegs[PIRACY];\r
+ }\r
+#endif\r
+\r
+ ShutdownId ();\r
+\r
+ if (error && *error)\r
+ {\r
+ vprintf(error,ap);\r
+ exit_code = 1;\r
+ }\r
+#ifndef CATALOG\r
+ else\r
+ if (!NoWait)\r
+ {\r
+ movedata (finscreen,0,0xb800,0,4000);\r
+ bioskey (0);\r
+ }\r
+#endif\r
+\r
+ va_end(ap);\r
+\r
+#ifndef CATALOG\r
+ if (!error)\r
+ {\r
+ _argc = 2;\r
+ _argv[1] = "LAST.SHL";\r
+ _argv[2] = "ENDSCN.SCN";\r
+ _argv[3] = NULL;\r
+ if (execv("LOADSCN.EXE", _argv) == -1)\r
+ {\r
+ clrscr();\r
+ puts("Couldn't find executable LOADSCN.EXE.\n");\r
+ exit(1);\r
+ }\r
+ }\r
+#endif\r
+\r
+ exit(exit_code);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= TEDDeath\r
+=\r
+==================\r
+*/\r
+\r
+void TEDDeath(void)\r
+{\r
+ ShutdownId();\r
+ execlp("TED5.EXE","TED5.EXE","/LAUNCH",NULL);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= DisplayDepartment\r
+=\r
+====================\r
+*/\r
+void DisplayDepartment(char *text)\r
+{\r
+ short temp;\r
+\r
+// bufferofs = 0;\r
+ PrintY = 1;\r
+ WindowX = 0;\r
+ WindowW = 320;\r
+\r
+ VW_Bar(WindowX,PrintY+1,WindowW,7,7);\r
+ temp = fontcolor;\r
+ fontcolor = 2;\r
+ US_CPrintLine (text);\r
+ fontcolor = temp;\r
+}\r
+\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= DemoLoop\r
+=\r
+=====================\r
+*/\r
+\r
+void DemoLoop (void)\r
+{\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+// main game cycle\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+ displayofs = bufferofs = 0;\r
+ VW_Bar (0,0,320,200,0);\r
+ VW_SetScreen(0,0);\r
+\r
+//\r
+// Read in all the graphic images needed for the title sequence\r
+//\r
+ VW_WaitVBL(1);\r
+ IN_ReadControl(0,&control);\r
+\r
+// set EASYMODE\r
+//\r
+ if (stricmp(_argv[2], "1") == 0)\r
+ EASYMODEON = true;\r
+ else\r
+ EASYMODEON = false;\r
+\r
+// restore game\r
+//\r
+ if (stricmp(_argv[3], "1") == 0)\r
+ {\r
+ VW_FadeOut();\r
+ bufferofs = displayofs = 0;\r
+ VW_Bar(0,0,320,200,0);\r
+ if (GE_LoadGame())\r
+ {\r
+ loadedgame = true;\r
+ playstate = ex_loadedgame;\r
+ Keyboard[sc_Enter] = true;\r
+ VW_Bar(0,0,320,200,0);\r
+ ColoredPalette();\r
+ }\r
+ VW_Bar(0,0,320,200,0);\r
+ VW_FadeIn();\r
+ }\r
+\r
+ // Play a game\r
+ //\r
+ restartgame = gd_Normal;\r
+ NewGame();\r
+ GameLoop();\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// DisplayIntroText()\r
+//-------------------------------------------------------------------------\r
+void DisplayIntroText()\r
+{\r
+ PresenterInfo pi;\r
+\r
+#ifdef TEXT_PRESENTER\r
+ char *toptext = "You stand before the gate leading into the Towne "\r
+ "of Morbidity.... "\r
+ "^XX";\r
+\r
+ char *bottomtext = "Enter now boldly to defeat the evil Nemesis "\r
+ "deep inside the catacombs."\r
+ "\r
+ "^XX";\r
+#endif\r
+\r
+ char oldfontcolor=fontcolor;\r
+\r
+ fontcolor = 14;\r
+\r
+\r
+#ifdef TEXT_PRESENTER\r
+ pi.xl = 0;\r
+ pi.yl = 0;\r
+ pi.xh = 319;\r
+ pi.yh = 1;\r
+ pi.bgcolor = 0;\r
+ pi.script[0] = (char far *)toptext;\r
+ Presenter(&pi);\r
+\r
+ pi.yl = 160;\r
+ pi.yh = 161;\r
+ pi.script[0] = (char far *)bottomtext;\r
+ Presenter(&pi);\r
+\r
+#else\r
+ PrintY = 1;\r
+ PrintX = 0;\r
+ WindowX = 0;\r
+ WindowW = 320;\r
+ US_Print (" You stand before the gate leading into\n");\r
+ US_Print (" the Towne of Morbidity...\n");\r
+\r
+ PrintY = 180;\r
+ US_Print (" Enter now boldly to defeat the evil Nemesis\n");\r
+ US_Print (" deep inside the catacombs.\n");\r
+\r
+#endif\r
+\r
+ fontcolor = oldfontcolor;\r
+}\r
+\r
+#if 0\r
+boolean ChooseGameLevel()\r
+{\r
+ char choices[] = {sc_Escape,sc_E,sc_N,sc_H,0},ch;\r
+\r
+ CenterWindow(20,10);\r
+\r
+ US_Print("\n Choose difficulty level:\n");\r
+ US_Print(" (E)asy\n");\r
+ US_Print(" (N)ormal\n");\r
+ US_Print(" (H)ard\n");\r
+ US_Print("\n (ESC)ape aborts\n");\r
+\r
+// VW_UpdateScreen();\r
+ if ((ch=GetKeyChoice(choices)) == sc_Escape)\r
+ {\r
+ while (Keyboard[sc_Escape]);\r
+ LastScan = 0;\r
+ return(false);\r
+ }\r
+\r
+ if (ch == sc_E)\r
+ restartgame = gd_Easy;\r
+ else\r
+ if (ch == sc_N)\r
+ restartgame = gd_Normal;\r
+ else\r
+ restartgame = gd_Hard;\r
+\r
+ return(true);\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= SetupScalePic\r
+=\r
+==========================\r
+*/\r
+\r
+void SetupScalePic (unsigned picnum)\r
+{\r
+ unsigned scnum;\r
+\r
+ if (picnum == 1)\r
+ return;\r
+\r
+ scnum = picnum-FIRSTSCALEPIC;\r
+\r
+ if (shapedirectory[scnum])\r
+ {\r
+ MM_SetPurge (&(memptr)shapedirectory[scnum],0);\r
+ return; // allready in memory\r
+ }\r
+\r
+ CA_CacheGrChunk (picnum);\r
+ DeplanePic (picnum);\r
+ shapesize[scnum] = BuildCompShape (&shapedirectory[scnum]);\r
+ grneeded[picnum]&= ~ca_levelbit;\r
+ MM_FreePtr (&grsegs[picnum]);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= SetupScaleWall\r
+=\r
+==========================\r
+*/\r
+\r
+void SetupScaleWall (unsigned picnum)\r
+{\r
+ int x,y;\r
+ unsigned scnum;\r
+ byte far *dest;\r
+\r
+ if (picnum == 1)\r
+ return;\r
+\r
+ scnum = picnum-FIRSTWALLPIC;\r
+\r
+ if (walldirectory[scnum])\r
+ {\r
+ MM_SetPurge (&walldirectory[scnum],0);\r
+ return; // allready in memory\r
+ }\r
+\r
+ CA_CacheGrChunk (picnum);\r
+ DeplanePic (picnum);\r
+ MM_GetPtr(&walldirectory[scnum],64*64);\r
+ dest = (byte far *)walldirectory[scnum];\r
+ for (x=0;x<64;x++)\r
+ for (y=0;y<64;y++)\r
+ *dest++ = spotvis[y][x];\r
+ grneeded[picnum]&= ~ca_levelbit;\r
+ MM_FreePtr (&grsegs[picnum]);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= SetupScaling\r
+=\r
+==========================\r
+*/\r
+\r
+void SetupScaling (void)\r
+{\r
+ int i,x,y;\r
+ byte far *dest;\r
+\r
+//\r
+// build the compiled scalers\r
+//\r
+ for (i=1;i<=VIEWWIDTH/2;i++)\r
+ BuildCompScale (i*2,&scaledirectory[i]);\r
+}\r
+\r
+//===========================================================================\r
+\r
+int showscorebox;\r
+\r
+void RF_FixOfs (void)\r
+{\r
+}\r
+\r
+void HelpScreens (void)\r
+{\r
+}\r
+\r
+\r
+#if 0\r
+/*\r
+==================\r
+=\r
+= CheckMemory\r
+=\r
+==================\r
+*/\r
+\r
+#define MINMEMORY 400000l\r
+\r
+void CheckMemory(void)\r
+{\r
+ unsigned finscreen;\r
+\r
+ if (Flags & FL_NOMEMCHECK)\r
+ return;\r
+\r
+ if (mminfo.nearheap+mminfo.farheap+mminfo.EMSmem+mminfo.XMSmem\r
+ >= MINMEMORY)\r
+ return;\r
+\r
+ CA_CacheGrChunk (OUTOFMEM);\r
+ finscreen = (unsigned)grsegs[OUTOFMEM];\r
+ ShutdownId ();\r
+ movedata (finscreen,7,0xb800,0,4000);\r
+ gotoxy (1,24);\r
+ exit(1);\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= main\r
+=\r
+==========================\r
+*/\r
+\r
+char *MainParmStrings[] = {"q","l","ver","nomemcheck","helptest",nil};\r
+\r
+void main (void)\r
+{\r
+ short i;\r
+\r
+ starting_level = 0;\r
+\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ switch (US_CheckParm(_argv[i],MainParmStrings))\r
+ {\r
+ case 0:\r
+ Flags |= FL_QUICK;\r
+ break;\r
+\r
+ case 1:\r
+ starting_level = atoi(_argv[i]+1);\r
+ if ((starting_level < 0) || (starting_level > LASTMAP-1))\r
+ starting_level = 0;\r
+ break;\r
+\r
+ case 2:\r
+ printf("%s\n", GAMENAME);\r
+ printf("Copyright 1992-93 Softdisk Publishing\n");\r
+ printf("%s %s\n",VERSION,REVISION);\r
+ printf("\n");\r
+ exit(0);\r
+ break;\r
+\r
+ case 3:\r
+ Flags |= FL_NOMEMCHECK;\r
+ break;\r
+\r
+ case 4:\r
+ Flags |= (FL_HELPTEST|FL_QUICK);\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (stricmp(_argv[1], "^(a@&r`"))\r
+ Quit("You must type CATARM to run CATACOMB ARMAGEDDON 3-D\n");\r
+\r
+ MainHelpText.xl = 0;\r
+ MainHelpText.yl = 0;\r
+ MainHelpText.xh = 639;\r
+ MainHelpText.yh = 199;\r
+ MainHelpText.bgcolor = 7;\r
+ MainHelpText.ltcolor = 15;\r
+ MainHelpText.dkcolor = 8;\r
+\r
+// jabhack();\r
+\r
+ randomize();\r
+\r
+ InitGame ();\r
+// CheckMemory ();\r
+ LoadLatchMem ();\r
+\r
+// if (!LoadTextFile("MAINHELP."EXT,&MainHelpText))\r
+// Quit("Can't load MAINHELP."EXT);\r
+\r
+#ifdef PROFILE\r
+ NewGame ();\r
+ GameLoop ();\r
+#endif\r
+\r
+ DemoLoop();\r
+ Quit(NULL);\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// Display640()\r
+//-------------------------------------------------------------------------\r
+void Display640()\r
+{\r
+// Can you believe it takes all this just to change to 640 mode!!???!\r
+//\r
+ VW_ScreenToScreen(0,FREESTART-STATUSLEN,40,80);\r
+ VW_SetLineWidth(80);\r
+ MoveScreen(0,0);\r
+ VW_Bar (0,0,640,200,0);\r
+ VW_SetScreenMode(EGA640GR);\r
+ VW_SetLineWidth(80);\r
+ BlackPalette();\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// Display320()\r
+//-------------------------------------------------------------------------\r
+void Display320()\r
+{\r
+// Can you believe it takes all this just to change to 320 mode!!???!\r
+//\r
+ VW_ColorBorder(0);\r
+ VW_FadeOut();\r
+ VW_SetLineWidth(40);\r
+ MoveScreen(0,0);\r
+ VW_Bar (0,0,320,200,0);\r
+ VW_SetScreenMode(EGA320GR);\r
+ BlackPalette();\r
+ VW_ScreenToScreen(FREESTART-STATUSLEN,0,40,80);\r
+}\r
+\r
+void PrintHelp(void)\r
+{\r
+ char oldfontcolor = fontcolor;\r
+ PrintY = 1;\r
+ WindowX = 135;\r
+ WindowW = 640;\r
+\r
+ VW_FadeOut();\r
+ bufferofs = displayofs = screenloc[0];\r
+ VW_Bar(0,0,320,200,0);\r
+\r
+ Display640();\r
+\r
+ VW_Bar(0, 0, 640, 200, 7);\r
+\r
+ fontcolor = (7 ^ 1);\r
+ US_Print ("\n\n SUMMARY OF GAME CONTROLS\n\n");\r
+\r
+ fontcolor = (7 ^ 4);\r
+ US_Print (" ACTION\n\n");\r
+\r
+ US_Print ("Arrow keys, joystick, or mouse\n");\r
+ US_Print ("TAB or V while turning\n");\r
+ US_Print ("ALT or Button 2 while turning\n");\r
+ US_Print ("CTRL or Button 1\n");\r
+ US_Print ("Z\n");\r
+ US_Print ("X or Enter\n");\r
+ US_Print ("F1\n");\r
+ US_Print ("F2\n");\r
+ US_Print ("F3\n");\r
+ US_Print ("F4\n");\r
+ US_Print ("F5\n");\r
+ US_Print ("ESC\n\n");\r
+#ifndef CATALOG\r
+ fontcolor = (7 ^ 0);\r
+ US_Print (" (See complete Instructions for more info)\n");\r
+#endif\r
+\r
+ fontcolor = (7 ^ 8);\r
+ PrintX = 400;\r
+ PrintY = 37;\r
+ WindowX = 400;\r
+ US_Print (" REACTION\n\n");\r
+ US_Print ("Move and turn\n");\r
+ US_Print ("Turn quickly (Quick Turn)\n");\r
+ US_Print ("Move sideways\n");\r
+ US_Print ("Shoot a Missile\n");\r
+ US_Print ("Shoot a Zapper\n");\r
+ US_Print ("Shoot an Xterminator\n");\r
+ US_Print ("Help (this screen)\n");\r
+ US_Print ("Sound control\n");\r
+ US_Print ("Save game position\n");\r
+ US_Print ("Restore a saved game\n");\r
+ US_Print ("Joystick control\n");\r
+ US_Print ("System options\n\n\n");\r
+\r
+ VW_UpdateScreen();\r
+ VW_FadeIn();\r
+ VW_ColorBorder(8 | 56);\r
+ IN_Ack();\r
+ Display320();\r
+ fontcolor = oldfontcolor;\r
+}
\ No newline at end of file
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define POINTTICS 6\r
+#define PAUSE 300\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+byte bcolor;\r
+short skytimer=-1,skytimer_reset;\r
+short groundtimer=-1,groundtimer_reset;\r
+\r
+unsigned scolor,gcolor;\r
+unsigned *skycolor,*groundcolor,debug_sky,debug_gnd;\r
+\r
+unsigned nocolorchange=0xFFFF;\r
+byte BGFLAGS, // global that holds all current flags\r
+ bgflag; // used by BG changer, this flag is set when done\r
+\r
+\r
+unsigned sky_daytonight[]={0x0909,0x0101,0x0808,0x0000,0xFFFF};\r
+//unsigned gnd_daytonight[]={0x0202,0xFFFF};\r
+\r
+unsigned sky_lightning[]={0x0101,0x0909,0x0f0f,0x0808,0x0000,0xFFFF};\r
+\r
+unsigned sky_colors[NUMLEVELS]={0x0000,0x0000,0x0000,0x0000,0x0808,\r
+ 0x0404,0x0000,0x0000,0x0000,0x0000,\r
+ 0x0000,0x0000,0x0000,0x0000,0x0606,\r
+ 0x0000,0x0000,0x0000,0x0000,0x0000,\r
+ 0x0000};\r
+unsigned gnd_colors[NUMLEVELS]={0x0202,0x0202,0x0606,0x0202,0x0707,\r
+ 0x0505,0x0808,0x0606,0x0101,0x0808,\r
+ 0x0606,0x0404,0x0808,0x0c0c,0x0e0e,\r
+ 0x0808,0x0808,0x0c0c,0x0000,0x0707,\r
+ 0x0808};\r
+\r
+\r
+ControlInfo control;\r
+boolean running=false; //,slowturn;\r
+\r
+int bordertime;\r
+objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist;\r
+\r
+#if USE_INERT_LIST\r
+inertobjtype inertobjlist[MAXINERTOBJ],*inert;\r
+#endif\r
+\r
+unsigned farmapylookup[MAPSIZE];\r
+byte *nearmapylookup[MAPSIZE];\r
+\r
+boolean singlestep,godmode;\r
+int extravbls;\r
+status_flags status_flag;\r
+int status_delay;\r
+\r
+//\r
+// replacing refresh manager\r
+//\r
+unsigned mapwidth,mapheight,tics,realtics;\r
+boolean compatability;\r
+byte *updateptr;\r
+unsigned mapwidthtable[64];\r
+unsigned uwidthtable[UPDATEHIGH];\r
+unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define UPDATESPARESIZE (UPDATEWIDE*2+4)\r
+#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+byte update[UPDATESIZE];\r
+\r
+int mousexmove,mouseymove;\r
+int pointcount,pointsleft;\r
+\r
+short BeepTime = 0;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void CalcBounds (objtype *ob);\r
+void DrawPlayScreen (void);\r
+void PreFullDisplay(void);\r
+void PostFullDisplay(boolean draw_view);\r
+\r
+\r
+//\r
+// near data map array (wall values only, get text number from far data)\r
+//\r
+byte tilemap[MAPSIZE][MAPSIZE];\r
+byte spotvis[MAPSIZE][MAPSIZE];\r
+objtype *actorat[MAPSIZE][MAPSIZE];\r
+\r
+objtype dummyobj;\r
+\r
+int bordertime;\r
+int objectcount;\r
+\r
+void StopMusic(void);\r
+void StartMusic(void);\r
+\r
+void CalibrateJoystick(short joynum);\r
+\r
+//==========================================================================\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// CenterWindow() - Generates a window of a given width & height in the\r
+// middle of the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+#define MAXX 320\r
+#define MAXY 120\r
+\r
+void CenterWindow(word w,word h)\r
+{\r
+ US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h);\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= CheckKeys\r
+=\r
+=====================\r
+*/\r
+\r
+void CheckKeys (void)\r
+{\r
+ extern boolean autofire;\r
+\r
+ if (screenfaded) // don't do anything with a faded screen\r
+ return;\r
+\r
+#if 0\r
+//\r
+// pause key wierdness can't be checked as a scan code\r
+//\r
+ if (Paused)\r
+ {\r
+ CenterWindow (8,3);\r
+ US_PrintCentered ("PAUSED");\r
+ VW_UpdateScreen ();\r
+// SD_MusicOff();\r
+ IN_Ack();\r
+// SD_MusicOn();\r
+ Paused = false;\r
+ if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement\r
+ }\r
+ else\r
+ if (Keyboard[sc_Enter]) // P = pause with no screen disruptioon\r
+ {\r
+// SD_MusicOff();\r
+ DisplaySMsg("PAUSED",NULL);\r
+ IN_Ack();\r
+// SD_MusicOn();\r
+ if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement\r
+ }\r
+ else\r
+ if (Keyboard[sc_S])\r
+ {\r
+ char *Text[] = {{"Slow Mode ON"},{"Slow Mode OFF"}};\r
+\r
+ SlowMode ^= 1;\r
+ extravbls = SlowMode << 3;\r
+ CenterWindow (8,3);\r
+ US_PrintCentered (Text[SlowMode]);\r
+ VW_UpdateScreen ();\r
+// SD_MusicOff();\r
+ IN_Ack();\r
+// SD_MusicOn();\r
+ if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement\r
+ }\r
+#endif\r
+\r
+\r
+// F2 - SOUND OPTIONS\r
+//\r
+ if (Keyboard[sc_F2])\r
+ {\r
+ int height=7;\r
+ boolean ChoiceMade = false;\r
+\r
+ if (AdLibPresent)\r
+ height++;\r
+\r
+ VW_FixRefreshBuffer();\r
+ CenterWindow(22,height);\r
+ US_Print( "\n 1 ) NO SOUND \n");\r
+ US_Print( " 2 ) PC AUDIO \n");\r
+\r
+ if (AdLibPresent)\r
+ US_Print(" 3 ) ADLIB AUDIO\n");\r
+\r
+ US_Print( "\n ESC) EXIT ");\r
+ VW_UpdateScreen();\r
+\r
+ // Switch audio device ON/OFF & load sounds if there\r
+ // was a change in the device.\r
+\r
+ do {\r
+\r
+ if (Keyboard[1]) // ESC - Exit\r
+ ChoiceMade = true;\r
+ else\r
+ if (Keyboard[2]) // 1 - No Sound\r
+ {\r
+ SD_SetSoundMode(sdm_Off);\r
+ ChoiceMade = true;\r
+ }\r
+ else\r
+ if (Keyboard[3]) // 2 - PC Audio\r
+ {\r
+ SD_SetSoundMode(sdm_PC);\r
+// if (oldsoundmode != sdm_PC)\r
+ CA_LoadAllSounds();\r
+ ChoiceMade = true;\r
+ }\r
+ else\r
+ if ((Keyboard[4]) && AdLibPresent) // 3 - AdLib Audio\r
+ {\r
+ SD_SetSoundMode(sdm_AdLib);\r
+// if (oldsoundmode != sdm_AdLib)\r
+ CA_LoadAllSounds();\r
+ ChoiceMade = true;\r
+ }\r
+\r
+ } while (!ChoiceMade);\r
+ tics = realtics = 1;\r
+ IN_ClearKeysDown();\r
+ }\r
+\r
+// F5 - CALIBRATE JOYSTICK\r
+//\r
+ if (Keyboard[sc_F5])\r
+ {\r
+ CalibrateJoystick(0);\r
+ tics = realtics = 1;\r
+ IN_ClearKeysDown();\r
+ }\r
+\r
+deadloop:;\r
+// ESCAPE - quits game\r
+//\r
+ if ((Keyboard[sc_Escape]) || (Flags & FL_DEAD))\r
+ {\r
+ char ch;\r
+\r
+ DisplaySMsg("Options", NULL);\r
+ status_flag = S_NONE;\r
+\r
+\r
+ if (Flags & FL_DEAD)\r
+ {\r
+ char choices[] = {sc_Escape,sc_R,sc_N,sc_Q,0};\r
+ ch = DisplayMsg("Restore New Quit",choices);\r
+ }\r
+ else\r
+ {\r
+ char choices[] = {sc_Escape,sc_S,sc_R,sc_N,sc_Q,0};\r
+ ch = DisplayMsg("Save Restore New Quit",choices);\r
+ }\r
+ DrawText(true);\r
+\r
+ switch (ch)\r
+ {\r
+ case sc_S:\r
+ if (!(Flags & FL_DEAD))\r
+ Keyboard[sc_F3] = true;\r
+ break;\r
+\r
+ case sc_R:\r
+ Keyboard[sc_F4] = true;\r
+ break;\r
+\r
+ case sc_N:\r
+ DisplaySMsg("Starting anew", NULL);\r
+ VW_WaitVBL(60);\r
+ playstate = ex_resetgame;\r
+ Flags &= ~FL_DEAD;\r
+ break;\r
+\r
+ case sc_Q:\r
+ DisplaySMsg("FARE THEE WELL!", NULL);\r
+ VW_WaitVBL(120);\r
+ if (!Flags & FL_QUICK)\r
+ VW_FadeOut();\r
+ NormalScreen();\r
+ FreeUpMemory();\r
+ Quit(NULL);\r
+ break;\r
+ }\r
+ tics = realtics = 1;\r
+ }\r
+\r
+// F1 - DISPLAY HELP\r
+//\r
+ if (Keyboard[sc_F1])\r
+ {\r
+ PrintHelp();\r
+\r
+#ifdef TEXT_PRESENTER\r
+\r
+ extern PresenterInfo MainHelpText;\r
+\r
+ VW_FadeOut();\r
+\r
+ FreeUpMemory();\r
+ if (!LoadPresenterScript("HELP.TXT",&MainHelpText))\r
+ {\r
+ VW_FadeIn();\r
+ CenterWindow(30,5);\r
+ US_CPrint("\nError loading HELP file.\n");\r
+ US_CPrint("Press any key.");\r
+ IN_Ack();\r
+ VW_FadeOut();\r
+ }\r
+ else\r
+ {\r
+ VW_SetSplitScreen(200);\r
+ bufferofs = displayofs = screenloc[0];\r
+ VW_Bar(0,0,320,200,0);\r
+\r
+ Display640();\r
+ Presenter(&MainHelpText);\r
+ Display320();\r
+ }\r
+ FreePresenterScript(&MainHelpText);\r
+#endif\r
+ VW_SetSplitScreen(120);\r
+ VW_SetScreen(screenloc[0],0);\r
+ screenpage = 0;\r
+ CacheScaleds();\r
+\r
+ bufferofs = 0;\r
+ RedrawStatusWindow();\r
+ ThreeDRefresh();\r
+ VW_FadeIn();\r
+ Keyboard[sc_F1] = false;\r
+ tics = realtics = 1;\r
+ IN_ClearKeysDown();\r
+ }\r
+\r
+// F3 - SAVE GAME\r
+//\r
+ if ((Keyboard[sc_F3]) && (!(Flags & FL_DEAD)))\r
+ {\r
+ PreFullDisplay();\r
+ GE_SaveGame();\r
+ PostFullDisplay(true);\r
+ tics = realtics = 1;\r
+ IN_ClearKeysDown();\r
+ }\r
+\r
+// F4 - LOAD GAME\r
+//\r
+ if (Keyboard[sc_F4])\r
+ {\r
+ PreFullDisplay();\r
+ if (GE_LoadGame())\r
+ {\r
+ loadedgame = true;\r
+ playstate = ex_loadedgame;\r
+ Flags &= ~FL_DEAD;\r
+ lasttext = -1;\r
+ PostFullDisplay(false);\r
+ }\r
+ else\r
+ if (playstate == ex_victorious)\r
+ {\r
+ PostFullDisplay(false);\r
+ Victory(false);\r
+ IN_Ack();\r
+// gamestate.mapon++;\r
+ }\r
+ else\r
+ PostFullDisplay(true);\r
+ Keyboard[sc_F5] = false;\r
+ tics = realtics = 1;\r
+ IN_ClearKeysDown();\r
+ }\r
+\r
+ if (Flags & FL_DEAD)\r
+ goto deadloop;\r
+\r
+//\r
+// F10-? debug keys\r
+//\r
+ if (Keyboard[sc_BackSpace])\r
+ {\r
+ DebugKeys();\r
+ if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement\r
+ lasttimecount = TimeCount;\r
+ }\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// PreFullDisplay()\r
+//-------------------------------------------------------------------------\r
+void PreFullDisplay()\r
+{\r
+ VW_FadeOut();\r
+ VW_SetSplitScreen(200);\r
+ bufferofs = displayofs = screenloc[0];\r
+ VW_Bar(0,0,320,200,0);\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// PostFullDisplay()\r
+//-------------------------------------------------------------------------\r
+void PostFullDisplay(boolean draw_view)\r
+{\r
+ VW_SetSplitScreen(120);\r
+ bufferofs = 0;\r
+ RedrawStatusWindow();\r
+ if (draw_view)\r
+ {\r
+ ThreeDRefresh();\r
+ VW_FadeIn();\r
+ }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+#############################################################################\r
+\r
+ The objlist data structure\r
+\r
+#############################################################################\r
+\r
+objlist containt structures for every actor currently playing. The structure\r
+is accessed as a linked list starting at *player, ending when ob->next ==\r
+NULL. GetNewObj inserts a new object at the end of the list, meaning that\r
+if an actor spawn another actor, the new one WILL get to think and react the\r
+same frame. RemoveObj unlinks the given object and returns it to the free\r
+list, but does not damage the objects ->next pointer, so if the current object\r
+removes itself, a linked list following loop can still safely get to the\r
+next element.\r
+\r
+<backwardly linked free list>\r
+\r
+#############################################################################\r
+*/\r
+\r
+\r
+/*\r
+=========================\r
+=\r
+= InitObjList\r
+=\r
+= Call to clear out the entire object list, returning them all to the free\r
+= list. Allocates a special spot for the player.\r
+=\r
+=========================\r
+*/\r
+\r
+void InitObjList (void)\r
+{\r
+ int i;\r
+\r
+ for (i=0;i<MAXACTORS;i++)\r
+ {\r
+ objlist[i].prev = &objlist[i+1];\r
+ objlist[i].next = NULL;\r
+ }\r
+\r
+ objlist[MAXACTORS-1].prev = NULL;\r
+\r
+ objfreelist = &objlist[0];\r
+ lastobj = NULL;\r
+\r
+ objectcount = 0;\r
+\r
+//\r
+// give the player and score the first free spots\r
+//\r
+ GetNewObj (false);\r
+ player = new;\r
+\r
+#if USE_INERT_LIST\r
+ inert = inertobjlist;\r
+#endif\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= GetNewObj\r
+=\r
+= Sets the global variable new to point to a free spot in objlist.\r
+= The free spot is inserted at the end of the liked list\r
+=\r
+= When the object list is full, the caller can either have it bomb out ot\r
+= return a dummy object pointer that will never get used\r
+=\r
+=========================\r
+*/\r
+\r
+void GetNewObj (boolean usedummy)\r
+{\r
+ if (!objfreelist)\r
+ {\r
+ if (usedummy)\r
+ {\r
+ new = &dummyobj;\r
+ return;\r
+ }\r
+ Quit ("GetNewObj: No free spots in objlist!");\r
+ }\r
+\r
+ new = objfreelist;\r
+ objfreelist = new->prev;\r
+ memset (new,0,sizeof(*new));\r
+\r
+ if (lastobj)\r
+ lastobj->next = new;\r
+ new->prev = lastobj; // new->next is allready NULL from memset\r
+\r
+ new->active = false;\r
+ lastobj = new;\r
+\r
+ objectcount++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= RemoveObj\r
+=\r
+= Add the given object back into the free list, and unlink it from it's\r
+= neighbors\r
+=\r
+=========================\r
+*/\r
+\r
+void RemoveObj (objtype *gone)\r
+{\r
+ objtype **spotat;\r
+\r
+ if (gone == player)\r
+ Quit ("RemoveObj: Tried to remove the player!");\r
+\r
+//\r
+// fix the next object's back link\r
+//\r
+ if (gone == lastobj)\r
+ lastobj = (objtype *)gone->prev;\r
+ else\r
+ gone->next->prev = gone->prev;\r
+\r
+//\r
+// fix the previous object's forward link\r
+//\r
+ gone->prev->next = gone->next;\r
+\r
+//\r
+// add it back in to the free list\r
+//\r
+ gone->prev = objfreelist;\r
+ objfreelist = gone;\r
+\r
+ objectcount--;\r
+}\r
+\r
+#if USE_INERT_LIST\r
+\r
+//--------------------------------------------------------------------------\r
+// MoveObjToInert()\r
+//--------------------------------------------------------------------------\r
+void MoveObjToInert(objtype *obj)\r
+{\r
+\r
+ if (inert == &inertobjlist[MAXINERTOBJ])\r
+ return;\r
+\r
+// Transfer info needed by inert objtype\r
+//\r
+ inert->x = obj->x;\r
+ inert->y = obj->y;\r
+ inert->size = obj->size;\r
+ inert->viewx = obj->viewx;\r
+ inert->tilex = obj->tilex;\r
+ inert->tiley = obj->tiley;\r
+ inert->state = obj->state;\r
+ inert->ticcount = obj->ticcount;\r
+\r
+// Setup links between inert objects\r
+//\r
+ if (inert != inertobjlist)\r
+ (inert-1)->next = inert;\r
+ inert->next = NULL;\r
+ inert++;\r
+\r
+// Free 'real' object from list.\r
+//\r
+ RemoveObj(obj);\r
+}\r
+\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= PollControls\r
+=\r
+===================\r
+*/\r
+\r
+void PollControls (void)\r
+{\r
+ unsigned buttons;\r
+\r
+ IN_ReadControl(0,&control);\r
+\r
+ if (MousePresent)\r
+ {\r
+ Mouse(MButtons);\r
+ buttons = _BX;\r
+ Mouse(MDelta);\r
+ mousexmove = _CX;\r
+ mouseymove = _DX;\r
+\r
+ if (buttons&1)\r
+ control.button0 = 1;\r
+ if (buttons&2)\r
+ control.button1 = 1;\r
+\r
+ }\r
+\r
+ if (Keyboard[sc_V] || Keyboard[sc_Tab])\r
+ running = true;\r
+ else\r
+ running = false;\r
+}\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+=================\r
+=\r
+= StopMusic\r
+=\r
+=================\r
+*/\r
+\r
+void StopMusic(void)\r
+{\r
+ int i;\r
+\r
+ SD_MusicOff();\r
+ for (i = 0;i < LASTMUSIC;i++)\r
+ if (audiosegs[STARTMUSIC + i])\r
+ {\r
+ MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3);\r
+ MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false);\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= StartMusic\r
+=\r
+=================\r
+*/\r
+\r
+// JAB - Cache & start the appropriate music for this level\r
+void StartMusic(void)\r
+{\r
+ musicnames chunk;\r
+\r
+ SD_MusicOff();\r
+ chunk = TOOHOT_MUS;\r
+// if ((chunk == -1) || (MusicMode != smm_AdLib))\r
+//DEBUG control panel return;\r
+\r
+ MM_BombOnError (false);\r
+ CA_CacheAudioChunk(STARTMUSIC + chunk);\r
+ MM_BombOnError (true);\r
+ if (mmerror)\r
+ mmerror = false;\r
+ else\r
+ {\r
+ MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true);\r
+ SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]);\r
+ }\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= PlayLoop\r
+=\r
+===================\r
+*/\r
+\r
+void PlayLoop (void)\r
+{\r
+ char shot_color[3] = {4,9,14};\r
+\r
+ int allgems[5]={GEM_DELAY_TIME, // used for Q & D comparison\r
+ GEM_DELAY_TIME, // for having all gems...\r
+ GEM_DELAY_TIME, // the "allgems" declaration MUST\r
+ GEM_DELAY_TIME, // match the "gems" declaration in\r
+ GEM_DELAY_TIME // the gametype structure!\r
+ };\r
+\r
+// int originx=0;\r
+// int i=100;\r
+ signed long dx,dy,radius,psin,pcos,newx,newy;\r
+ int give;\r
+ short objnum;\r
+ signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy;\r
+ short o_radius;\r
+\r
+ void (*think)();\r
+\r
+ ingame = true;\r
+ playstate = TimeCount = 0;\r
+ gamestate.shotpower = handheight = 0;\r
+ pointcount = pointsleft = 0;\r
+\r
+ status_flag = S_NONE;\r
+\r
+#if 0\r
+ // setup sky/ground colors and effects (based on level)\r
+ //\r
+ switch (gamestate.mapon)\r
+ {\r
+ case 255:\r
+ if (!(BGFLAGS & BGF_NIGHT))\r
+ {\r
+ InitBgChange(3*60,sky_daytonight,-1,NULL,BGF_NIGHT);\r
+ groundcolor = &gnd_colors[0];\r
+ }\r
+ else\r
+ {\r
+ skycolor = &sky_colors[0];\r
+ groundcolor = &gnd_colors[0];\r
+ }\r
+ break;\r
+\r
+ default:\r
+ skycolor = &sky_colors[gamestate.mapon];\r
+ groundcolor = &gnd_colors[gamestate.mapon];\r
+ skytimer = groundtimer = -1;\r
+ break;\r
+ }\r
+#endif\r
+\r
+ BGFLAGS |= BGF_NOT_LIGHTNING;\r
+ skytimer = groundtimer = -1;\r
+\r
+ debug_gnd = *groundcolor;\r
+ debug_sky = *skycolor;\r
+ RedrawStatusWindow();\r
+ ThreeDRefresh();\r
+ if (screenfaded)\r
+ VW_FadeIn();\r
+\r
+#ifndef PROFILE\r
+ fizzlein = true; // fizzle fade in the first refresh\r
+#endif\r
+ TimeCount = lasttimecount = lastnuke = 0;\r
+\r
+ PollControls (); // center mouse\r
+// StartMusic ();\r
+ do\r
+ {\r
+#ifndef PROFILE\r
+ PollControls();\r
+#else\r
+ control.xaxis = 1;\r
+ if (++TimeCount == 300)\r
+ return;\r
+#endif\r
+ DisplayStatus(&status_flag);\r
+\r
+ objnum=0;\r
+ for (obj = player;obj;obj = obj->next)\r
+ {\r
+ if ((obj->active >= yes) && (!(FreezeTime && (obj!=player))))\r
+ {\r
+ if (obj->ticcount)\r
+ {\r
+ obj->ticcount-=realtics;\r
+ while ( obj->ticcount <= 0)\r
+ {\r
+ think = obj->state->think;\r
+ if (think)\r
+ {\r
+ statetype *oldstate=obj->state;\r
+\r
+ think (obj);\r
+ if (!obj->state)\r
+ {\r
+ RemoveObj (obj);\r
+ goto nextactor;\r
+ }\r
+ if (obj->state != oldstate)\r
+ break;\r
+ }\r
+\r
+ obj->state = obj->state->next;\r
+ if (!obj->state)\r
+ {\r
+ RemoveObj (obj);\r
+ goto nextactor;\r
+ }\r
+ if (!obj->state->tictime)\r
+ {\r
+ obj->ticcount = 0;\r
+ goto nextactor;\r
+ }\r
+ if (obj->state->tictime>0)\r
+ obj->ticcount += obj->state->tictime;\r
+ }\r
+ }\r
+\r
+ think = obj->state->think;\r
+ if (think)\r
+ {\r
+ think (obj);\r
+ if (!obj->state)\r
+ RemoveObj (obj);\r
+ }\r
+nextactor:;\r
+ }\r
+\r
+ // keep a list of objects around the player for radar updates\r
+ //\r
+ if (obj == player)\r
+ {\r
+ px = player->x;\r
+ py = player->y;\r
+ psin = sintable[player->angle];\r
+ pcos = costable[player->angle];\r
+ xl = px-((long)RADAR_WIDTH<<TILESHIFT)/2;\r
+ xh = px+((long)RADAR_WIDTH<<TILESHIFT)/2-1;\r
+ yl = py-((long)RADAR_HEIGHT<<TILESHIFT)/2;\r
+ yh = py+((long)RADAR_HEIGHT<<TILESHIFT)/2;\r
+ }\r
+\r
+ if (objnum > MAX_RADAR_BLIPS-2)\r
+ objnum = MAX_RADAR_BLIPS-2;\r
+\r
+ ox = obj->x;\r
+ oy = obj->y;\r
+\r
+\r
+ if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh))\r
+ {\r
+ norm_dx = (dx = px-ox)>>TILESHIFT;\r
+ norm_dy = (dy = oy-py)>>TILESHIFT;\r
+\r
+ o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy));\r
+\r
+ if (o_radius < RADAR_RADIUS)\r
+ {\r
+ newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin);\r
+ newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos);\r
+\r
+ RadarXY[objnum][0]=newx>>TILESHIFT;\r
+ RadarXY[objnum][1]=newy>>TILESHIFT;\r
+\r
+ // Define color to use for this object...\r
+ //\r
+\r
+ switch (obj->obclass)\r
+ {\r
+ // NO GEM NEEDED\r
+ //\r
+ // THE WIZARD! (YOU)\r
+ //\r
+ case playerobj:\r
+ RadarXY[objnum++][2]=15;\r
+ break;\r
+\r
+ // WIZARD'S SHOTS\r
+ //\r
+ case pshotobj:\r
+ case bigpshotobj:\r
+ RadarXY[objnum++][2]=shot_color[screenpage];\r
+ break;\r
+\r
+ // BATS (DK GRAY)\r
+ //\r
+ case batobj:\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=8;\r
+ break;\r
+\r
+ // RABBITS (LT GRAY)\r
+ //\r
+ case bunnyobj:\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=7;\r
+ break;\r
+\r
+ // RED GEM\r
+ //\r
+ // EYE, RED DEMON (DK RED)\r
+ //\r
+ case eyeobj:\r
+ case reddemonobj:\r
+ if (gamestate.gems[B_RGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=4;\r
+ break;\r
+\r
+ // RED MAGE (LT RED)\r
+ //\r
+ case mageobj:\r
+ if (gamestate.gems[B_RGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=12;\r
+ break;\r
+\r
+ // BLUE GEM\r
+ //\r
+ // SUCCUBUS (LT BLUE)\r
+ //\r
+ case succubusobj:\r
+ if (gamestate.gems[B_BGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=9;\r
+ break;\r
+\r
+ // WATER DRAGON (DK BLUE)\r
+ //\r
+ case wetobj:\r
+ if (gamestate.gems[B_GGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=1;\r
+ break;\r
+\r
+\r
+\r
+ // GREEN GEM\r
+ //\r
+ // GREEN TROLL (LT GREEN)\r
+ //\r
+ case fatdemonobj:\r
+ if (gamestate.gems[B_GGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=10;\r
+ break;\r
+\r
+ // GODESS (DK GREEN)\r
+ //\r
+ case godessobj:\r
+ if (gamestate.gems[B_GGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=2;\r
+ break;\r
+\r
+ // YELLOW GEM\r
+ //\r
+ // ANT (BROWN)\r
+ //\r
+ case antobj:\r
+ case treeobj:\r
+ if (gamestate.gems[B_YGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=6;\r
+ break;\r
+\r
+ // SKELETON (YELLOW)\r
+ //\r
+ case skeletonobj:\r
+ if (gamestate.gems[B_YGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=14;\r
+ break;\r
+\r
+ // PURPLE GEM\r
+ //\r
+ // ZOMBIE\r
+ //\r
+ case zombieobj:\r
+ if (gamestate.gems[B_PGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=13;\r
+ break;\r
+\r
+ // ALL GEMS NEEDED\r
+ //\r
+ // NEMESIS\r
+ //\r
+ case grelmobj:\r
+ if (!memcmp(gamestate.gems,allgems,sizeof(gamestate.gems)))\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=15;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ RadarXY[objnum][2]=-1; // Signals end of RadarXY list...\r
+\r
+#if USE_INERT_LIST\r
+ if (inert != inertobjlist)\r
+ for (obj=(objtype *)inertobjlist;obj;obj=obj->next)\r
+ if (obj->ticcount)\r
+ {\r
+ obj->ticcount-=realtics;\r
+ while ( obj->ticcount <= 0)\r
+ {\r
+ obj->state = obj->state->next;\r
+ if (!obj->state)\r
+ Quit("Removable object in INERT list.");\r
+\r
+ if (!obj->state->tictime)\r
+ {\r
+ obj->ticcount = 0;\r
+ goto nextactor;\r
+ }\r
+\r
+ if (obj->state->tictime>0)\r
+ obj->ticcount += obj->state->tictime;\r
+ }\r
+ }\r
+#endif\r
+\r
+ if (bordertime)\r
+ {\r
+ bordertime -= realtics;\r
+ if (bordertime<=0)\r
+ {\r
+ bordertime = 0;\r
+ VW_ColorBorder(0);\r
+ }\r
+ }\r
+\r
+#if 1\r
+// random lightning?\r
+//\r
+ if (BGFLAGS & (BGF_NOT_LIGHTNING))\r
+ {\r
+ if ((scolor & 0xe0) && (!(random(20-realtics))))\r
+ {\r
+ BGFLAGS &= ~BGF_NOT_LIGHTNING;\r
+ InitBgChange(1,sky_lightning,-1,NULL,BGF_NOT_LIGHTNING);\r
+ }\r
+ }\r
+\r
+// handle sky/ground color changes\r
+//\r
+ if (skytimer != -1)\r
+ {\r
+ skytimer -= realtics;\r
+ if (skytimer < 0)\r
+ {\r
+ skycolor++;\r
+ if (*skycolor == 0xffff)\r
+ {\r
+ skytimer = -1;\r
+// skycolor--;\r
+ skycolor = &scolor;\r
+ if (groundtimer == -1)\r
+ BGFLAGS |= bgflag;\r
+ }\r
+ else\r
+ skytimer = skytimer_reset;\r
+ }\r
+ }\r
+\r
+ if (groundtimer != -1)\r
+ {\r
+ groundtimer -= realtics;\r
+ if (groundtimer < 0)\r
+ {\r
+ groundcolor++;\r
+ if (*groundcolor == 0xffff)\r
+ {\r
+ groundtimer = -1;\r
+// groundcolor--;\r
+ groundcolor = &gcolor;\r
+ if (skytimer == -1)\r
+ BGFLAGS |= bgflag;\r
+ }\r
+ else\r
+ groundtimer = groundtimer_reset;\r
+ }\r
+ }\r
+#endif\r
+\r
+\r
+//\r
+// Handle FreezeTime counter..\r
+//\r
+ if (FreezeTime)\r
+ {\r
+ if (FreezeTime<20*30)\r
+ if ((BeepTime+=realtics)>=60)\r
+ {\r
+ BeepTime -= 60;\r
+ SD_PlaySound(TICKSND);\r
+ }\r
+\r
+ if ((FreezeTime-=realtics)<=0)\r
+ {\r
+ FreezeTime=0;\r
+ SD_PlaySound(TIMERETURNSND);\r
+ DisplaySMsg(NULL,NULL);\r
+ status_flag = S_NONE;\r
+ }\r
+ }\r
+\r
+\r
+// refresh all\r
+//\r
+ ThreeDRefresh ();\r
+\r
+ if (Flags & FL_DEAD)\r
+ {\r
+ SD_PlaySound (GAMEOVERSND);\r
+ DisplaySMsg("DEAD",NULL);\r
+ DrawHealth();\r
+ if (gamestate.potions)\r
+ {\r
+ bufferofs = displayofs = screenloc[screenpage];\r
+ CenterWindow(35,3);\r
+ US_CPrint("\nYou should use your Cure Potions wisely\n");\r
+ IN_Ack();\r
+ }\r
+ }\r
+\r
+// check for win\r
+//\r
+ if (playstate == ex_victorious)\r
+ {\r
+ Victory(true);\r
+// Flags |= FL_DEAD;\r
+ IN_Ack();\r
+// gamestate.mapon++;\r
+ }\r
+\r
+ CheckKeys();\r
+\r
+ }while (!playstate);\r
+// StopMusic ();\r
+\r
+ ingame = false;\r
+ if (bordertime)\r
+ {\r
+ bordertime = 0;\r
+ VW_ColorBorder(0);\r
+ }\r
+\r
+ if (abortgame)\r
+ abortgame = false;\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// IntSqrt() - by Master Programmer, George Leritte!\r
+//--------------------------------------------------------------------------\r
+int IntSqrt(long va)\r
+{\r
+asm mov AX, word ptr va\r
+asm mov DX, word ptr va+2\r
+asm mov bx,dx // {bx = integer square root of dx:ax}\r
+asm or bx,ax // {if dx:ax=0 then return}\r
+asm jz isq01\r
+asm mov bx,dx\r
+asm shl bx,1\r
+asm or bl,ah\r
+asm or bl,al\r
+asm dec bx\r
+asm add bx,dx // { initial guess}\r
+asm jg isq10\r
+asm inc bx // { don't return zero}\r
+asm jg isq10\r
+asm mov bx,7fffh\r
+isq01:;\r
+ goto exitrout;\r
+\r
+isq10:;\r
+asm push ax\r
+asm push dx\r
+asm div bx\r
+asm sub ax,bx\r
+asm cmp ax,1\r
+asm jbe isq90\r
+asm cmp ax,-1\r
+asm jae isq90\r
+asm sar ax,1\r
+asm add bx,ax\r
+asm pop dx\r
+asm pop ax\r
+asm jmp isq10\r
+isq90:;\r
+asm pop dx\r
+asm pop ax\r
+exitrout:;\r
+asm mov ax,bx\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// InitBgChange()\r
+//-------------------------------------------------------------------------\r
+void InitBgChange(short stimer, unsigned *scolors, short gtimer, unsigned *gcolors, byte flag)\r
+{\r
+ skytimer_reset = skytimer = stimer;\r
+ if (scolors)\r
+ skycolor = scolors;\r
+\r
+ groundtimer_reset = groundtimer = gtimer;\r
+ if (gcolors)\r
+ groundcolor = gcolors;\r
+\r
+ bgflag = flag;\r
+}\r
+\r
+////////////////////////////////////////////////////////\r
+//\r
+// DisplayStatus\r
+//\r
+// Stat_Flag - contains the type of status displayed\r
+// -- also uses status_delay (global variable) will not\r
+// change display until this variable is zero.\r
+// -- heirarchy is determined by the series of if statements,\r
+// to change it, rearrange th if statements.\r
+//\r
+////////////////////////////////////////////////////////\r
+\r
+#define MESSAGEDELAY 25\r
+void DisplayStatus (status_flags *stat_flag)\r
+{\r
+ status_flags temp_status;\r
+\r
+\r
+ if (*stat_flag == S_TIMESTOP)\r
+ return;\r
+\r
+ if (status_delay > 0)\r
+ {\r
+ status_delay -= realtics;\r
+ return;\r
+ }\r
+ else\r
+ status_delay = 0;\r
+\r
+ // check for a change in status from previous call\r
+\r
+ temp_status = S_VIEWING; //precaution\r
+\r
+ if (Keyboard[sc_Control] || control.button0)\r
+ temp_status = S_MISSLE;\r
+\r
+ if (Keyboard[sc_Z] && !Keyboard[sc_F10])\r
+ temp_status = S_ZAPPER;\r
+\r
+ if ((Keyboard[sc_X] && !Keyboard[sc_F10]) || Keyboard[sc_Enter])\r
+ temp_status = S_XTER;\r
+\r
+ if (control.x)\r
+ temp_status = S_TURN;\r
+\r
+ if ((Keyboard[sc_V] || Keyboard[sc_Tab]) && control.x)\r
+ temp_status = S_QTURN;\r
+\r
+ if (Keyboard[sc_Alt] && control.x)\r
+ temp_status = S_SIDESTEP;\r
+\r
+ if (control.y < 0)\r
+ temp_status = S_ADVANCE;\r
+\r
+ if (control.y > 0)\r
+ temp_status = S_RETREAT;\r
+\r
+ if (Keyboard[sc_F5])\r
+ temp_status = S_JOYSTICK;\r
+\r
+ if (Keyboard[sc_F4])\r
+ temp_status = S_RESTORING;\r
+\r
+ if (Keyboard[sc_F3])\r
+ temp_status = S_SAVING;\r
+\r
+ if (Keyboard[sc_F2])\r
+ temp_status = S_SND;\r
+\r
+ if (Keyboard[sc_F1])\r
+ temp_status = S_HELP;\r
+\r
+ if (temp_status != *stat_flag)\r
+ {\r
+ *stat_flag = temp_status;\r
+\r
+\r
+ switch (*stat_flag)\r
+ {\r
+ case S_MISSLE:\r
+ DisplaySMsg("Magick Missile", NULL);\r
+ status_delay = MESSAGEDELAY;\r
+ break;\r
+\r
+ case S_ZAPPER:\r
+ if (gamestate.bolts)\r
+ {\r
+ DisplaySMsg("Zapper", NULL);\r
+ status_delay = MESSAGEDELAY+10;\r
+ }\r
+ break;\r
+\r
+ case S_XTER:\r
+ if (gamestate.nukes)\r
+ {\r
+ DisplaySMsg("Xterminator", NULL);\r
+ status_delay = MESSAGEDELAY+5;\r
+ }\r
+ break;\r
+\r
+ case S_TURN:\r
+ DisplaySMsg("Turning", NULL);\r
+ status_delay = MESSAGEDELAY;\r
+ break;\r
+\r
+ case S_QTURN:\r
+ DisplaySMsg("Quick Turning", NULL);\r
+ status_delay = MESSAGEDELAY;\r
+ break;\r
+\r
+ case S_SIDESTEP:\r
+ DisplaySMsg("Sidestepping", NULL);\r
+ status_delay = MESSAGEDELAY;\r
+ break;\r
+\r
+ case S_ADVANCE:\r
+ DisplaySMsg("Advancing", NULL);\r
+ status_delay = MESSAGEDELAY;\r
+ break;\r
+\r
+ case S_RETREAT:\r
+ DisplaySMsg("Retreating", NULL);\r
+ status_delay = MESSAGEDELAY;\r
+ break;\r
+\r
+ case S_JOYSTICK:\r
+ DisplaySMsg("Adjusting Joystick", NULL);\r
+ break;\r
+\r
+ case S_RESTORING:\r
+ DisplaySMsg("Restoring", NULL);\r
+ break;\r
+\r
+ case S_SAVING:\r
+ DisplaySMsg("Saving", NULL);\r
+ break;\r
+\r
+ case S_SND:\r
+ DisplaySMsg("Select Sound", NULL);\r
+ break;\r
+\r
+ case S_HELP:\r
+ DisplaySMsg("Getting Help", NULL);\r
+ break;\r
+\r
+ case S_VIEWING:\r
+ DisplaySMsg("Viewing", NULL);\r
+ break;\r
+ }\r
+ bufferofs = displayofs = screenloc[screenpage];\r
+\r
+ }\r
+}\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_SCALE.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+//const unsigned viewheight = 144;\r
+const unsigned screenbwide = 40;\r
+const byte BACKGROUNDPIX = 5;\r
+\r
+unsigned shapesize[NUMSCALEPICS];\r
+t_compscale _seg *scaledirectory[NUMSCALEPICS];\r
+t_compshape _seg *shapedirectory[NUMSCALEPICS];\r
+memptr walldirectory[NUMSCALEWALLS];\r
+\r
+/*\r
+===========================\r
+=\r
+= DeplanePic\r
+=\r
+= Takes a raw bit map of width bytes by height and creates a scaleable shape\r
+=\r
+= Returns the length of the shape in bytes\r
+=\r
+= Fills in spotvis (a convenient 64*64 array) with the color values\r
+=\r
+===========================\r
+*/\r
+\r
+void DeplanePic (int picnum)\r
+{\r
+ byte far *plane0,far *plane1,far *plane2,far *plane3;\r
+ byte by0,by1,by2,by3;\r
+ unsigned x,y,b,color,shift,width,height;\r
+ byte *dest;\r
+\r
+//\r
+// convert ega pixels to byte color values in a temp buffer\r
+//\r
+ width = pictable[picnum-STARTPICS].width;\r
+ height = pictable[picnum-STARTPICS].height;\r
+\r
+ if (width>8 || height!=64)\r
+ Quit ("DePlanePic: Bad size shape");\r
+\r
+ memset (spotvis,BACKGROUNDPIX,sizeof(spotvis));\r
+\r
+ plane0 = (byte _seg *)grsegs[picnum];\r
+ plane1 = plane0 + width*height;\r
+ plane2 = plane1 + width*height;\r
+ plane3 = plane2 + width*height;\r
+\r
+ for (y=0;y<height;y++)\r
+ {\r
+ dest = &spotvis[y][0];\r
+ for (x=0;x<width;x++)\r
+ {\r
+ by0 = *plane0++;\r
+ by1 = *plane1++;\r
+ by2 = *plane2++;\r
+ by3 = *plane3++;\r
+\r
+ for (b=0;b<8;b++)\r
+ {\r
+ shift=8-b;\r
+\r
+ color = 0;\r
+ asm mov cl,[BYTE PTR shift]\r
+ asm mov al,[BYTE PTR by3]\r
+ asm rcr al,cl;\r
+ asm rcl [BYTE PTR color],1;\r
+\r
+ asm mov cl,[BYTE PTR shift]\r
+ asm mov al,[BYTE PTR by2]\r
+ asm rcr al,cl;\r
+ asm rcl [BYTE PTR color],1;\r
+\r
+ asm mov cl,[BYTE PTR shift]\r
+ asm mov al,[BYTE PTR by1]\r
+ asm rcr al,cl;\r
+ asm rcl [BYTE PTR color],1;\r
+\r
+ asm mov cl,[BYTE PTR shift]\r
+ asm mov al,[BYTE PTR by0]\r
+ asm rcr al,cl;\r
+ asm rcl [BYTE PTR color],1;\r
+\r
+ *dest++ = color;\r
+ } // B\r
+ } // X\r
+ } // Y\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= BuildCompScale\r
+=\r
+= Builds a compiled scaler object that will scale a 64 tall object to\r
+= the given height (centered vertically on the screen)\r
+=\r
+= height should be even\r
+=\r
+= Call with\r
+= ---------\r
+= DS:SI Source for scale\r
+= ES:DI Dest for scale\r
+=\r
+= Calling the compiled scaler only destroys AL\r
+=\r
+========================\r
+*/\r
+\r
+unsigned BuildCompScale (int height, memptr *finalspot)\r
+{\r
+ t_compscale _seg *work;\r
+ byte far *code;\r
+\r
+ int i;\r
+ long fix,step;\r
+ unsigned src,totalscaled,totalsize;\r
+ int startpix,endpix,toppix;\r
+\r
+\r
+ MM_GetPtr (&(memptr)work,20000);\r
+\r
+ step = ((long)height<<16) / 64;\r
+ code = &work->code[0];\r
+ toppix = (viewheight-height)/2;\r
+ fix = 0;\r
+\r
+ for (src=0;src<=64;src++)\r
+ {\r
+ startpix = fix>>16;\r
+ fix += step;\r
+ endpix = fix>>16;\r
+\r
+ work->start[src] = startpix;\r
+ if (endpix>startpix)\r
+ work->width[src] = endpix-startpix;\r
+ else\r
+ work->width[src] = 0;\r
+\r
+//\r
+// mark the start of the code\r
+//\r
+ work->codeofs[src] = FP_OFF(code);\r
+\r
+//\r
+// compile some code if the source pixel generates any screen pixels\r
+//\r
+ startpix+=toppix;\r
+ endpix+=toppix;\r
+\r
+ if (startpix == endpix || endpix < 0 || startpix >= VIEWHEIGHT || src == 64)\r
+ continue;\r
+\r
+ //\r
+ // mov al,[si+src]\r
+ //\r
+ *code++ = 0x8a;\r
+ *code++ = 0x44;\r
+ *code++ = src;\r
+\r
+ for (;startpix<endpix;startpix++)\r
+ {\r
+ if (startpix >= VIEWHEIGHT)\r
+ break; // off the bottom of the view area\r
+ if (startpix < 0)\r
+ continue; // not into the view area\r
+\r
+ //\r
+ // and [es:di+heightofs],al\r
+ //\r
+ *code++ = 0x26;\r
+ *code++ = 0x20;\r
+ *code++ = 0x85;\r
+ *((unsigned far *)code)++ = startpix*screenbwide;\r
+ }\r
+\r
+ }\r
+\r
+//\r
+// retf\r
+//\r
+ *code++ = 0xcb;\r
+\r
+ totalsize = FP_OFF(code);\r
+ MM_GetPtr (finalspot,totalsize);\r
+ _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);\r
+ MM_FreePtr (&(memptr)work);\r
+\r
+ return totalsize;\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= BuildCompShape\r
+=\r
+= typedef struct\r
+= {\r
+= unsigned width;\r
+= unsigned codeofs[64];\r
+= } t_compshape;\r
+=\r
+= Width is the number of compiled line draws in the shape. The shape\r
+= drawing code will assume that the midpoint of the shape is in the\r
+= middle of the width.\r
+=\r
+= The non background pixel data will start at codeofs[width], so codeofs\r
+= greater than width will be invalid.\r
+=\r
+= Each code offset will draw one vertical line of the shape, consisting\r
+= of 0 or more segments of scaled pixels.\r
+=\r
+= The scaled shapes use ss:0-4 as a scratch variable for the far call to\r
+= the compiled scaler, so zero it back out after the shape is scaled, or\r
+= a "null pointer assignment" will result upon termination.\r
+=\r
+= Setup for a call to a compiled shape\r
+= -----------------------------------\r
+= ax toast\r
+= bx toast\r
+= cx toast\r
+= dx segment of compiled shape\r
+= si toast\r
+= di byte at top of view area to draw the line in\r
+= bp 0\r
+= ss:2 and ds the segment of the compiled scaler to use\r
+= es screenseg\r
+=\r
+= Upon return, ds IS NOT SET to the data segment. Do:\r
+= mov ax,ss\r
+= mov ds,ax\r
+=\r
+=\r
+= GC_BITMASK set to the pixels to be drawn in the row of bytes under DI\r
+= GC_MODE read mode 1, write mode 2\r
+= GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff\r
+=\r
+=\r
+= Code generated for each segment\r
+= -------------------------------\r
+= mov bx,[(segend+1)*2]\r
+= mov cx,[bx]\r
+= mov [BYTE PTR bx],0xc8 // far return\r
+= mov ax,[segstart*2]\r
+= mov [ss:0],ax // entry point into the compiled scaler\r
+= mov ds,dx // (mov ds,cs) the data is after the compiled code\r
+= mov si,ofs data\r
+= call [bp] // scale some pixels\r
+= mov ds,[bp+2]\r
+= mov [bx],cx // un patch return\r
+=\r
+= Code generated after all segments on a line\r
+= -------------------------------------------\r
+= retf\r
+=\r
+========================\r
+*/\r
+\r
+unsigned BuildCompShape (t_compshape _seg **finalspot)\r
+{\r
+ t_compshape _seg *work;\r
+ byte far *code;\r
+ int firstline,lastline,x,y;\r
+ unsigned firstpix,lastpix,width;\r
+ unsigned totalsize,pixelofs;\r
+ unsigned buff;\r
+\r
+\r
+// MM_GetPtr (&(memptr)work,20000);\r
+ EGAWRITEMODE(0);\r
+ EGAREADMAP(0); // use ega screen memory for temp buffer\r
+ EGAMAPMASK(1);\r
+ buff = screenloc[1];\r
+ work = (t_compshape _seg *)(0xa000+(buff+15)/16);\r
+\r
+//\r
+// find the width of the shape\r
+//\r
+ firstline = -1;\r
+ x=0;\r
+ do\r
+ {\r
+ for (y=0;y<64;y++)\r
+ if (spotvis[y][x] != BACKGROUNDPIX)\r
+ {\r
+ firstline = x;\r
+ break;\r
+ }\r
+ if (++x == 64)\r
+ Quit ("BuildCompShape: No shape data!");\r
+ } while (firstline == -1);\r
+\r
+ lastline = -1;\r
+ x=63;\r
+ do\r
+ {\r
+ for (y=0;y<64;y++)\r
+ if (spotvis[y][x] != BACKGROUNDPIX)\r
+ {\r
+ lastline = x;\r
+ break;\r
+ }\r
+ x--;\r
+ } while (lastline == -1);\r
+\r
+ width = lastline-firstline+1;\r
+\r
+ work->width = width;\r
+ code = (byte far *)&work->codeofs[width];\r
+\r
+//\r
+// copy all non background pixels to the work space\r
+//\r
+ pixelofs = FP_OFF(code);\r
+\r
+ for (x=firstline;x<=lastline;x++)\r
+ for (y=0;y<64;y++)\r
+ if (spotvis[y][x] != BACKGROUNDPIX)\r
+ *code++ = spotvis[y][x];\r
+\r
+//\r
+// start compiling the vertical lines\r
+//\r
+ for (x=firstline;x<=lastline;x++)\r
+ {\r
+ work->codeofs[x-firstline] = FP_OFF(code);\r
+\r
+ y=0;\r
+ do\r
+ {\r
+ //\r
+ // scan past black background pixels\r
+ //\r
+ while (spotvis[y][x] == BACKGROUNDPIX && y<64)\r
+ y++;\r
+\r
+ if (y>63) // no more segments\r
+ break;\r
+\r
+ firstpix = y+1; // +1 because width is before codeofs\r
+\r
+ //\r
+ // scan past scalable pixels\r
+ //\r
+ while (spotvis[y][x] != BACKGROUNDPIX && y<64)\r
+ y++;\r
+\r
+ if (y>63)\r
+ lastpix = 65;\r
+ else\r
+ lastpix = y+1; // actually one pixel past the last displayed\r
+\r
+ //\r
+ // compile the scale call\r
+ //\r
+ *code++ = 0x8b; // mov bx,[lastpix*2]\r
+ *code++ = 0x1e;\r
+ *((unsigned far *)code)++ = lastpix*2;\r
+\r
+ *code++ = 0x8b; // mov cx,[bx]\r
+ *code++ = 0x0f;\r
+\r
+ *code++ = 0xc6; // move [BYTE bx],0xcb\r
+ *code++ = 0x07;\r
+ *code++ = 0xcb;\r
+\r
+ *code++ = 0xa1; // mov ax,[firstpix*2] /*************\r
+ *((unsigned far *)code)++ = firstpix*2;\r
+\r
+ *code++ = 0x36; // mov [ss:0],ax\r
+ *code++ = 0xa3;\r
+ *code++ = 0x00;\r
+ *code++ = 0x00;\r
+\r
+ *code++ = 0x8e; // mov ds,dx (mov ds,cs)\r
+ *code++ = 0xda;\r
+\r
+ *code++ = 0xbe; // mov si,OFFSET pixelofs-firstpixel\r
+ *((unsigned far *)code)++ = pixelofs-firstpix;\r
+\r
+ *code++ = 0xff; // call [DWORD bp]\r
+ *code++ = 0x5e;\r
+ *code++ = 0x00;\r
+\r
+ *code++ = 0x8e; // mov ds,[bp+2]\r
+ *code++ = 0x5e;\r
+ *code++ = 0x02;\r
+\r
+ *code++ = 0x89; // mov [bx],cx\r
+ *code++ = 0x0f;\r
+\r
+ pixelofs += (lastpix-firstpix);\r
+ } while (y<63);\r
+\r
+ //\r
+ // retf\r
+ //\r
+ *code++ = 0xcb;\r
+ }\r
+\r
+\r
+//\r
+// copy the final shape to a properly sized buffer\r
+//\r
+ totalsize = FP_OFF(code);\r
+\r
+ if (totalsize >= (PAGELEN*2))\r
+ Quit("BuildCompShape(): Shape is too complex!");\r
+\r
+ MM_GetPtr ((memptr *)finalspot,totalsize);\r
+ _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);\r
+// MM_FreePtr (&(memptr)work);\r
+\r
+ return totalsize;\r
+}\r
+\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= ScaleShape\r
+=\r
+= Draws a compiled shape at [scale] pixels high\r
+=\r
+= Setup for call\r
+= --------------\r
+= GC_MODE read mode 1, write mode 2\r
+= GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff\r
+= GC_INDEX pointing at GC_BITMASK\r
+=\r
+=======================\r
+*/\r
+\r
+static long longtemp;\r
+\r
+void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale)\r
+{\r
+ #define MAX_OBJ_SCALE (MAXSCALE)\r
+\r
+\r
+ t_compscale _seg *comptable;\r
+ unsigned width,scalewidth;\r
+ int x,pixel,lastpixel,pixwidth,min;\r
+ unsigned far *codehandle, far *widthptr;\r
+ unsigned badcodeptr;\r
+ int rightclip;\r
+\r
+ if (!compshape)\r
+ Quit ("ScaleShape: NULL compshape ptr!");\r
+\r
+ scale = (scale+1)/2;\r
+ if (!scale)\r
+ return; // too far away\r
+ if (scale>MAX_OBJ_SCALE)\r
+ scale = MAX_OBJ_SCALE;\r
+ comptable = scaledirectory[scale];\r
+\r
+ width = compshape->width;\r
+ scalewidth = comptable->start[width];\r
+\r
+ pixel = xcenter - scalewidth/2;\r
+ lastpixel = pixel+scalewidth-1;\r
+ if (pixel >= VIEWWIDTH || lastpixel < 0)\r
+ return; // totally off screen\r
+\r
+//\r
+// scan backwards from the right edge until pixels are visable\r
+// rightclip is the first NON VISABLE pixel\r
+//\r
+ if (lastpixel>=VIEWWIDTH-1)\r
+ rightclip = VIEWWIDTH-1;\r
+ else\r
+ rightclip = lastpixel;\r
+\r
+ if (zbuffer[rightclip]>scale)\r
+ {\r
+ if (pixel>0)\r
+ min = pixel;\r
+ else\r
+ min = 0;\r
+ do\r
+ {\r
+ if (--rightclip < min)\r
+ return; // totally covered or before 0\r
+ if (zbuffer[rightclip]<=scale)\r
+ break;\r
+ } while (1);\r
+ }\r
+ rightclip++;\r
+\r
+//\r
+// scan from the left until it is on screen, leaving\r
+// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly\r
+//\r
+ *(((unsigned *)&longtemp)+1) = (unsigned)compshape; // seg of shape\r
+ codehandle = &compshape->codeofs[0];\r
+ badcodeptr = compshape->codeofs[width];\r
+ widthptr = &comptable->width[0];\r
+ asm mov ax,[comptable]\r
+ asm mov WORD PTR [2],ax // ds:0-4 is used as a far call pointer\r
+ // by the compiled shapes\r
+ pixwidth = *widthptr; // scaled width of this pixel\r
+ while (!pixwidth)\r
+ {\r
+ pixwidth = *++widthptr; // find the first visable pixel\r
+ codehandle++;\r
+ }\r
+\r
+ if (pixel<0)\r
+ {\r
+ do\r
+ {\r
+ if (pixel+pixwidth>0)\r
+ {\r
+ pixwidth += pixel;\r
+ pixel = 0;\r
+ break;\r
+ }\r
+ do\r
+ {\r
+ pixwidth = *++widthptr;\r
+ codehandle++;\r
+ } while (!pixwidth);\r
+ pixel+=pixwidth;\r
+ } while (1);\r
+ }\r
+\r
+//\r
+// scan until it is visable, leaving\r
+// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly\r
+//\r
+ do\r
+ {\r
+ if (zbuffer[pixel] <= scale)\r
+ break; // start drawing here\r
+ pixel++;\r
+ if (!--pixwidth)\r
+ {\r
+ do\r
+ {\r
+ pixwidth = *++widthptr;\r
+ codehandle++;\r
+ } while (!pixwidth);\r
+ }\r
+ } while (1);\r
+\r
+ if (pixel+pixwidth>rightclip)\r
+ pixwidth = rightclip-pixel;\r
+//\r
+// draw lines\r
+//\r
+ do // while (1)\r
+ {\r
+ //\r
+ // scale a vertical segment [pixwidth] pixels wide at [pixel]\r
+ //\r
+ (unsigned)longtemp = *codehandle; // offset of compiled code\r
+ if ((unsigned)longtemp == badcodeptr)\r
+ Quit ("ScaleShape: codehandle past end!");\r
+\r
+ asm mov bx,[pixel]\r
+ asm mov di,bx\r
+ asm shr di,1\r
+ asm shr di,1\r
+ asm shr di,1 // X in bytes\r
+ asm add di,[bufferofs]\r
+ asm and bx,7\r
+ asm shl bx,1\r
+ asm shl bx,1\r
+ asm shl bx,1\r
+ asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1\r
+ asm dec bx\r
+ asm push bx\r
+ asm mov al,BYTE PTR [bitmasks1+bx]\r
+ asm mov dx,GC_INDEX+1\r
+ asm out dx,al // set bit mask register\r
+\r
+ asm mov es,[screenseg]\r
+ asm push si\r
+ asm push di\r
+ asm push bp\r
+ asm xor bp,bp\r
+ asm mov dx,[WORD PTR longtemp+2]\r
+ asm mov ds,[2]\r
+ asm call ss:[DWORD PTR longtemp] // scale the line of pixels\r
+ asm mov ax,ss\r
+ asm mov ds,ax\r
+ asm pop bp\r
+ asm pop di\r
+ asm pop si\r
+\r
+ asm pop bx\r
+ asm mov al,BYTE PTR [bitmasks2+bx]\r
+ asm or al,al\r
+ asm jz nosecond\r
+\r
+ //\r
+ // draw a second byte for vertical strips that cross two bytes\r
+ //\r
+ asm inc di\r
+ asm mov dx,GC_INDEX+1\r
+ asm out dx,al // set bit mask register\r
+ asm push si\r
+ asm push di\r
+ asm push bp\r
+ asm xor bp,bp\r
+ asm mov dx,[WORD PTR longtemp+2]\r
+ asm mov ds,[2]\r
+ asm call ss:[DWORD PTR longtemp] // scale the line of pixels\r
+ asm mov ax,ss\r
+ asm mov ds,ax\r
+ asm pop bp\r
+ asm pop di\r
+ asm pop si\r
+\r
+\r
+ //\r
+ // advance to the next drawn line\r
+ //\r
+nosecond:;\r
+ if ( (pixel+=pixwidth) == rightclip )\r
+ {\r
+ asm mov WORD PTR [0],0\r
+ asm mov WORD PTR [2],0\r
+ return; // all done!\r
+ }\r
+\r
+ do\r
+ {\r
+ pixwidth = *++widthptr;\r
+ codehandle++;\r
+ } while (!pixwidth);\r
+\r
+ if (pixel+pixwidth > rightclip)\r
+ pixwidth = rightclip-pixel;\r
+\r
+ } while (1);\r
+\r
+}\r
+\r
+//\r
+// bit mask tables for drawing scaled strips up to eight pixels wide\r
+//\r
+\r
+byte bitmasks1[8][8] = {\r
+{0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff},\r
+{0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f,0x7f},\r
+{0x20,0x30,0x38,0x3c,0x3e,0x3f,0x3f,0x3f},\r
+{0x10,0x18,0x1c,0x1e,0x1f,0x1f,0x1f,0x1f},\r
+{0x8,0xc,0xe,0xf,0xf,0xf,0xf,0xf},\r
+{0x4,0x6,0x7,0x7,0x7,0x7,0x7,0x7},\r
+{0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3},\r
+{0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1} };\r
+\r
+byte bitmasks2[8][8] = {\r
+{0,0,0,0,0,0,0,0},\r
+{0,0,0,0,0,0,0,0x80},\r
+{0,0,0,0,0,0,0x80,0xc0},\r
+{0,0,0,0,0,0x80,0xc0,0xe0},\r
+{0,0,0,0,0x80,0xc0,0xe0,0xf0},\r
+{0,0,0,0x80,0xc0,0xe0,0xf0,0xf8},\r
+{0,0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc},\r
+{0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe} };\r
+\r
+\r
+\r
+\r
+\r
+\r
--- /dev/null
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+IDEAL\r
+MODEL MEDIUM,C\r
+\r
+include "ID_ASM.EQU"\r
+\r
+;===========================================================================\r
+;\r
+; SCALING GRAPHICS\r
+;\r
+;===========================================================================\r
+\r
+\r
+\r
+MACRO MAKELAB NUM\r
+\r
+lab&NUM:\r
+\r
+ENDM\r
+\r
+MACRO MAKEREF NUM\r
+\r
+dw OFFSET lab&NUM\r
+\r
+ENDM\r
+\r
+\r
+;=========================================================================\r
+\r
+MAXSCALES equ 256\r
+\r
+ DATASEG\r
+\r
+EXTRN screenseg:WORD\r
+EXTRN linewidth:WORD\r
+\r
+LABEL endtable WORD\r
+labcount = 0\r
+REPT MAXSCALES\r
+MAKEREF %labcount\r
+labcount = labcount + 1\r
+ENDM\r
+\r
+\r
+ CODESEG\r
+\r
+;==================================================\r
+;\r
+; void scaleline (int scale, unsigned picseg, unsigned maskseg,\r
+; unsigned screen, unsigned width)\r
+;\r
+;==================================================\r
+\r
+PROC ScaleLine pixels:word, scaleptr:dword, picptr:dword, screen:word\r
+USES si,di\r
+PUBLIC ScaleLine\r
+\r
+;\r
+; modify doline procedure for proper width\r
+;\r
+ mov bx,[pixels]\r
+ cmp bx,MAXSCALES\r
+ jbe @@scaleok\r
+ mov bx,MAXSCALES\r
+@@scaleok:\r
+ shl bx,1\r
+ mov bx,[endtable+bx]\r
+ push [cs:bx] ;save the code that will be modified over\r
+ mov [WORD cs:bx],0d18eh ;mov ss,cx\r
+ push [cs:bx+2] ;save the code that will be modified over\r
+ mov [WORD cs:bx+2],90c3h ;ret / nop\r
+ push bx\r
+\r
+ mov dx,[linewidth]\r
+\r
+ mov di,[WORD screen]\r
+ mov es,[screenseg]\r
+\r
+ mov si,[WORD scaleptr]\r
+ mov ds,[WORD scaleptr+2]\r
+\r
+ mov bx,[WORD picptr]\r
+ mov ax,[WORD picptr+2] ;will be moved into ss after call\r
+\r
+ mov bp,bx\r
+\r
+ cli\r
+ call doline\r
+ sti\r
+;\r
+; restore doline to regular state\r
+;\r
+ pop bx ;address of modified code\r
+ pop [cs:bx+2]\r
+ pop [cs:bx]\r
+\r
+ mov ax,ss\r
+ mov ds,ax\r
+ ret\r
+\r
+;================\r
+;\r
+; doline\r
+;\r
+; Big unwound scaling routine\r
+;\r
+; ds:si = scale table\r
+; ss:bx = pic data\r
+; es:di = screen location\r
+;\r
+;================\r
+\r
+doline:\r
+\r
+ mov cx,ss\r
+ mov ss,ax ;can't call a routine with ss used...\r
+\r
+labcount = 0\r
+\r
+REPT MAXSCALES\r
+\r
+MAKELAB %labcount\r
+labcount = labcount + 1\r
+\r
+ lodsb ; get scaled pixel number\r
+ xlat [ss:bx] ; look it up in the picture\r
+ xchg [es:di],al ; load latches and write pixel to screen\r
+ add di,dx ; down to next line\r
+\r
+ENDM\r
+\r
+ mov ss,cx\r
+ ret\r
+\r
+ENDP\r
+\r
+END
\ No newline at end of file
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_STATE.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+dirtype opposite[9] =\r
+ {south,west,north,east,southwest,northwest,northeast,southeast,nodir};\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= Internal_SpawnNewObj\r
+=\r
+===================\r
+*/\r
+void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat)\r
+{\r
+ extern objtype dummyobj;\r
+\r
+ GetNewObj(UseDummy);\r
+ new->size = size;\r
+ new->state = state;\r
+ new->ticcount = random (state->tictime)+1;\r
+\r
+ new->tilex = x;\r
+ new->tiley = y;\r
+ new->x = ((long)x<<TILESHIFT)+TILEGLOBAL/2;\r
+ new->y = ((long)y<<TILESHIFT)+TILEGLOBAL/2;\r
+ CalcBounds(new);\r
+ new->dir = nodir;\r
+ new->active = noalways;\r
+\r
+ if (new != &dummyobj && PutInActorat)\r
+ actorat[new->tilex][new->tiley] = new;\r
+}\r
+\r
+void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy)\r
+{\r
+ GetNewObj(UseDummy);\r
+ new->size = size;\r
+ new->state = state;\r
+ new->ticcount = random (state->tictime)+1;\r
+ new->active = noalways;\r
+\r
+ new->x = x;\r
+ new->y = y;\r
+ new->tilex = x>>TILESHIFT;\r
+ new->tiley = y>>TILESHIFT;\r
+ CalcBounds(new);\r
+ new->distance = 100;\r
+ new->dir = nodir;\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= CheckHandAttack\r
+=\r
+= If the object can move next to the player, it will return true\r
+=\r
+===================\r
+*/\r
+\r
+boolean CheckHandAttack (objtype *ob)\r
+{\r
+ long deltax,deltay,size;\r
+\r
+ size = (long)ob->size + player->size + ob->speed*tics + SIZE_TEST;\r
+ deltax = ob->x - player->x;\r
+ deltay = ob->y - player->y;\r
+\r
+ if (deltax > size || deltax < -size || deltay > size || deltay < -size)\r
+ return false;\r
+\r
+ return true;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= T_DoDamage\r
+=\r
+= Attacks the player if still nearby, then immediately changes to next state\r
+=\r
+===================\r
+*/\r
+\r
+void T_DoDamage (objtype *ob)\r
+{\r
+ int points;\r
+\r
+\r
+ if (CheckHandAttack(ob) && (!(ob->flags & of_damagedone)))\r
+ {\r
+ points = 0;\r
+\r
+ switch (ob->obclass)\r
+ {\r
+ case zombieobj:\r
+ case fatdemonobj:\r
+ points = 8;\r
+ break;\r
+ case reddemonobj:\r
+ case godessobj:\r
+ points = 15;\r
+ break;\r
+ case antobj:\r
+ points = 2;\r
+ break;\r
+ case skeletonobj:\r
+ points = 6;\r
+ break;\r
+\r
+ case wetobj:\r
+ points = 7;\r
+ break;\r
+ case treeobj:\r
+ points = 7;\r
+ break;\r
+ case bunnyobj:\r
+ points = 4;\r
+ break;\r
+ }\r
+ TakeDamage (points);\r
+ ob->flags |= of_damagedone;\r
+ }\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================================\r
+=\r
+= Walk\r
+=\r
+==================================\r
+*/\r
+\r
+boolean Walk (objtype *ob)\r
+{\r
+ switch (ob->dir)\r
+ {\r
+ case north:\r
+ if (actorat[ob->tilex][ob->tiley-1])\r
+ return false;\r
+ ob->tiley--;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case northeast:\r
+ if (actorat[ob->tilex+1][ob->tiley-1])\r
+ return false;\r
+ ob->tilex++;\r
+ ob->tiley--;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case east:\r
+ if (actorat[ob->tilex+1][ob->tiley])\r
+ return false;\r
+ ob->tilex++;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case southeast:\r
+ if (actorat[ob->tilex+1][ob->tiley+1])\r
+ return false;\r
+ ob->tilex++;\r
+ ob->tiley++;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case south:\r
+ if (actorat[ob->tilex][ob->tiley+1])\r
+ return false;\r
+ ob->tiley++;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case southwest:\r
+ if (actorat[ob->tilex-1][ob->tiley+1])\r
+ return false;\r
+ ob->tilex--;\r
+ ob->tiley++;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case west:\r
+ if (actorat[ob->tilex-1][ob->tiley])\r
+ return false;\r
+ ob->tilex--;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case northwest:\r
+ if (actorat[ob->tilex-1][ob->tiley-1])\r
+ return false;\r
+ ob->tilex--;\r
+ ob->tiley--;\r
+ ob->distance = TILEGLOBAL;\r
+ return true;\r
+\r
+ case nodir:\r
+ return false;\r
+ }\r
+\r
+ Quit ("Walk: Bad dir");\r
+ return false;\r
+}\r
+\r
+\r
+\r
+/*\r
+==================================\r
+=\r
+= ChaseThink\r
+= have the current monster go after the player,\r
+= either diagonally or straight on\r
+=\r
+==================================\r
+*/\r
+\r
+void ChaseThink (objtype *obj, boolean diagonal)\r
+{\r
+ int deltax,deltay,i;\r
+ dirtype d[3];\r
+ dirtype tdir, olddir, turnaround;\r
+\r
+\r
+ olddir=obj->dir;\r
+ turnaround=opposite[olddir];\r
+\r
+ deltax=player->tilex - obj->tilex;\r
+ deltay=player->tiley - obj->tiley;\r
+\r
+ d[1]=nodir;\r
+ d[2]=nodir;\r
+\r
+ if (deltax>0)\r
+ d[1]= east;\r
+ if (deltax<0)\r
+ d[1]= west;\r
+ if (deltay>0)\r
+ d[2]=south;\r
+ if (deltay<0)\r
+ d[2]=north;\r
+\r
+ if (abs(deltay)>abs(deltax))\r
+ {\r
+ tdir=d[1];\r
+ d[1]=d[2];\r
+ d[2]=tdir;\r
+ }\r
+\r
+ if (d[1]==turnaround)\r
+ d[1]=nodir;\r
+ if (d[2]==turnaround)\r
+ d[2]=nodir;\r
+\r
+\r
+ if (diagonal)\r
+ { /*ramdiagonals try the best dir first*/\r
+ if (d[1]!=nodir)\r
+ {\r
+ obj->dir=d[1];\r
+ if (Walk(obj))\r
+ return; /*either moved forward or attacked*/\r
+ }\r
+\r
+ if (d[2]!=nodir)\r
+ {\r
+ obj->dir=d[2];\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ { /*ramstraights try the second best dir first*/\r
+\r
+ if (d[2]!=nodir)\r
+ {\r
+ obj->dir=d[2];\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+\r
+ if (d[1]!=nodir)\r
+ {\r
+ obj->dir=d[1];\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+ }\r
+\r
+/* there is no direct path to the player, so pick another direction */\r
+\r
+ obj->dir=olddir;\r
+ if (Walk(obj))\r
+ return;\r
+\r
+ if (US_RndT()>128) /*randomly determine direction of search*/\r
+ {\r
+ for (tdir=north;tdir<=west;tdir++)\r
+ {\r
+ if (tdir!=turnaround)\r
+ {\r
+ obj->dir=tdir;\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (tdir=west;tdir>=north;tdir--)\r
+ {\r
+ if (tdir!=turnaround)\r
+ {\r
+ obj->dir=tdir;\r
+ if (Walk(obj))\r
+ return;\r
+ }\r
+ }\r
+ }\r
+\r
+ obj->dir=turnaround;\r
+ Walk(obj); /*last chance, don't worry about returned value*/\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= MoveObj\r
+=\r
+=================\r
+*/\r
+\r
+void MoveObj (objtype *ob, long move)\r
+{\r
+ ob->distance -=move;\r
+\r
+ switch (ob->dir)\r
+ {\r
+ case north:\r
+ ob->y -= move;\r
+ return;\r
+ case northeast:\r
+ ob->x += move;\r
+ ob->y -= move;\r
+ return;\r
+ case east:\r
+ ob->x += move;\r
+ return;\r
+ case southeast:\r
+ ob->x += move;\r
+ ob->y += move;\r
+ return;\r
+ case south:\r
+ ob->y += move;\r
+ return;\r
+ case southwest:\r
+ ob->x -= move;\r
+ ob->y += move;\r
+ return;\r
+ case west:\r
+ ob->x -= move;\r
+ return;\r
+ case northwest:\r
+ ob->x -= move;\r
+ ob->y -= move;\r
+ return;\r
+\r
+ case nodir:\r
+ return;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= Chase\r
+=\r
+= returns true if hand attack range\r
+=\r
+=================\r
+*/\r
+\r
+boolean Chase (objtype *ob, boolean diagonal)\r
+{\r
+ long move;\r
+ long deltax,deltay,size;\r
+\r
+ ob->flags &= ~of_damagedone;\r
+\r
+ move = ob->speed*tics;\r
+ size = (long)ob->size + player->size + move + SIZE_TEST;\r
+\r
+ while (move)\r
+ {\r
+ deltax = ob->x - player->x;\r
+ deltay = ob->y - player->y;\r
+\r
+ if (deltax <= size && deltax >= -size\r
+ && deltay <= size && deltay >= -size)\r
+ {\r
+ CalcBounds (ob);\r
+ return true;\r
+ }\r
+\r
+ if (move < ob->distance)\r
+ {\r
+ MoveObj (ob,move);\r
+ break;\r
+ }\r
+ actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal\r
+ if (ob->dir == nodir)\r
+ ob->dir = north;\r
+\r
+ ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+ ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+ move -= ob->distance;\r
+\r
+ ChaseThink (ob,diagonal);\r
+ if (!ob->distance)\r
+ break; // no possible move\r
+ actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker\r
+ }\r
+ CalcBounds (ob);\r
+ return false;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ShootActor\r
+=\r
+===================\r
+*/\r
+\r
+void ShootActor (objtype *ob, unsigned damage)\r
+{\r
+ ob->hitpoints -= damage;\r
+\r
+ if (ob->hitpoints<=0)\r
+ {\r
+ switch (ob->obclass)\r
+ {\r
+ case reddemonobj:\r
+ ob->state = &s_red_demondie1;\r
+ break;\r
+ case succubusobj:\r
+ ob->state = &s_succubus_death1;\r
+ break;\r
+ case fatdemonobj:\r
+ ob->state = &s_fatdemon_blowup1;\r
+ break;\r
+ case godessobj:\r
+ ob->state = &s_godessdie1;\r
+ break;\r
+ case mageobj:\r
+ ob->state = &s_magedie1;\r
+ break;\r
+ case batobj:\r
+ ob->state = &s_batdie1;\r
+#if USE_INERT_LIST\r
+ ob->obclass = solidobj; // don't add this obj to inert list\r
+#endif\r
+ break;\r
+ case grelmobj:\r
+ ob->state = &s_greldie1;\r
+ break;\r
+\r
+ case zombieobj:\r
+ ob->state = &s_zombie_death1;\r
+ break;\r
+\r
+ case skeletonobj:\r
+ ob->state = &s_skel_die1;\r
+ break;\r
+\r
+ case antobj:\r
+ ob->state = &s_ant_die1;\r
+ break;\r
+\r
+ case wetobj:\r
+ ob->state = &s_wet_die1;\r
+#if USE_INERT_LIST\r
+ ob->obclass = solidobj; // don't add this obj to inert list\r
+#endif\r
+ break;\r
+\r
+ case eyeobj:\r
+ ob->state = &s_eye_die1;\r
+ break;\r
+\r
+ case sshotobj:\r
+ case eshotobj:\r
+ case mshotobj:\r
+ ob->state = &s_bonus_die;\r
+#if USE_INERT_LIST\r
+ ob->obclass = solidobj; // don't add these objs to inert list\r
+#endif\r
+ break;\r
+\r
+ case treeobj:\r
+ ob->state = &s_tree_death1;\r
+ ob->obclass = solidobj;\r
+ ob->temp1 = 3;\r
+ ob->flags &= ~of_damagedone;\r
+ CalcBounds(ob);\r
+ break;\r
+\r
+ case bunnyobj:\r
+ ob->state = &s_bunny_death1;\r
+ break;\r
+\r
+ case bonusobj:\r
+ case freezeobj:\r
+ switch (ob->temp1)\r
+ {\r
+ case B_POTION:\r
+ case B_CHEST:\r
+ case B_NUKE:\r
+ case B_BOLT:\r
+ ob->state = &s_pshot_exp1;\r
+ ob->obclass = expobj;\r
+ ob->ticcount = ob->state->tictime;\r
+ SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L));\r
+ bordertime = FLASHTICS<<2;\r
+ bcolor = 14;\r
+ VW_ColorBorder(14 | 56);\r
+ DisplaySMsg("Item destroyed", NULL);\r
+ status_flag = S_NONE;\r
+ status_delay = 80;\r
+ break;\r
+ }\r
+#if USE_INERT_LIST\r
+ ob->obclass = solidobj; // don't add this obj to inert list\r
+#endif\r
+ break;\r
+\r
+ }\r
+\r
+ if (ob->obclass != solidobj && ob->obclass != realsolidobj)\r
+ {\r
+ ob->obclass = inertobj;\r
+ ob->flags &= ~of_shootable;\r
+ actorat[ob->tilex][ob->tiley] = NULL;\r
+#if USE_INERT_LIST\r
+ MoveObjToInert(ob);\r
+#endif\r
+ }\r
+ else\r
+ {\r
+ if (ob->flags & of_forcefield)\r
+ {\r
+ ob->state = &s_force_field_die;\r
+ ob->flags &= ~of_shootable;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ switch (ob->obclass)\r
+ {\r
+ case reddemonobj:\r
+ if (!(random(8)))\r
+ ob->state = &s_red_demonouch;\r
+ else\r
+ return;\r
+ break;\r
+ case succubusobj:\r
+ ob->state = &s_succubus_ouch;\r
+ break;\r
+ case fatdemonobj:\r
+ ob->state = &s_fatdemon_ouch;\r
+ break;\r
+ case godessobj:\r
+ ob->state = &s_godessouch;\r
+ break;\r
+ case mageobj:\r
+ ob->state = &s_mageouch;\r
+ break;\r
+\r
+ case grelmobj:\r
+ ob->state = &s_grelouch;\r
+ break;\r
+\r
+ case zombieobj:\r
+ ob->state = &s_zombie_ouch;\r
+ break;\r
+\r
+ case antobj:\r
+ ob->state = &s_ant_ouch;\r
+ break;\r
+\r
+ case skeletonobj:\r
+ ob->state = &s_skel_ouch;\r
+ break;\r
+\r
+ case wetobj:\r
+ ob->state = &s_wet_ouch;\r
+ break;\r
+\r
+ case eyeobj:\r
+ ob->state = &s_eye_ouch;\r
+ break;\r
+\r
+ case treeobj:\r
+ ob->state = &s_tree_ouch;\r
+ break;\r
+\r
+ case bunnyobj:\r
+ ob->state = &s_bunny_ouch;\r
+ break;\r
+ }\r
+ }\r
+ ob->ticcount = ob->state->tictime;\r
+}\r
+\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_TRACE.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+//\r
+// TESTWALLVISABLE will set the global variable wallvisable to 1 or 0\r
+// depending on if tile.x,tile.y,wallon is visable from focal point\r
+//\r
+#define TESTWALLVISABLE { \\r
+ if (tile.y<focal.y) \\r
+ voffset = 0; \\r
+ else if (tile.y==focal.y) \\r
+ voffset = 3; \\r
+ else \\r
+ voffset = 6; \\r
+ if (tile.x==focal.x) \\r
+ voffset ++; \\r
+ else if (tile.x>focal.x) \\r
+ voffset += 2; \\r
+ wallvisable = visable[voffset][wallon]; }\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean aborttrace;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+unsigned wallvisable,voffset;\r
+\r
+\r
+fixed edgex,edgey;\r
+\r
+int wallon;\r
+int basecolor;\r
+\r
+walltype *oldwall;\r
+\r
+//\r
+// offsets from upper left corner of a tile to the left and right edges of\r
+// a given wall (NORTH-WEST)\r
+//\r
+fixed point1x[4] = {GLOBAL1,GLOBAL1,0 ,0 };\r
+fixed point1y[4] = {0 ,GLOBAL1,GLOBAL1,0 };\r
+\r
+fixed point2x[4] = {0 ,GLOBAL1,GLOBAL1,0 };\r
+fixed point2y[4] = {0 ,0 ,GLOBAL1 ,GLOBAL1};\r
+\r
+\r
+//\r
+// offset from tile.x,tile.y of the tile that shares wallon side\r
+// (side is not visable if it is shared)\r
+//\r
+int sharex[4] = { 0, 1, 0,-1};\r
+int sharey[4] = {-1, 0, 1, 0};\r
+\r
+//\r
+// amount to move tile.x,tile.y to follow wallon to another tile\r
+//\r
+int followx[4] = {-1, 0, 1, 0};\r
+int followy[4] = { 0,-1, 0, 1};\r
+\r
+//\r
+// cornerwall gives the wall on the same tile to start following when the\r
+// wall ends at an empty tile (go around an edge on same tile)\r
+// turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following\r
+// when the wall hits another tile (right angle corner)\r
+//\r
+int cornerwall[4] = {WEST,NORTH,EAST,SOUTH};\r
+int turnwall[4] = {EAST,SOUTH,WEST,NORTH};\r
+\r
+//\r
+// wall visabilities in reletive locations\r
+// -,- 0,- +,-\r
+// -,0 0,0 +,0\r
+// -,+ 0,+ +,+\r
+//\r
+int visable[9][4] =\r
+{\r
+ {0,1,1,0}, {0,0,1,0}, {0,0,1,1},\r
+ {0,1,0,0}, {0,0,0,0}, {0,0,0,1},\r
+ {1,1,0,0}, {1,0,0,0}, {1,0,0,1}\r
+};\r
+\r
+int startwall[9] = {2,2,3, 1,0,3, 1,0,0};\r
+int backupwall[9] = {3,3,0, 2,0,0, 2,1,1};\r
+\r
+\r
+int walllength;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ FUNCTIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+========================\r
+=\r
+= FollowTrace\r
+=\r
+========================\r
+*/\r
+\r
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max)\r
+{\r
+ int tx,ty,otx,oty;\r
+ long absdx,absdy,xstep,ystep;\r
+\r
+ tx = tracex>>TILESHIFT;\r
+ ty = tracey>>TILESHIFT;\r
+\r
+ spotvis[tx][ty] = true;\r
+\r
+ absdx=LABS(deltax);\r
+ absdy=LABS(deltay);\r
+\r
+ if (absdx>absdy)\r
+ {\r
+ ystep = (deltay<<8)/(absdx>>8);\r
+\r
+ if (!ystep)\r
+ ystep = deltay>0 ? 1 : -1;\r
+\r
+ oty = (tracey+ystep)>>TILESHIFT;\r
+ if (deltax>0)\r
+ {\r
+//###############\r
+//\r
+// step x by +1\r
+//\r
+//###############\r
+ do\r
+ {\r
+ tx++;\r
+ spotvis[tx][ty] = true;\r
+ tracey+=ystep;\r
+ ty = tracey>>TILESHIFT;\r
+\r
+ if (ty!=oty)\r
+ {\r
+ if (tilemap[tx-1][ty])\r
+ {\r
+ tile.x = tx-1;\r
+ tile.y = ty;\r
+ return 1;\r
+ }\r
+ oty = ty;\r
+ }\r
+ if (tilemap[tx][ty])\r
+ {\r
+ tile.x = tx;\r
+ tile.y = ty;\r
+ return 1;\r
+ }\r
+ } while (--max);\r
+ return 0;\r
+ }\r
+ else\r
+ {\r
+//###############\r
+//\r
+// step x by -1\r
+//\r
+//###############\r
+ do\r
+ {\r
+ tx--;\r
+ spotvis[tx][ty] = true;\r
+ tracey+=ystep;\r
+ ty = tracey>>TILESHIFT;\r
+\r
+ if (ty!=oty)\r
+ {\r
+ if (tilemap[tx][oty])\r
+ {\r
+ tile.x = tx;\r
+ tile.y = oty;\r
+ return 1;\r
+ }\r
+ oty = ty;\r
+ }\r
+ if (tilemap[tx][ty])\r
+ {\r
+ tile.x = tx;\r
+ tile.y = ty;\r
+ return 1;\r
+ }\r
+ } while (--max);\r
+ return 0;\r
+\r
+ }\r
+ }\r
+ else\r
+ {\r
+ xstep = (deltax<<8)/(absdy>>8);\r
+ if (!xstep)\r
+ xstep = deltax>0 ? 1 : -1;\r
+\r
+\r
+ otx = (tracex+xstep)>>TILESHIFT;\r
+ if (deltay>0)\r
+ {\r
+//###############\r
+//\r
+// step y by +1\r
+//\r
+//###############\r
+ do\r
+ {\r
+ ty++;\r
+ spotvis[tx][ty] = true;\r
+ tracex+=xstep;\r
+ tx = tracex>>TILESHIFT;\r
+\r
+ if (tx!=otx)\r
+ {\r
+ if (tilemap[tx][ty-1])\r
+ {\r
+ tile.x = tx;\r
+ tile.y = ty-1;\r
+ return 1;\r
+ }\r
+ otx = tx;\r
+ }\r
+ if (tilemap[tx][ty])\r
+ {\r
+ tile.x = tx;\r
+ tile.y = ty;\r
+ return 1;\r
+ }\r
+ } while (--max);\r
+ return 0;\r
+ }\r
+ else\r
+ {\r
+//###############\r
+//\r
+// step y by -1\r
+//\r
+//###############\r
+ do\r
+ {\r
+ ty--;\r
+ spotvis[tx][ty] = true;\r
+ tracex+=xstep;\r
+ tx = tracex>>TILESHIFT;\r
+\r
+ if (tx!=otx)\r
+ {\r
+ if (tilemap[otx][ty])\r
+ {\r
+ tile.x = otx;\r
+ tile.y = ty;\r
+ return 1;\r
+ }\r
+ otx = tx;\r
+ }\r
+ if (tilemap[tx][ty])\r
+ {\r
+ tile.x = tx;\r
+ tile.y = ty;\r
+ return 1;\r
+ }\r
+ } while (--max);\r
+ return 0;\r
+ }\r
+\r
+ }\r
+\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= BackTrace\r
+=\r
+= Traces backwards from edgex,edgey to viewx,viewy to see if a closer\r
+= tile obscures the given point. If it does, it finishes the wall and\r
+= starts a new one.\r
+= Returns true if a tile is hit.\r
+= Call with a 1 to have it automatically finish the current wall\r
+=\r
+=================\r
+*/\r
+\r
+int BackTrace (int finish)\r
+{\r
+ fixed tracex,tracey;\r
+ long deltax,deltay,absdx,absdy;\r
+ int steps,otx,oty,testx,testheight,offset,wall;\r
+\r
+ deltax = viewx-edgex;\r
+ deltay = viewy-edgey;\r
+\r
+ absdx = LABS(deltax);\r
+ absdy = LABS(deltay);\r
+\r
+ if (absdx>absdy)\r
+ steps = ABS(focal.x-(edgex>>TILESHIFT))-1;\r
+ else\r
+ steps = ABS(focal.y-(edgey>>TILESHIFT))-1;\r
+\r
+ if (steps<=0)\r
+ return 0;\r
+\r
+ otx = tile.x;\r
+ oty = tile.y;\r
+ if (!FollowTrace(edgex,edgey,deltax,deltay,steps))\r
+ return 0;\r
+\r
+//\r
+// if the start wall is behind the focal point, the trace went too far back\r
+//\r
+ if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2) // too close\r
+ {\r
+ if (tile.x == focal.x && tile.y == focal.y)\r
+ {\r
+ tile.x = otx;\r
+ tile.y = oty;\r
+ return 0;\r
+ }\r
+\r
+ if (tile.x<focal.x)\r
+ {\r
+ if (tile.y<focal.y)\r
+ wall = SOUTH;\r
+ else\r
+ wall = EAST;\r
+ }\r
+ else if (tile.x==focal.x)\r
+ {\r
+ if (tile.y<focal.y)\r
+ wall = SOUTH;\r
+ else\r
+ wall = NORTH;\r
+ }\r
+ else\r
+ {\r
+ if (tile.y<=focal.y)\r
+ wall = WEST;\r
+ else\r
+ wall = NORTH;\r
+ }\r
+\r
+ //\r
+ // rotate the X value to see if it is behind the view plane\r
+ //\r
+ if (TransformX (((long)tile.x<<16)+point1x[wall],\r
+ ((long)tile.y<<16)+point1y[wall]) < FOCALLENGTH)\r
+ {\r
+ tile.x = otx;\r
+ tile.y = oty;\r
+ return 0;\r
+ }\r
+ }\r
+\r
+//\r
+// if the old wall is still behind a closer wall, ignore the back trace\r
+// and continue on (dealing with limited precision...)\r
+//\r
+ if (finish && !FinishWall ()) // the wall is still behind a forward wall\r
+ {\r
+ tile.x = otx;\r
+ tile.y = oty;\r
+ rightwall->x1 = oldwall->x2; // common edge with last wall\r
+ rightwall->height1 = oldwall->height2;\r
+ return 0;\r
+ }\r
+\r
+\r
+//\r
+// back up along the intersecting face to find the rightmost wall\r
+//\r
+\r
+ if (tile.y<focal.y)\r
+ offset = 0;\r
+ else if (tile.y==focal.y)\r
+ offset = 3;\r
+ else\r
+ offset = 6;\r
+ if (tile.x==focal.x)\r
+ offset ++;\r
+ else if (tile.x>focal.x)\r
+ offset += 2;\r
+\r
+ wallon = backupwall[offset];\r
+\r
+ while (tilemap[tile.x][tile.y])\r
+ {\r
+ tile.x += followx[wallon];\r
+ tile.y += followy[wallon];\r
+ };\r
+\r
+ tile.x -= followx[wallon];\r
+ tile.y -= followy[wallon];\r
+\r
+ wallon = cornerwall[wallon]; // turn to first visable face\r
+\r
+ edgex = ((long)tile.x<<16);\r
+ edgey = ((long)tile.y<<16);\r
+\r
+ TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],\r
+ &rightwall->x1,&rightwall->height1);\r
+\r
+ basecolor = tilemap[tile.x][tile.y];\r
+\r
+ return 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= ForwardTrace\r
+=\r
+= Traces forwards from edgex,edgey along the line from viewx,viewy until\r
+= a solid tile is hit. Sets tile.x,tile.y\r
+=\r
+=================\r
+*/\r
+\r
+void ForwardTrace (void)\r
+{\r
+ int offset;\r
+ fixed tracex,tracey;\r
+ long deltax,deltay;\r
+\r
+ deltax = edgex-viewx;\r
+ deltay = edgey-viewy;\r
+\r
+ FollowTrace(edgex,edgey,deltax,deltay,0);\r
+\r
+ if (tile.y<focal.y)\r
+ offset = 0;\r
+ else if (tile.y==focal.y)\r
+ offset = 3;\r
+ else\r
+ offset = 6;\r
+ if (tile.x==focal.x)\r
+ offset ++;\r
+ else if (tile.x>focal.x)\r
+ offset += 2;\r
+\r
+ wallon = startwall[offset];\r
+\r
+//\r
+// start the new wall\r
+//\r
+ edgex = ((long)tile.x<<16);\r
+ edgey = ((long)tile.y<<16);\r
+\r
+//\r
+// if entire first wall is invisable, corner\r
+//\r
+ TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon],\r
+ &rightwall->x2,&rightwall->height2);\r
+\r
+ if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]\r
+ || rightwall->x2 < (rightwall-1)->x2 )\r
+ wallon = cornerwall [wallon];\r
+\r
+//\r
+// transform first point\r
+//\r
+\r
+ TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],\r
+ &rightwall->x1,&rightwall->height1);\r
+\r
+ basecolor = tilemap[tile.x][tile.y];\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= FinishWall\r
+=\r
+= Transforms edgex,edgey as the next point of the current wall\r
+= and sticks it in the wall list\r
+=\r
+=================\r
+*/\r
+\r
+int FinishWall (void)\r
+{\r
+ char num[20];\r
+\r
+ oldwall = rightwall;\r
+\r
+ rightwall->color = basecolor;\r
+\r
+ TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2);\r
+\r
+ if (rightwall->x2 <= (rightwall-1)->x2+2\r
+ && rightwall->height2 < (rightwall-1)->height2 )\r
+ return 0;\r
+\r
+ rightwall->walllength = walllength;\r
+\r
+ switch (wallon)\r
+ {\r
+ case north:\r
+ case south:\r
+ rightwall->side = 0;\r
+ rightwall->planecoord = edgey;\r
+ break;\r
+\r
+ case west:\r
+ case east:\r
+ rightwall->side = 1;\r
+ rightwall->planecoord = edgex;\r
+ break;\r
+ }\r
+\r
+ walllength = 1;\r
+\r
+ rightwall++;\r
+\r
+ return 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= InsideCorner\r
+=\r
+=================\r
+*/\r
+\r
+void InsideCorner (void)\r
+{\r
+ int offset;\r
+\r
+ //\r
+ // the wall turned -90 degrees, so draw what we have, move to the new tile,\r
+ // change wallon, change color, and continue following.\r
+ //\r
+ FinishWall ();\r
+\r
+ tile.x += sharex[wallon];\r
+ tile.y += sharey[wallon];\r
+\r
+ wallon = turnwall[wallon];\r
+\r
+ //\r
+ // if the new wall is visable, continue following it. Otherwise\r
+ // follow it backwards until it turns\r
+ //\r
+ TESTWALLVISABLE;\r
+\r
+ if (wallvisable)\r
+ {\r
+ //\r
+ // just turn to the next wall and continue\r
+ //\r
+ rightwall->x1 = oldwall->x2; // common edge with last wall\r
+ rightwall->height1 = oldwall->height2;\r
+ basecolor = tilemap[tile.x][tile.y];\r
+ return; // continue from here\r
+ }\r
+\r
+ //\r
+ // back follow the invisable wall until it turns, then follow that\r
+ //\r
+ do\r
+ {\r
+ tile.x += followx[wallon];\r
+ tile.y += followy[wallon];\r
+ } while (tilemap[tile.x][tile.y]);\r
+\r
+ tile.x -= followx[wallon];\r
+ tile.y -= followy[wallon];\r
+\r
+ wallon = cornerwall[wallon]; // turn to first visable face\r
+\r
+ edgex = ((long)tile.x<<16)+point1x[wallon];\r
+ edgey = ((long)tile.y<<16)+point1y[wallon];\r
+\r
+ if (!BackTrace(0)) // backtrace without finishing a wall\r
+ {\r
+ TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1);\r
+ basecolor = tilemap[tile.x][tile.y];\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= OutsideCorner\r
+=\r
+=================\r
+*/\r
+\r
+void OutsideCorner (void)\r
+{\r
+ int offset;\r
+\r
+ //\r
+ // edge is the outside edge of a corner, so draw the current wall and\r
+ // turn the corner (+90 degrees)\r
+ //\r
+ FinishWall ();\r
+\r
+ tile.x -= followx[wallon]; // backup to the real tile\r
+ tile.y -= followy[wallon];\r
+ wallon = cornerwall[wallon];\r
+\r
+ //\r
+ // if the new wall is visable, continue following it. Otherwise\r
+ // trace a ray from the corner to find a wall in the distance to\r
+ // follow\r
+ //\r
+ TESTWALLVISABLE;\r
+\r
+ if (wallvisable)\r
+ {\r
+ //\r
+ // the new wall is visable, so just continue on\r
+ //\r
+ rightwall->x1 = oldwall->x2; // common edge with last wall\r
+ rightwall->height1 = oldwall->height2;\r
+ return; // still on same tile, so color is ok\r
+ }\r
+\r
+//\r
+// start from a new tile further away\r
+//\r
+ ForwardTrace(); // find the next wall further back\r
+\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= FollowWalls\r
+=\r
+= Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it\r
+= until something else is seen or the entire view area is covered\r
+=\r
+=================\r
+*/\r
+\r
+void FollowWalls (void)\r
+{\r
+ int height,newcolor,offset,wall;\r
+\r
+//####################\r
+//\r
+// figure leftmost wall of new tile\r
+//\r
+//####################\r
+\r
+restart:\r
+\r
+ walllength = 1;\r
+\r
+ if (tile.y<focal.y)\r
+ offset = 0;\r
+ else if (tile.y==focal.y)\r
+ offset = 3;\r
+ else\r
+ offset = 6;\r
+ if (tile.x==focal.x)\r
+ offset ++;\r
+ else if (tile.x>focal.x)\r
+ offset += 2;\r
+\r
+ wallon = startwall[offset];\r
+\r
+//\r
+// if the start wall is inside a block, skip it by cornering to the second wall\r
+//\r
+ if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])\r
+ wallon = cornerwall [wallon];\r
+\r
+//\r
+// transform first edge to screen coordinates\r
+//\r
+ edgex = ((long)tile.x<<16);\r
+ edgey = ((long)tile.y<<16);\r
+\r
+ TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],\r
+ &rightwall->x1,&rightwall->height1);\r
+\r
+ basecolor = tilemap[tile.x][tile.y];\r
+\r
+//##################\r
+//\r
+// follow the wall as long as possible\r
+//\r
+//##################\r
+\r
+advance:\r
+\r
+ do // while ( tile.x != right.x || tile.y != right.y)\r
+ {\r
+//\r
+// check for conditions that shouldn't happed...\r
+//\r
+ if (rightwall->x1 > VIEWXH) // somehow missed right tile...\r
+ return;\r
+\r
+ if (rightwall == &walls[DANGERHIGH])\r
+ {\r
+ //\r
+ // somethiing got messed up! Correct by thrusting ahead...\r
+ //\r
+// VW_ColorBorder(6);\r
+ bordertime = 60;\r
+ Thrust(player->angle,TILEGLOBAL/4);\r
+ player->angle+=5;\r
+ if (player->angle>ANGLES)\r
+ player->angle-=ANGLES;\r
+ aborttrace = true;\r
+ return;\r
+\r
+#if 0\r
+ strcpy (str,"Wall list overflow at LE:");\r
+ itoa(mapon+1,str2,10);\r
+ strcat (str,str2);\r
+ strcat (str," X:");\r
+ ltoa(objlist[0].x,str2,10);\r
+ strcat (str,str2);\r
+ strcat (str," Y:");\r
+ ltoa(objlist[0].y,str2,10);\r
+ strcat (str,str2);\r
+ strcat (str," AN:");\r
+ itoa(objlist[0].angle,str2,10);\r
+ strcat (str,str2);\r
+\r
+ Quit (str);\r
+#endif\r
+ }\r
+\r
+//\r
+// proceed along wall\r
+//\r
+\r
+ edgex = ((long)tile.x<<16)+point2x[wallon];\r
+ edgey = ((long)tile.y<<16)+point2y[wallon];\r
+\r
+ if (BackTrace(1)) // went behind a closer wall\r
+ continue;\r
+\r
+ //\r
+ // advance to next tile along wall\r
+ //\r
+ tile.x += followx[wallon];\r
+ tile.y += followy[wallon];\r
+\r
+ if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])\r
+ {\r
+ InsideCorner (); // turn at a corner\r
+ continue;\r
+ }\r
+\r
+ newcolor = tilemap[tile.x][tile.y];\r
+\r
+ if (!newcolor) // turn around an edge\r
+ {\r
+ OutsideCorner ();\r
+ continue;\r
+ }\r
+\r
+ if (newcolor != basecolor)\r
+ {\r
+ //\r
+ // wall changed color, so draw what we have and continue following\r
+ //\r
+ FinishWall ();\r
+ rightwall->x1 = oldwall->x2; // new wall shares this edge\r
+ rightwall->height1 = oldwall->height2;\r
+ basecolor = newcolor;\r
+\r
+ continue;\r
+ }\r
+ walllength++;\r
+ } while (tile.x != right.x || tile.y != right.y);\r
+\r
+\r
+\r
+//######################\r
+//\r
+// draw the last tile\r
+//\r
+//######################\r
+\r
+ edgex = ((long)tile.x<<16)+point2x[wallon];\r
+ edgey = ((long)tile.y<<16)+point2y[wallon];\r
+ FinishWall();\r
+\r
+ wallon = cornerwall[wallon];\r
+\r
+ //\r
+ // if the corner wall is visable, draw it\r
+ //\r
+ TESTWALLVISABLE;\r
+\r
+ if (wallvisable)\r
+ {\r
+ rightwall->x1 = oldwall->x2; // common edge with last wall\r
+ rightwall->height1 = oldwall->height2;\r
+ edgex = ((long)tile.x<<16)+point2x[wallon];\r
+ edgey = ((long)tile.y<<16)+point2y[wallon];\r
+ FinishWall();\r
+ }\r
+\r
+}\r
+\r
+//===========================================================================\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_WIZ.C\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+////////#define NUMSCROLLS 8\r
+\r
+#define SHOWITEMS 9\r
+\r
+#define NUKETIME 40\r
+#define NUMBOLTS 10\r
+#define BOLTTICS 6\r
+\r
+#define STATUSCOLOR 8\r
+#define TEXTCOLOR 14\r
+\r
+#define SIDEBARWIDTH 5\r
+\r
+#define BODYLINE 8\r
+#define POWERLINE 80\r
+\r
+#define SPECTILESTART 0 // 18\r
+\r
+\r
+#define SHOTDAMAGE 1\r
+#define BIGSHOTDAMAGE 3\r
+\r
+\r
+#define PLAYERSPEED 5120\r
+#define RUNSPEED (8192<<1)\r
+\r
+#define SHOTSPEED 10000\r
+\r
+//#define LASTWALLTILE 47\r
+//#define LASTSPECIALTILE 37\r
+\r
+#define LASTTILE (LASTWALLPIC-FIRSTWALLPIC) // 47\r
+\r
+#define FIRETIME 2\r
+\r
+#define HANDPAUSE 60\r
+\r
+#define RIGHTEDGE 205;\r
+#define LEFTEDGE 95;\r
+#define PRNY 32;\r
+#define WINX 10;\r
+#define WINY 32;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+long lastnuke,lasthand;\r
+int lasttext;\r
+int handheight;\r
+int boltsleft,bolttimer;\r
+short RadarXY[MAX_RADAR_BLIPS][3]={-1,-1,-1};\r
+short radarx=RADARX,radary=RADARY,radar_xcenter=RADAR_XCENTER,radar_ycenter=RADAR_YCENTER;\r
+int key_x[4]={24,27,27,24},key_y[4]={30,57,30,57};\r
+\r
+boolean redraw_gems,button0down;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+int lastradar;\r
+unsigned lastfiretime;\r
+\r
+int strafeangle[9] = {0,90,180,270,45,135,225,315,0};\r
+\r
+short RotateAngle = -1; // -1 == No Angle to turn to...\r
+short FreezeTime = 0; // Stops all think (except player)\r
+short RotateSpeed; // Speed (and dir) to rotate..\r
+\r
+\r
+//===========================================================================\r
+\r
+void CalcBounds(objtype *ob);\r
+boolean VerifyGateExit(void);\r
+void DrawNSEWIcons(void);\r
+void DrawGems(void);\r
+void DrawRadar (void);\r
+void DrawChar (unsigned x, unsigned y, unsigned tile);\r
+void RedrawStatusWindow (void);\r
+void GiveBolt (void);\r
+void TakeBolt (void);\r
+void GiveNuke (void);\r
+void TakeNuke (void);\r
+void GivePotion (void);\r
+void TakePotion (void);\r
+void GiveKey (int keytype);\r
+void TakeKey (int keytype);\r
+////////////void GiveScroll (int scrolltype,boolean show);\r
+////////////void ReadScroll (int scroll);\r
+////////////void DrawScrolls(void);\r
+\r
+void DrawNum(short x,short y,short value,short maxdigits);\r
+\r
+//----------\r
+\r
+void Shoot (void);\r
+void BigShoot (void);\r
+void CastBolt (void);\r
+void CastNuke (void);\r
+void DrinkPotion (void);\r
+\r
+//----------\r
+void DrawHealth(void);\r
+\r
+void SpawnPlayer (int tilex, int tiley, int dir);\r
+void Thrust (int angle, unsigned speed);\r
+void T_Player (objtype *ob);\r
+\r
+//void AddPoints (int points);\r
+\r
+void ClipMove (objtype *ob, long xmove, long ymove);\r
+boolean ShotClipMove (objtype *ob, long xmove, long ymove);\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= DrawChar\r
+=\r
+===============\r
+*/\r
+\r
+void DrawChar (unsigned x, unsigned y, unsigned tile)\r
+{\r
+ unsigned junk = latchpics[0];\r
+\r
+ EGAWRITEMODE(1);\r
+asm mov bx,[y]\r
+asm shl bx,1\r
+asm mov di,[WORD PTR ylookup+bx]\r
+asm add di,[x]\r
+asm mov si,[tile]\r
+asm shl si,1\r
+asm shl si,1\r
+asm shl si,1\r
+asm add si,[junk] // the damn inline assembler won't reference latchpics\r
+asm mov ax,[screenseg]\r
+asm mov es,ax\r
+asm mov ds,ax\r
+asm mov dx,SCREENWIDTH-1\r
+asm movsb\r
+asm add di,dx\r
+asm movsb\r
+asm add di,dx\r
+asm movsb\r
+asm add di,dx\r
+asm movsb\r
+asm add di,dx\r
+asm movsb\r
+asm add di,dx\r
+asm movsb\r
+asm add di,dx\r
+asm movsb\r
+asm add di,dx\r
+asm movsb\r
+\r
+asm mov ax,ss\r
+asm mov ds,ax\r
+ EGAWRITEMODE(0);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= RedrawStatusWindow\r
+=\r
+===============\r
+*/\r
+\r
+void RedrawStatusWindow (void)\r
+{\r
+ short keytype;\r
+\r
+ EGABITMASK(0xff);\r
+ for (keytype=0; keytype<4; keytype++)\r
+ DrawNum(key_x[keytype],key_y[keytype],gamestate.keys[keytype],2);\r
+ DrawNum(20,54,gamestate.potions,2);\r
+ DrawNum(20,36,gamestate.nukes,2);\r
+ DrawNum(20,18,gamestate.bolts,2);\r
+\r
+ DrawHealth();\r
+ DrawRadar();\r
+ EGAWRITEMODE(0);\r
+ DrawGems();\r
+//////// DrawScrolls();\r
+ redraw_gems = false;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveBolt\r
+=\r
+===============\r
+*/\r
+\r
+void GiveBolt (void)\r
+{\r
+ if (gamestate.bolts == 99)\r
+ return;\r
+\r
+ SD_PlaySound (GETBOLTSND);\r
+ DrawNum(20,18,++gamestate.bolts,2);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeBolt\r
+=\r
+===============\r
+*/\r
+\r
+void TakeBolt (void)\r
+{\r
+ SD_PlaySound (USEBOLTSND);\r
+ DrawNum(20,18,--gamestate.bolts,2);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveNuke\r
+=\r
+===============\r
+*/\r
+\r
+void GiveNuke (void)\r
+{\r
+ if (gamestate.nukes == 99)\r
+ return;\r
+\r
+ SD_PlaySound (GETNUKESND);\r
+ DrawNum(20,36,++gamestate.nukes,2);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeNuke\r
+=\r
+===============\r
+*/\r
+\r
+void TakeNuke (void)\r
+{\r
+ SD_PlaySound (USENUKESND);\r
+ DrawNum(20,36,--gamestate.nukes,2);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GivePotion\r
+=\r
+===============\r
+*/\r
+\r
+void GivePotion (void)\r
+{\r
+ if (gamestate.potions == 99)\r
+ return;\r
+\r
+ SD_PlaySound (GETPOTIONSND);\r
+ DrawNum(20,54,++gamestate.potions,2);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakePotion\r
+=\r
+===============\r
+*/\r
+\r
+void TakePotion (void)\r
+{\r
+ SD_PlaySound (USEPOTIONSND);\r
+ DrawNum(20,54,--gamestate.potions,2);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveKey\r
+=\r
+===============\r
+*/\r
+\r
+void GiveKey (int keytype)\r
+{\r
+ int i,j,x;\r
+\r
+ if (gamestate.keys[keytype] == 99)\r
+ return;\r
+\r
+ SD_PlaySound (GETKEYSND);\r
+ DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeKey\r
+=\r
+===============\r
+*/\r
+\r
+void TakeKey (int keytype)\r
+{\r
+ int i,j,x;\r
+ char *key_colors[] = {"a RED key",\r
+ "a YELLOW key",\r
+ "a GREEN key",\r
+ "a BLUE key"};\r
+\r
+\r
+ SD_PlaySound (USEKEYSND);\r
+ DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2);\r
+ displayofs = bufferofs = screenloc[screenpage];\r
+ CenterWindow(20,5);\r
+ US_CPrint("\nYou use\n");\r
+ US_CPrint(key_colors[keytype]);\r
+ VW_UpdateScreen();\r
+ VW_WaitVBL(120);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveGem\r
+=\r
+===============\r
+*/\r
+\r
+void GiveGem (int gemtype)\r
+{\r
+#if 0\r
+ int i,j,x;\r
+\r
+ SD_PlaySound (GETKEYSND);\r
+ DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2);\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeGem\r
+=\r
+===============\r
+*/\r
+\r
+void TakeGem (int gemtype)\r
+{\r
+#if 0\r
+ int i,j,x;\r
+\r
+ SD_PlaySound (USEKEYSND);\r
+ DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2);\r
+#endif\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= DrawGem\r
+=\r
+===============\r
+*/\r
+\r
+void DrawGems()\r
+{\r
+ short loop;\r
+\r
+ redraw_gems = false;\r
+\r
+ bufferofs = 0;\r
+ LatchDrawPic (31,51,RADAR_BOTTOMPIC);\r
+ for (loop=0; loop<5; loop++)\r
+ if (gamestate.gems[loop])\r
+ LatchDrawPic (32+loop,53,RADAR_RGEMPIC+loop);\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+\r
+/*\r
+===============\r
+=\r
+= GiveScroll\r
+=\r
+===============\r
+*/\r
+\r
+void GiveScroll (int scrolltype,boolean show)\r
+{\r
+ int i,j,x,y,scrollnum;\r
+\r
+ SD_PlaySound (GETSCROLLSND);\r
+ gamestate.scrolls[scrolltype] = true;\r
+\r
+ y = 30 + ((scrolltype > 3) * 10);\r
+ x = 26 + (scrolltype % 4);\r
+ DrawChar(x,y,SCROLLCHARS+scrolltype);\r
+\r
+ if (show)\r
+ ReadScroll(scrolltype);\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= DrawScrolls\r
+=\r
+= Force draw of all scrolls\r
+=\r
+===============\r
+*/\r
+void DrawScrolls()\r
+{\r
+ int loop,x,y;\r
+\r
+ VW_Bar(210,30,30,18,0xf);\r
+\r
+ for (loop=0;loop<8;loop++)\r
+ if (gamestate.scrolls[loop])\r
+ {\r
+ y = 30 + ((loop > 3) * 10);\r
+ x = 26 + (loop % 4);\r
+ DrawChar(x,y,SCROLLCHARS+loop);\r
+ }\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= GivePoints\r
+=\r
+===============\r
+*/\r
+\r
+void GivePoints (int points)\r
+{\r
+ pointcount = 1;\r
+ pointsleft += points;\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= AddPoints\r
+=\r
+===============\r
+*/\r
+\r
+void AddPoints (int points)\r
+{\r
+ char str[10];\r
+ int len,x,i;\r
+\r
+ gamestate.score += points;\r
+\r
+ ltoa (gamestate.score,str,10);\r
+ len = strlen (str);\r
+\r
+ x=24+(8-len);\r
+ for (i=0;i<len;i++)\r
+ DrawChar(x++,40,NUMBERCHARS+str[i]-'0');\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawHealth\r
+=\r
+===============\r
+*/\r
+void DrawHealth()\r
+{\r
+ char picnum;\r
+ int percentage;\r
+\r
+ percentage = PERCENTAGE(100,MAXBODY,gamestate.body,9);\r
+\r
+ DrawNum(11,57,percentage,3);\r
+\r
+ if (percentage > 75)\r
+ picnum = FACE1PIC;\r
+ else\r
+ if (percentage > 50)\r
+ picnum = FACE2PIC;\r
+ else\r
+ if (percentage > 25)\r
+ picnum = FACE3PIC;\r
+ else\r
+ if (percentage)\r
+ picnum = FACE4PIC;\r
+ else\r
+ {\r
+ picnum = FACE5PIC;\r
+ CA_CacheGrChunk (picnum);\r
+ }\r
+\r
+ bufferofs = 0;\r
+ if (!percentage)\r
+ {\r
+ UNMARKGRCHUNK(picnum);\r
+// VW_DrawPic(8,14,picnum);\r
+ VW_DrawPic(10,14,picnum);\r
+ }\r
+ else\r
+ LatchDrawPic(10,14,picnum);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawFreezeTime\r
+=\r
+===============\r
+*/\r
+void DrawFreezeTime()\r
+{\r
+ long percentage;\r
+ percentage = PERCENTAGE(100,MAXFREEZETIME,(long)FreezeTime,7);\r
+ DrawNum(23,70,percentage,3);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawNum\r
+=\r
+===============\r
+*/\r
+void DrawNum(short x,short y,short value,short maxdigits)\r
+{\r
+ char str[10],len,i;\r
+\r
+ itoa(value,str,10);\r
+ len=strlen(str);\r
+\r
+ for (i=len; i<maxdigits; i++)\r
+ DrawChar(x++,y,BLANKCHAR);\r
+\r
+ for (i=0;i<len;i++)\r
+ DrawChar(x++,y,NUMBERCHARS+str[i]-'0');\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveChest\r
+=\r
+===============\r
+*/\r
+\r
+void GiveChest(void)\r
+{\r
+ char i;\r
+\r
+ for (i=0;i<random(4);i++)\r
+ {\r
+ GiveBolt();\r
+ SD_WaitSoundDone();\r
+ }\r
+\r
+ for (i=0;i<random(3);i++)\r
+ {\r
+ GiveNuke();\r
+ SD_WaitSoundDone();\r
+ }\r
+\r
+ for (i=0;i<random(2);i++)\r
+ {\r
+ GivePotion();\r
+ SD_WaitSoundDone();\r
+ }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveGoal\r
+=\r
+===============\r
+*/\r
+\r
+void GiveGoal (void)\r
+{\r
+ SD_PlaySound (GETPOINTSSND);\r
+ playstate = ex_victorious;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= DrawLevelNumber\r
+=\r
+===============\r
+*/\r
+\r
+void DrawLevelNumber (int number)\r
+{\r
+ char str[10];\r
+ int len;\r
+ unsigned temp;\r
+\r
+ bufferofs = 0;\r
+ if (number<9)\r
+ PrintX=13;\r
+ else\r
+ PrintX = 5;\r
+ PrintY = 4;\r
+ VW_Bar (5,4,16,9,STATUSCOLOR);\r
+ temp = fontcolor;\r
+ fontcolor = TEXTCOLOR^STATUSCOLOR;\r
+ US_PrintUnsigned (number+1);\r
+ fontcolor = temp;\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawText\r
+=\r
+===============\r
+*/\r
+\r
+void DrawText (boolean draw_text_whether_it_needs_it_or_not)\r
+{\r
+ unsigned number;\r
+ char str[80];\r
+ char far *text;\r
+ unsigned temp;\r
+\r
+ //\r
+ // draw a new text description if needed\r
+ //\r
+ number = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex)-NAMESTART;\r
+\r
+ if ( number>26 )\r
+ number = 0;\r
+\r
+ if ((number == lasttext) && (!draw_text_whether_it_needs_it_or_not))\r
+ return;\r
+\r
+ lasttext = number;\r
+\r
+ text = (char _seg *)grsegs[LEVEL1TEXT+mapon]+textstarts[number];\r
+\r
+ _fmemcpy (str,text,80);\r
+ DisplayMsg(str,NULL);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DisplayMsg\r
+=\r
+===============\r
+*/\r
+\r
+char DisplayMsg(char *text,char *choices)\r
+{\r
+ char ch=true;\r
+ short temp;\r
+\r
+ bufferofs = 0;\r
+ PrintY = 1;\r
+ WindowX = 20;\r
+ WindowW = 270;\r
+\r
+ VW_Bar (WindowX,2,WindowW,8,STATUSCOLOR);\r
+ temp = fontcolor;\r
+ fontcolor = TEXTCOLOR^STATUSCOLOR;\r
+ US_CPrintLine (text);\r
+ fontcolor = temp;\r
+\r
+ if (choices)\r
+ {\r
+ ch=GetKeyChoice(choices,true);\r
+ LastScan = 0;\r
+ }\r
+\r
+ return(ch);\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= DisplaySMsg\r
+=\r
+===============\r
+*/\r
+char DisplaySMsg(char *text,char *choices)\r
+{\r
+ char ch=true;\r
+ short temp;\r
+\r
+ bufferofs = 0;\r
+ PrintY = 69;\r
+ WindowX = 98;\r
+ WindowW = 115;\r
+\r
+ VW_Bar(WindowX,PrintY+1,WindowW,8,STATUSCOLOR);\r
+ temp = fontcolor;\r
+ fontcolor = TEXTCOLOR^STATUSCOLOR;\r
+ US_CPrintLine (text);\r
+ fontcolor = temp;\r
+\r
+ if (choices)\r
+ {\r
+ ch=GetKeyChoice(choices,true);\r
+ LastScan = 0;\r
+ }\r
+\r
+ return(ch);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawRadar\r
+=\r
+===============\r
+*/\r
+\r
+void DrawRadar (void)\r
+{\r
+ int angle,number;\r
+ short objnum;\r
+\r
+ bufferofs = 0;\r
+ LatchDrawPic (radarx,radary,RADAR_TOPPIC);\r
+\r
+ asm cli\r
+ asm mov dx,GC_INDEX\r
+ asm mov ax,2*256+GC_MODE\r
+ asm out dx,ax // write mode 2\r
+\r
+ asm mov ax,GC_DATAROTATE\r
+ asm out dx,ax // no rotation / logical operation\r
+\r
+ asm mov dx,SC_INDEX\r
+ asm mov al,SC_MAPMASK\r
+ asm mov ah,15\r
+ asm out dx,ax // write to all four planes\r
+ asm sti\r
+\r
+ objnum = 0;\r
+ while (RadarXY[objnum][2] != -1)\r
+ {\r
+ RadarBlip(radar_xcenter+RadarXY[objnum][0],radar_ycenter+RadarXY[objnum][1],RadarXY[objnum][2]);\r
+ objnum++;\r
+ }\r
+\r
+ asm cli\r
+ asm mov dx,GC_INDEX\r
+ asm mov ax,255*256+GC_BITMASK\r
+ asm out dx,ax // reset bitmask to %11111111\r
+ asm sti\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// DrawNSEWIcons(void)\r
+//--------------------------------------------------------------------------\r
+\r
+void DrawRadarObj(short dx, short dy, unsigned sprnum,signed long psin,signed long pcos);\r
+\r
+void DrawNSEWIcons()\r
+{\r
+ signed x,y;\r
+\r
+ x = -FixedByFrac(RADAR_X_IRADIUS,costable[player->angle]);\r
+ y = -FixedByFrac(RADAR_Y_IRADIUS,sintable[player->angle]);\r
+\r
+ VWB_DrawSprite(radar_xcenter+x-3,radar_ycenter+y-3,NORTHICONSPR);\r
+\r
+}\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= DrawBars\r
+=\r
+===============\r
+*/\r
+\r
+void DrawBars (void)\r
+{\r
+ int i;\r
+ unsigned source,dest,topline;\r
+\r
+ for (i=0;i<3;i++)\r
+ {\r
+ bufferofs = screenloc[i];\r
+ VW_Bar (34*8,POWERLINE,40,MAXSHOTPOWER,1);\r
+ }\r
+ EGAWRITEMODE(1);\r
+ asm mov es,[screenseg]\r
+\r
+//\r
+// shot power\r
+//\r
+ if (gamestate.shotpower)\r
+ {\r
+ topline = MAXSHOTPOWER - gamestate.shotpower;\r
+\r
+ source = latchpics[SHOTPOWERPIC-FIRSTLATCHPIC]+topline*SIDEBARWIDTH;\r
+ dest = (POWERLINE+topline)*SCREENWIDTH+34;\r
+\r
+ asm mov si,[source]\r
+ asm mov di,[dest]\r
+\r
+ asm mov cx,[WORD PTR gamestate.shotpower]\r
+newline:\r
+ asm mov al,[es:si]\r
+ asm mov [es:di+PAGE1START],al\r
+ asm mov [es:di+PAGE2START],al\r
+ asm mov [es:di+PAGE3START],al\r
+ asm mov al,[es:si+1]\r
+ asm mov [es:di+1+PAGE1START],al\r
+ asm mov [es:di+1+PAGE2START],al\r
+ asm mov [es:di+1+PAGE3START],al\r
+ asm mov al,[es:si+2]\r
+ asm mov [es:di+2+PAGE1START],al\r
+ asm mov [es:di+2+PAGE2START],al\r
+ asm mov [es:di+2+PAGE3START],al\r
+ asm mov al,[es:si+3]\r
+ asm mov [es:di+3+PAGE1START],al\r
+ asm mov [es:di+3+PAGE2START],al\r
+ asm mov [es:di+3+PAGE3START],al\r
+ asm mov al,[es:si+4]\r
+ asm mov [es:di+4+PAGE1START],al\r
+ asm mov [es:di+4+PAGE2START],al\r
+ asm mov [es:di+4+PAGE3START],al\r
+\r
+ asm add di,SCREENWIDTH\r
+ asm add si,5\r
+\r
+ asm loop newline\r
+ }\r
+\r
+//\r
+// body\r
+//\r
+ if (gamestate.body)\r
+ {\r
+ source = latchpics[BODYPIC-FIRSTLATCHPIC];\r
+ dest = BODYLINE*SCREENWIDTH+34;\r
+\r
+ asm mov si,[source]\r
+ asm mov di,[dest]\r
+\r
+ asm mov cx,[WORD PTR gamestate.body]\r
+newline2:\r
+ asm mov al,[es:si]\r
+ asm mov [es:di+PAGE1START],al\r
+ asm mov [es:di+PAGE2START],al\r
+ asm mov [es:di+PAGE3START],al\r
+ asm mov al,[es:si+1]\r
+ asm mov [es:di+1+PAGE1START],al\r
+ asm mov [es:di+1+PAGE2START],al\r
+ asm mov [es:di+1+PAGE3START],al\r
+ asm mov al,[es:si+2]\r
+ asm mov [es:di+2+PAGE1START],al\r
+ asm mov [es:di+2+PAGE2START],al\r
+ asm mov [es:di+2+PAGE3START],al\r
+ asm mov al,[es:si+3]\r
+ asm mov [es:di+3+PAGE1START],al\r
+ asm mov [es:di+3+PAGE2START],al\r
+ asm mov [es:di+3+PAGE3START],al\r
+ asm mov al,[es:si+4]\r
+ asm mov [es:di+4+PAGE1START],al\r
+ asm mov [es:di+4+PAGE2START],al\r
+ asm mov [es:di+4+PAGE3START],al\r
+\r
+ asm add di,SCREENWIDTH\r
+ asm add si,5\r
+\r
+ asm loop newline2\r
+ }\r
+\r
+ if (gamestate.body != MAXBODY)\r
+ {\r
+ source = latchpics[NOBODYPIC-FIRSTLATCHPIC]+gamestate.body*SIDEBARWIDTH;\r
+ dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34;\r
+ topline = MAXBODY-gamestate.body;\r
+\r
+ asm mov si,[source]\r
+ asm mov di,[dest]\r
+\r
+ asm mov cx,[WORD PTR topline]\r
+newline3:\r
+ asm mov al,[es:si]\r
+ asm mov [es:di+PAGE1START],al\r
+ asm mov [es:di+PAGE2START],al\r
+ asm mov [es:di+PAGE3START],al\r
+ asm mov al,[es:si+1]\r
+ asm mov [es:di+1+PAGE1START],al\r
+ asm mov [es:di+1+PAGE2START],al\r
+ asm mov [es:di+1+PAGE3START],al\r
+ asm mov al,[es:si+2]\r
+ asm mov [es:di+2+PAGE1START],al\r
+ asm mov [es:di+2+PAGE2START],al\r
+ asm mov [es:di+2+PAGE3START],al\r
+ asm mov al,[es:si+3]\r
+ asm mov [es:di+3+PAGE1START],al\r
+ asm mov [es:di+3+PAGE2START],al\r
+ asm mov [es:di+3+PAGE3START],al\r
+ asm mov al,[es:si+4]\r
+ asm mov [es:di+4+PAGE1START],al\r
+ asm mov [es:di+4+PAGE2START],al\r
+ asm mov [es:di+4+PAGE3START],al\r
+\r
+ asm add di,SCREENWIDTH\r
+ asm add si,5\r
+\r
+ asm loop newline3\r
+ }\r
+\r
+ EGAWRITEMODE(0);\r
+}\r
+#endif\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Check the object and make sure it is a monster. Used in making the sound\r
+// of a monster being shot.\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+boolean PlayMonsterSound(classtype objclass)\r
+{\r
+ switch (objclass)\r
+ {\r
+ case solidobj:\r
+ case realsolidobj:\r
+ return false;\r
+ default:\r
+ return true;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ SHOTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Pshot (objtype *ob);\r
+\r
+\r
+extern statetype s_pshot1;\r
+extern statetype s_pshot2;\r
+\r
+//extern statetype s_bigpshot1;\r
+//extern statetype s_bigpshot2;\r
+\r
+\r
+statetype s_pshot1 = {PSHOT1PIC,8,&T_Pshot,&s_pshot2};\r
+statetype s_pshot2 = {PSHOT2PIC,8,&T_Pshot,&s_pshot1};\r
+\r
+\r
+statetype s_pshot_exp1 = {PSHOT_EXP1PIC,7,NULL,&s_pshot_exp2};\r
+statetype s_pshot_exp2 = {PSHOT_EXP2PIC,7,NULL,&s_pshot_exp3};\r
+statetype s_pshot_exp3 = {PSHOT_EXP3PIC,7,NULL,NULL};\r
+\r
+\r
+//statetype s_shotexplode = {PSHOT2PIC,8,NULL,NULL};\r
+\r
+//statetype s_bigpshot1 = {BIGPSHOT1PIC,8,&T_Pshot,&s_bigpshot2};\r
+//statetype s_bigpshot2 = {BIGPSHOT2PIC,8,&T_Pshot,&s_bigpshot1};\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= SpawnPShot\r
+=\r
+===================\r
+*/\r
+\r
+void SpawnPShot (void)\r
+{\r
+ DSpawnNewObjFrac (player->x,player->y,&s_pshot1,PIXRADIUS*7);\r
+ new->obclass = pshotobj;\r
+ new->speed = SHOTSPEED;\r
+ new->angle = player->angle;\r
+ new->active = always;\r
+}\r
+\r
+#if 0\r
+void SpawnBigPShot (void)\r
+{\r
+ SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS);\r
+ new->obclass = bigpshotobj;\r
+ new->speed = SHOTSPEED;\r
+ new->angle = player->angle;\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= JimsShotClipMove\r
+=\r
+= Only checks corners, so the object better be less than one tile wide!\r
+=\r
+===================\r
+*/\r
+boolean JimsShotClipMove (objtype *ob, long xmove, long ymove)\r
+{\r
+ int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+ long intersect,basex,basey,pointx,pointy;\r
+ unsigned inside,total,tile;\r
+ objtype *check;\r
+ boolean moveok;\r
+\r
+//\r
+// move player and check to see if any corners are in solid tiles\r
+//\r
+// basex = ob->x;\r
+// basey = ob->y;\r
+\r
+// ob->x += xmove;\r
+// ob->y += ymove;\r
+\r
+// CalcBounds (ob);\r
+\r
+ xl = ob->xl>>TILESHIFT;\r
+ yl = ob->yl>>TILESHIFT;\r
+\r
+ xh = ob->xh>>TILESHIFT;\r
+ yh = ob->yh>>TILESHIFT;\r
+\r
+ for (y=yl;y<=yh;y++)\r
+ for (x=xl;x<=xh;x++)\r
+ {\r
+ check = actorat[x][y];\r
+\r
+ if ((!check) || (check == player) || (!(check->flags & of_shootable)))\r
+ continue;\r
+\r
+ ob->x -= xmove;\r
+ ob->y -= ymove;\r
+\r
+ if (check->obclass != solidobj && check->obclass != hbunnyobj)\r
+ {\r
+ if (PlayMonsterSound(check->obclass))\r
+ SD_PlaySound (SHOOTMONSTERSND);\r
+ if (ob->obclass == bigpshotobj)\r
+ ShootActor (check,BIGSHOTDAMAGE);\r
+ else\r
+ ShootActor (check,SHOTDAMAGE);\r
+ }\r
+ else\r
+ if (check->obclass == solidobj && (check->flags & of_forcefield))\r
+ {\r
+ if (PlayMonsterSound(check->obclass))\r
+ SD_PlaySound (SHOOTMONSTERSND);\r
+ if (ob->obclass == bigpshotobj)\r
+ ShootActor (check,BIGSHOTDAMAGE);\r
+ else\r
+ ShootActor (check,SHOTDAMAGE);\r
+ }\r
+ ob->state = &s_pshot_exp1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return(true);\r
+ }\r
+\r
+ return(false); // move is OK!\r
+\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Pshot\r
+=\r
+===============\r
+*/\r
+#if 0\r
+void T_Pshot (objtype *ob)\r
+{\r
+ objtype *check;\r
+ long xmove,ymove,speed;\r
+\r
+//\r
+// check current position for monsters having moved into it\r
+//\r
+ for (check = player->next; check; check=check->next)\r
+ if ((check->flags & of_shootable)\r
+ && ob->xl <= check->xh\r
+ && ob->xh >= check->xl\r
+ && ob->yl <= check->yh\r
+ && ob->yh >= check->yl)\r
+ {\r
+\r
+ if (check->obclass != solidobj)\r
+ {\r
+ if (PlayMonsterSound(check->obclass))\r
+ SD_PlaySound (SHOOTMONSTERSND);\r
+ if (ob->obclass == bigpshotobj)\r
+ ShootActor (check,BIGSHOTDAMAGE);\r
+ else\r
+ ShootActor (check,SHOTDAMAGE);\r
+ }\r
+\r
+ ob->state = &s_pshot_exp1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+\r
+\r
+//\r
+// move ahead, possibly hitting a wall\r
+//\r
+ speed = ob->speed*tics;\r
+\r
+ xmove = FixedByFrac(speed,costable[ob->angle]);\r
+ ymove = -FixedByFrac(speed,sintable[ob->angle]);\r
+\r
+ if (ShotClipMove(ob,xmove,ymove))\r
+ {\r
+ ob->state = &s_pshot_exp1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+\r
+ ob->tilex = ob->x >> TILESHIFT;\r
+ ob->tiley = ob->y >> TILESHIFT;\r
+\r
+//\r
+// check final position for monsters hit\r
+//\r
+ for (check = player->next; check; check=check->next)\r
+ if ((ob->flags & of_shootable)\r
+ && ob->xl <= check->xh\r
+ && ob->xh >= check->xl\r
+ && ob->yl <= check->yh\r
+ && ob->yh >= check->yl)\r
+ {\r
+ ShootActor (check,SHOTDAMAGE);\r
+ ob->state = &s_pshot_exp1;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+}\r
+#endif\r
+\r
+\r
+\r
+void T_Pshot (objtype *ob)\r
+{\r
+ objtype *check;\r
+ long xmove,ymove,speed;\r
+ int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+ long intersect,basex,basey,pointx,pointy;\r
+ unsigned inside,total,tile;\r
+ boolean moveok;\r
+\r
+//\r
+// check current position for monsters having moved into it\r
+//\r
+ for (check = player->next; check; check=check->next)\r
+ if ((check->flags & of_shootable)\r
+ && ob->xl <= check->xh\r
+ && ob->xh >= check->xl\r
+ && ob->yl <= check->yh\r
+ && ob->yh >= check->yl)\r
+ {\r
+\r
+ if (check->obclass != solidobj && check->obclass != hbunnyobj)\r
+ {\r
+ if (PlayMonsterSound(check->obclass))\r
+ SD_PlaySound (SHOOTMONSTERSND);\r
+ if (ob->obclass == bigpshotobj)\r
+ ShootActor (check,BIGSHOTDAMAGE);\r
+ else\r
+ ShootActor (check,SHOTDAMAGE);\r
+ }\r
+\r
+ ob->state = &s_pshot_exp1;\r
+ ob->obclass = expobj;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+\r
+\r
+//\r
+// move ahead, possibly hitting a wall\r
+//\r
+ speed = ob->speed*tics;\r
+\r
+ xmove = FixedByFrac(speed,costable[ob->angle]);\r
+ ymove = -FixedByFrac(speed,sintable[ob->angle]);\r
+\r
+ if (ShotClipMove(ob,xmove,ymove))\r
+ {\r
+ ob->state = &s_pshot_exp1;\r
+ ob->obclass = expobj;\r
+ ob->ticcount = ob->state->tictime;\r
+ return;\r
+ }\r
+\r
+ ob->tilex = ob->x >> TILESHIFT;\r
+ ob->tiley = ob->y >> TILESHIFT;\r
+\r
+//\r
+// check final position for monsters hit\r
+//\r
+\r
+ JimsShotClipMove(obj,xmove,ymove);\r
+\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ PLAYER ACTIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+===============\r
+=\r
+= BuildShotPower\r
+=\r
+===============\r
+*/\r
+\r
+void BuildShotPower (void)\r
+{\r
+ int newlines,topline;\r
+ long i;\r
+ unsigned source,dest;\r
+\r
+ if (gamestate.shotpower == MAXSHOTPOWER)\r
+ return;\r
+\r
+ newlines = 0;\r
+ for (i=lasttimecount-realtics;i<lasttimecount;i++)\r
+ newlines += (i&1);\r
+\r
+ gamestate.shotpower += newlines;\r
+\r
+ if (gamestate.shotpower > MAXSHOTPOWER)\r
+ {\r
+ newlines -= (gamestate.shotpower - MAXSHOTPOWER);\r
+ gamestate.shotpower = MAXSHOTPOWER;\r
+ }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= ClearShotPower\r
+=\r
+===============\r
+*/\r
+\r
+void ClearShotPower (void)\r
+{\r
+ unsigned source,dest,topline;\r
+\r
+#if 0\r
+ topline = MAXSHOTPOWER - gamestate.shotpower;\r
+\r
+ source = latchpics[L_NOSHOT]+topline*SIDEBARWIDTH;\r
+ dest = (POWERLINE+topline)*SCREENWIDTH+34;\r
+\r
+ asm mov es,[screenseg]\r
+ asm mov si,[source]\r
+ asm mov di,[dest]\r
+\r
+ if (!gamestate.shotpower)\r
+ return;\r
+\r
+ EGAWRITEMODE(1);\r
+\r
+ asm mov cx,[WORD PTR gamestate.shotpower]\r
+newline:\r
+ asm mov al,[es:si]\r
+ asm mov [es:di+PAGE1START],al\r
+ asm mov [es:di+PAGE2START],al\r
+ asm mov [es:di+PAGE3START],al\r
+ asm mov al,[es:si+1]\r
+ asm mov [es:di+1+PAGE1START],al\r
+ asm mov [es:di+1+PAGE2START],al\r
+ asm mov [es:di+1+PAGE3START],al\r
+ asm mov al,[es:si+2]\r
+ asm mov [es:di+2+PAGE1START],al\r
+ asm mov [es:di+2+PAGE2START],al\r
+ asm mov [es:di+2+PAGE3START],al\r
+ asm mov al,[es:si+3]\r
+ asm mov [es:di+3+PAGE1START],al\r
+ asm mov [es:di+3+PAGE2START],al\r
+ asm mov [es:di+3+PAGE3START],al\r
+ asm mov al,[es:si+4]\r
+ asm mov [es:di+4+PAGE1START],al\r
+ asm mov [es:di+4+PAGE2START],al\r
+ asm mov [es:di+4+PAGE3START],al\r
+\r
+ asm add di,SCREENWIDTH\r
+ asm add si,5\r
+\r
+ asm loop newline\r
+\r
+ EGAWRITEMODE(0);\r
+#endif\r
+\r
+ gamestate.shotpower = 0;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= Shoot\r
+=\r
+===============\r
+*/\r
+\r
+void Shoot (void)\r
+{\r
+ ClearShotPower ();\r
+ SD_PlaySound (SHOOTSND);\r
+ SpawnPShot ();\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= BigShoot\r
+=\r
+===============\r
+*/\r
+\r
+void BigShoot (void)\r
+{\r
+ ClearShotPower ();\r
+ SD_PlaySound (BIGSHOOTSND);\r
+ SpawnBigPShot ();\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= CastBolt\r
+=\r
+===============\r
+*/\r
+\r
+void CastBolt (void)\r
+{\r
+ if (!gamestate.bolts)\r
+ {\r
+ SD_PlaySound (NOITEMSND);\r
+ return;\r
+ }\r
+\r
+ TakeBolt ();\r
+ boltsleft = NUMBOLTS;\r
+ bolttimer = BOLTTICS;\r
+ Shoot ();\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= ContinueBolt\r
+=\r
+===============\r
+*/\r
+\r
+void ContinueBolt (void)\r
+{\r
+ bolttimer-=realtics;\r
+ if (bolttimer<0)\r
+ {\r
+ boltsleft--;\r
+ bolttimer = BOLTTICS;\r
+ Shoot ();\r
+ }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= CastNuke\r
+=\r
+===============\r
+*/\r
+\r
+void CastNuke (void)\r
+{\r
+ extern boolean autofire;\r
+\r
+ int angle;\r
+\r
+ if (!gamestate.nukes)\r
+ {\r
+ SD_PlaySound (NOITEMSND);\r
+ return;\r
+ }\r
+\r
+ if (!autofire)\r
+ TakeNuke ();\r
+ lastnuke = TimeCount;\r
+\r
+ for (angle = 0; angle < ANGLES; angle+= ANGLES/16)\r
+ {\r
+ DSpawnNewObjFrac (player->x,player->y,&s_pshot1,24*PIXRADIUS);\r
+ new->obclass = bigpshotobj;\r
+ new->speed = SHOTSPEED;\r
+ new->angle = angle;\r
+ new->active = always;\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrinkPotion\r
+=\r
+===============\r
+*/\r
+\r
+void DrinkPotion (void)\r
+{\r
+ unsigned source,dest,topline;\r
+\r
+ if (!gamestate.potions)\r
+ {\r
+ SD_PlaySound (NOITEMSND);\r
+ return;\r
+ }\r
+\r
+ DisplaySMsg("Curing", NULL);\r
+ TakePotion ();\r
+ gamestate.body = MAXBODY;\r
+ VW_WaitVBL(30);\r
+ status_flag = S_NONE;\r
+\r
+#if 0\r
+//\r
+// draw a full up bar\r
+//\r
+ source = latchpics[L_BODYBAR];\r
+ dest = BODYLINE*SCREENWIDTH+34;\r
+\r
+ asm mov es,[screenseg]\r
+ asm mov si,[source]\r
+ asm mov di,[dest]\r
+\r
+ EGAWRITEMODE(1);\r
+\r
+ asm mov cx,MAXBODY\r
+newline:\r
+ asm mov al,[es:si]\r
+ asm mov [es:di+PAGE1START],al\r
+ asm mov [es:di+PAGE2START],al\r
+ asm mov [es:di+PAGE3START],al\r
+ asm mov al,[es:si+1]\r
+ asm mov [es:di+1+PAGE1START],al\r
+ asm mov [es:di+1+PAGE2START],al\r
+ asm mov [es:di+1+PAGE3START],al\r
+ asm mov al,[es:si+2]\r
+ asm mov [es:di+2+PAGE1START],al\r
+ asm mov [es:di+2+PAGE2START],al\r
+ asm mov [es:di+2+PAGE3START],al\r
+ asm mov al,[es:si+3]\r
+ asm mov [es:di+3+PAGE1START],al\r
+ asm mov [es:di+3+PAGE2START],al\r
+ asm mov [es:di+3+PAGE3START],al\r
+ asm mov al,[es:si+4]\r
+ asm mov [es:di+4+PAGE1START],al\r
+ asm mov [es:di+4+PAGE2START],al\r
+ asm mov [es:di+4+PAGE3START],al\r
+ asm add di,SCREENWIDTH\r
+ asm add si,5\r
+\r
+ asm loop newline\r
+\r
+ EGAWRITEMODE(0);\r
+#endif\r
+}\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// GetScrollText\r
+//\r
+// parms - scroll -- the number of the scroll to display\r
+// returns - a far pointer to the scroll text\r
+//\r
+////////////////////////////////////////////////////////////////////////////\r
+\r
+char far *GetScrollText (int scroll)\r
+{\r
+ boolean found;\r
+ int i;\r
+ char far *txt;\r
+ unsigned ofset;\r
+\r
+ CA_CacheGrChunk(SCROLLTEXT);\r
+\r
+ found = false;\r
+ i = 0;\r
+\r
+ txt = (char _seg *)grsegs[SCROLLTEXT];\r
+\r
+ while (!found)\r
+ {\r
+ while (*txt != '\n')\r
+ {\r
+ if (*txt == '\r')\r
+ *txt = 0;\r
+ txt++;\r
+ }\r
+ txt++;\r
+ if (i == scroll)\r
+ {\r
+ found = true;\r
+ ofset = FP_OFF(txt);\r
+\r
+ while (*txt != '\n')\r
+ {\r
+ if (*txt == '\r')\r
+ *txt = 0;\r
+ txt++;\r
+ }\r
+ }\r
+ i++;\r
+ }\r
+ txt = (char _seg *)grsegs[SCROLLTEXT]+ofset;\r
+\r
+ UNMARKGRCHUNK(SCROLLTEXT);\r
+ return(txt);\r
+} //End of GetScrollText\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= ReadScroll\r
+=\r
+===============\r
+*/\r
+\r
+extern boolean tileneeded[NUMFLOORS];\r
+\r
+void ReadScroll (int scroll)\r
+{\r
+ PresenterInfo pi;\r
+ int i;\r
+ unsigned *skytemp,*gndtemp,blackcolor=0;\r
+ char far *scrolltext;\r
+\r
+ DisplaySMsg("Reading Scroll", NULL);\r
+ bufferofs = displayofs = screenloc[screenpage];\r
+\r
+ if (status_flag != S_TIMESTOP)\r
+ status_flag = S_NONE;\r
+\r
+ FreeUpMemory();\r
+\r
+ CA_CacheGrChunk (SCROLLTOPPIC);\r
+ CA_CacheGrChunk (SCROLL1PIC);\r
+ CA_CacheGrChunk (SCROLLBOTTOMPIC);\r
+\r
+ skytemp = skycolor;\r
+ gndtemp = groundcolor;\r
+ skycolor = groundcolor = &blackcolor;\r
+\r
+ VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0);\r
+ VW_DrawPic (10,0,SCROLLTOPPIC);\r
+ VW_DrawPic (10,32,SCROLL1PIC);\r
+ VW_DrawPic (10,88,SCROLLBOTTOMPIC);\r
+\r
+ scrolltext = GetScrollText(scroll);\r
+\r
+ pi.xl = LEFTEDGE;\r
+ pi.yl = PRNY;\r
+ pi.xh = RIGHTEDGE;\r
+ pi.yh = PRNY+1;\r
+ pi.bgcolor = 7;\r
+ pi.script[0] = (char far *)scrolltext;\r
+ Presenter(&pi);\r
+\r
+ skycolor = skytemp;\r
+ groundcolor = gndtemp;\r
+\r
+ UNMARKGRCHUNK(SCROLL1PIC);\r
+ UNMARKGRCHUNK(SCROLLTOPPIC);\r
+ UNMARKGRCHUNK(SCROLLBOTTOMPIC);\r
+ MM_FreePtr (&grsegs[SCROLL1PIC]);\r
+ MM_FreePtr (&grsegs[SCROLLTOPPIC]);\r
+ MM_FreePtr (&grsegs[SCROLLBOTTOMPIC]);\r
+\r
+ CacheScaleds();\r
+\r
+ IN_ClearKeysDown ();\r
+ lasttext = -1;\r
+ DisplayMsg("Press ENTER or ESC to exit.",NULL);\r
+ while ((!Keyboard[sc_Escape]) && (!Keyboard[sc_Enter]));\r
+ IN_ClearKeysDown ();\r
+\r
+ if (status_flag == S_TIMESTOP)\r
+ DisplaySMsg("Time Stopped: ",NULL);\r
+}\r
+\r
+#endif\r
+\r
+\r
+//===============\r
+//\r
+// StopTime()\r
+//\r
+//\r
+//===============\r
+void StopTime()\r
+{\r
+ FreezeTime = MAXFREEZETIME;\r
+ SD_PlaySound(FREEZETIMESND);\r
+ DisplaySMsg("Time Stopped: ",NULL);\r
+ status_flag = S_TIMESTOP;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeDamage\r
+=\r
+===============\r
+*/\r
+\r
+void TakeDamage (int points)\r
+{\r
+ unsigned source,dest,topline;\r
+\r
+ if (!gamestate.body || (bordertime && bcolor==FLASHCOLOR) || godmode)\r
+ return;\r
+\r
+ if (points != 1)\r
+ points = EasyDoDamage(points);\r
+\r
+ if (points >= gamestate.body)\r
+ {\r
+ points = gamestate.body;\r
+ Flags |= FL_DEAD;\r
+ }\r
+\r
+ bordertime = FLASHTICS<<2;\r
+ bcolor = FLASHCOLOR;\r
+ VW_ColorBorder (FLASHCOLOR);\r
+\r
+ DisplaySMsg("Damaging blows!", NULL);\r
+ status_flag = S_NONE;\r
+ status_delay = 80;\r
+\r
+ if (gamestate.body<MAXBODY/3)\r
+ SD_PlaySound (TAKEDMGHURTSND);\r
+ else\r
+ SD_PlaySound (TAKEDAMAGESND);\r
+\r
+ gamestate.body -= points;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ INTERACTION\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+#if 0\r
+/*\r
+==================\r
+=\r
+= OpenDoor\r
+=\r
+==================\r
+*/\r
+\r
+void OpenDoor (unsigned bx, unsigned by, unsigned doorbase)\r
+{\r
+ int x,y;\r
+ unsigned far *map;\r
+\r
+ x=bx;\r
+ y=by;\r
+ map = mapsegs[0]+farmapylookup[y]+x;\r
+ while (tilemap[x][y]-doorbase<4)\r
+ {\r
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+ map--;\r
+ x--;\r
+ }\r
+ x=bx+1;\r
+ map = mapsegs[0]+farmapylookup[y]+x;\r
+ while (tilemap[x][y]-doorbase<4)\r
+ {\r
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+ map++;\r
+ x++;\r
+ }\r
+ x=bx;\r
+ y=by-1;\r
+ map = mapsegs[0]+farmapylookup[y]+x;\r
+ while (tilemap[x][y]-doorbase<4)\r
+ {\r
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+ map-=mapwidth;\r
+ y--;\r
+ }\r
+ y=by+1;\r
+ map = mapsegs[0]+farmapylookup[y]+x;\r
+ while (tilemap[x][y]-doorbase<4)\r
+ {\r
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+ map+=mapwidth;\r
+ y++;\r
+ }\r
+}\r
+#endif\r
+\r
+#if 0\r
+/*\r
+==================\r
+=\r
+= RemoveWalls - similar to OpenDoor(), but on a different plane\r
+=\r
+==================\r
+*/\r
+void RemoveWalls (unsigned bx, unsigned by, unsigned remove_code)\r
+{\r
+ int x,y;\r
+ unsigned far *map,*p2;\r
+\r
+ x=bx;\r
+ y=by;\r
+ p2 = *(mapsegs[2]+farmapylookup[y]+x);\r
+ map = mapsegs[0]+farmapylookup[y]+x;\r
+ while (*p2 == remove_code)\r
+ {\r
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+ map--;\r
+ p2--;\r
+ x--;\r
+ }\r
+ x=bx+1;\r
+ p2 = *(mapsegs[2]+farmapylookup[y]+x);\r
+ map = mapsegs[0]+farmapylookup[y]+x;\r
+ while (*p2 == remove_code)\r
+ {\r
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+ map++;\r
+ p2++;\r
+ x++;\r
+ }\r
+ x=bx;\r
+ y=by-1;\r
+ p2 = *(mapsegs[2]+farmapylookup[y]+x);\r
+ map = mapsegs[0]+farmapylookup[y]+x;\r
+ while (*p2 == remove_code)\r
+ {\r
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+ map-=mapwidth;\r
+ p2 -= mapwidth;\r
+ y--;\r
+ }\r
+ y=by+1;\r
+ p2 = *(mapsegs[2]+farmapylookup[y]+x);\r
+ map = mapsegs[0]+farmapylookup[y]+x;\r
+ while (*p2 == remove_code)\r
+ {\r
+ tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+ map+=mapwidth;\r
+ p2 += mapwidth;\r
+ y++;\r
+ }\r
+}\r
+#endif\r
+\r
+/*\r
+==================\r
+=\r
+= HitSpecialTile\r
+=\r
+= Returns true if the move is blocked\r
+=\r
+==================\r
+*/\r
+\r
+boolean HitSpecialTile (unsigned x, unsigned y, unsigned tile)\r
+{\r
+ objtype *check;\r
+ short keyspot;\r
+ unsigned temp,spot,curmap=gamestate.mapon,newlevel;\r
+ char *key_colors[] = {"a RED key",\r
+ "a YELLOW key",\r
+ "a GREEN key",\r
+ "a BLUE key"};\r
+\r
+ switch (tile)\r
+ {\r
+ case 65:\r
+ playstate = ex_victorious;\r
+ break;\r
+\r
+ case 9:\r
+ case 15:\r
+ case 27:\r
+ case 30:\r
+ case 40:\r
+ case 42:\r
+ case 43:\r
+ case 45:\r
+ case 46:\r
+ case 47:\r
+ case 49:\r
+ case 76:\r
+ case 77:\r
+\r
+ if (!playstate && !FreezeTime)\r
+ {\r
+\r
+ // Is this an openable door? (Is "openable" a word?)\r
+ //\r
+ spot = (*(mapsegs[2]+farmapylookup[y]+x)) >> 8;\r
+ if (spot == CANT_OPEN_CODE) // CAN'T EVER OPEN (it's just for looks)\r
+ {\r
+ CenterWindow(30,4);\r
+ US_CPrint("\nThis door is permanently blocked");\r
+ VW_UpdateScreen();\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+ return;\r
+ }\r
+\r
+ // make sure player has key to get into door\r
+ //\r
+\r
+ if (TILE_FLAGS(tile) & tf_EMBEDDED_KEY_COLOR)\r
+ keyspot = GATE_KEY_COLOR(tile);\r
+ else\r
+ keyspot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8;\r
+\r
+ if (keyspot--)\r
+ if (!gamestate.keys[keyspot])\r
+ {\r
+ SD_PlaySound(HIT_GATESND);\r
+ CenterWindow(20,5);\r
+ US_CPrint("\nYou need\n");\r
+ US_CPrint(key_colors[keyspot]);\r
+ VW_UpdateScreen();\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+ return;\r
+ }\r
+\r
+ //\r
+ // deal with this gate (warp? simply open? whatever...)\r
+ //\r
+ switch (spot)\r
+ {\r
+ case NEXT_LEVEL_CODE: // WARP TO NEXT LEVEL\r
+ newlevel = gamestate.mapon+1;\r
+ playstate = ex_warped;\r
+ break;\r
+\r
+ case REMOVE_DOOR_CODE: // REMOVE DOOR\r
+ (unsigned)actorat[x][y] = tilemap[x][y] = *(mapsegs[0]+farmapylookup[y]+x) = 0;\r
+ *(mapsegs[2]+farmapylookup[y+1]+x) = 0; // key no longer needed\r
+ if (keyspot>=0)\r
+ TakeKey(keyspot);\r
+ break;\r
+\r
+ default: // WARP TO A LEVEL\r
+ newlevel = spot;\r
+ playstate = ex_warped;\r
+ break;\r
+ }\r
+\r
+ if (playstate == ex_warped)\r
+ {\r
+ SD_PlaySound(HIT_GATESND);\r
+// levelinfo *li=&gamestate.levels[curmap];\r
+\r
+// OldAngle = FaceDoor(x,y);\r
+\r
+ if (!VerifyGateExit())\r
+ {\r
+ IN_ClearKeysDown ();\r
+ playstate = ex_stillplaying;\r
+ break;\r
+ }\r
+\r
+// FaceAngle(OldAngle);\r
+\r
+ if (keyspot>=0)\r
+ TakeKey(keyspot);\r
+ *(mapsegs[2]+farmapylookup[y+1]+x) = 0; // key no longer needed\r
+\r
+ gamestate.mapon = newlevel;\r
+ SD_PlaySound(WARPUPSND);\r
+ IN_ClearKeysDown ();\r
+\r
+// li->x = player->tilex;\r
+// li->y = player->tiley;\r
+// li->angle = player->angle+180;\r
+// if (li->angle > 360)\r
+// li->angle -= 360;\r
+ }\r
+ }\r
+ break;\r
+ }\r
+\r
+ return true;\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// VerifyGateExit()\r
+//-------------------------------------------------------------------------\r
+boolean VerifyGateExit()\r
+{\r
+ char choices[] = {sc_Escape,sc_Y,sc_N,0},ch;\r
+\r
+ ch=DisplayMsg("Pass this way? Y/N",choices);\r
+ DrawText(true);\r
+\r
+ return(ch == sc_Y);\r
+}\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= TouchActor\r
+=\r
+= Returns true if the move is blocked\r
+=\r
+==================\r
+*/\r
+\r
+boolean TouchActor (objtype *ob, objtype *check)\r
+{\r
+ if (ob->xh < check->xl || ob->xl > check->xh ||\r
+ ob->yh < check->yl || ob->yl > check->yh)\r
+ return false; // not quite touching\r
+\r
+ switch (check->obclass)\r
+ {\r
+ case bonusobj:\r
+ switch (check->temp1)\r
+ {\r
+ case B_BOLT: GiveBolt (); break;\r
+\r
+ case B_NUKE: GiveNuke (); break;\r
+\r
+ case B_POTION: GivePotion (); break;\r
+\r
+// case B_RKEY2: GiveKey(B_RKEY-B_RKEY); break;\r
+\r
+ case B_RKEY:\r
+ case B_YKEY:\r
+ case B_GKEY:\r
+ case B_BKEY: GiveKey (check->temp1-B_RKEY); break;\r
+\r
+#if 0\r
+ case B_SCROLL1:\r
+ case B_SCROLL2:\r
+ case B_SCROLL3:\r
+ case B_SCROLL4:\r
+ case B_SCROLL5:\r
+ case B_SCROLL6:\r
+ case B_SCROLL7:\r
+ case B_SCROLL8: GiveScroll (check->temp1-B_SCROLL1,true); break;\r
+#endif\r
+\r
+ case B_CHEST: GiveChest (); break;\r
+\r
+ case B_RGEM:\r
+ case B_YGEM:\r
+ case B_GGEM:\r
+ case B_BGEM:\r
+ case B_PGEM:\r
+ SD_PlaySound(GETGEMSND);\r
+ gamestate.gems[check->temp1-B_RGEM] = GEM_DELAY_TIME;\r
+ redraw_gems = true;\r
+ break;\r
+\r
+ default:\r
+ Quit("TouchActor(): INVALID BONUS");\r
+ break;\r
+ }\r
+\r
+ (unsigned)actorat[check->tilex][check->tiley] = 0;\r
+ RemoveObj (check);\r
+\r
+ return false;\r
+\r
+ case freezeobj:\r
+ StopTime();\r
+ (unsigned)actorat[check->tilex][check->tiley] = 0;\r
+ RemoveObj(check);\r
+ return(false);\r
+\r
+ case cloudobj:\r
+ TakeDamage(2);\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+}\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= CalcBounds\r
+=\r
+==================\r
+*/\r
+\r
+void CalcBounds (objtype *ob)\r
+{\r
+//\r
+// calculate hit rect\r
+//\r
+ ob->xl = ob->x - ob->size;\r
+ ob->xh = ob->x + ob->size;\r
+ ob->yl = ob->y - ob->size;\r
+ ob->yh = ob->y + ob->size;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= LocationInActor\r
+=\r
+===================\r
+*/\r
+\r
+boolean LocationInActor (objtype *ob)\r
+{\r
+ int x,y,xmin,ymin,xmax,ymax;\r
+ objtype *check;\r
+\r
+ CalcBounds (ob);\r
+\r
+ xmin = (ob->x >> TILESHIFT)-2;\r
+ ymin = (ob->y >> TILESHIFT)-2;\r
+ xmax = xmin+5;\r
+ ymax = ymin+5;\r
+\r
+ for (x=xmin;x<xmax;x++)\r
+ for (y=ymin;y<ymax;y++)\r
+ {\r
+ check = actorat[x][y];\r
+ if (check>(objtype *)LASTTILE\r
+ && (check->flags & of_shootable)\r
+ && (check->obclass != bonusobj)\r
+ && (check->obclass != freezeobj)\r
+ && (check->obclass != solidobj)\r
+ && ob->xl-SIZE_TEST <= check->xh\r
+ && ob->xh+SIZE_TEST >= check->xl\r
+ && ob->yl-SIZE_TEST <= check->yh\r
+ && ob->yh+SIZE_TEST >= check->yl)\r
+ return true;\r
+ }\r
+\r
+ return false;\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= ClipXMove\r
+=\r
+= Only checks corners, so the object better be less than one tile wide!\r
+=\r
+===================\r
+*/\r
+void ClipXMove (objtype *ob, long xmove)\r
+{\r
+ int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+ long intersect,basex,basey,pointx,pointy;\r
+ unsigned inside,total,tile;\r
+ objtype *check;\r
+ boolean moveok;\r
+ boolean invisible_present = false;\r
+\r
+//\r
+// move player and check to see if any corners are in solid tiles\r
+//\r
+ basex = ob->x;\r
+ basey = ob->y;\r
+\r
+ ob->x += xmove;\r
+\r
+ CalcBounds (ob);\r
+\r
+ xl = ob->xl>>TILESHIFT;\r
+ yl = ob->yl>>TILESHIFT;\r
+\r
+ xh = ob->xh>>TILESHIFT;\r
+ yh = ob->yh>>TILESHIFT;\r
+\r
+ for (y=yl;y<=yh;y++)\r
+ for (x=xl;x<=xh;x++)\r
+ {\r
+ check = actorat[x][y];\r
+\r
+ if (!check)\r
+ continue; // blank floor, walk ok\r
+\r
+ if ((unsigned)check <= LASTTILE)\r
+ {\r
+ if (TILE_FLAGS((unsigned)check) & tf_SPECIAL)\r
+ {\r
+ HitSpecialTile(x,y,(unsigned)check-SPECTILESTART);\r
+ goto blockmove;\r
+ }\r
+\r
+ if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL)\r
+ {\r
+ invisible_present = true;\r
+ goto blockmove;\r
+ }\r
+\r
+\r
+ if (TILE_FLAGS((unsigned)check) & tf_SOLID)\r
+ {\r
+ goto blockmove; // solid wall\r
+ }\r
+ }\r
+\r
+ TouchActor(ob,check); // pick up items\r
+ }\r
+\r
+//\r
+// check nearby actors\r
+//\r
+ if (LocationInActor(ob))\r
+ {\r
+ ob->x -= xmove;\r
+ if (LocationInActor(ob))\r
+ {\r
+ ob->x += xmove;\r
+ if (LocationInActor(ob))\r
+ ob->x -= xmove;\r
+ }\r
+ }\r
+ return; // move is OK!\r
+\r
+\r
+blockmove:\r
+\r
+// if (!SD_SoundPlaying())\r
+// SD_PlaySound (HITWALLSND);\r
+\r
+ moveok = false;\r
+\r
+ do\r
+ {\r
+ xmove /= 2;\r
+ if (moveok)\r
+ {\r
+ ob->x += xmove;\r
+ }\r
+ else\r
+ {\r
+ ob->x -= xmove;\r
+ }\r
+ CalcBounds (ob);\r
+ xl = ob->xl>>TILESHIFT;\r
+ yl = ob->yl>>TILESHIFT;\r
+ xh = ob->xh>>TILESHIFT;\r
+ yh = ob->yh>>TILESHIFT;\r
+ if (tilemap[xl][yl] || tilemap[xh][yl]\r
+ || tilemap[xh][yh] || tilemap[xl][yh] )\r
+ {\r
+ moveok = false;\r
+ if (xmove>=-2048 && xmove <=2048)\r
+ {\r
+ ob->x = basex;\r
+ ob->y = basey;\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ if (invisible_present)\r
+ {\r
+ moveok = false;\r
+ if (xmove>=-2048 && xmove <=2048)\r
+ {\r
+ ob->x = basex;\r
+ ob->y = basey;\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ if (xmove>=-2048 && xmove <=2048)\r
+ return;\r
+ moveok = true;\r
+ } while (1);\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ClipYMove\r
+=\r
+= Only checks corners, so the object better be less than one tile wide!\r
+=\r
+===================\r
+*/\r
+void ClipYMove (objtype *ob, long ymove)\r
+{\r
+ int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+ long intersect,basex,basey,pointx,pointy;\r
+ unsigned inside,total,tile;\r
+ objtype *check;\r
+ boolean moveok;\r
+ boolean invisible_present = false;\r
+\r
+//\r
+// move player and check to see if any corners are in solid tiles\r
+//\r
+ basex = ob->x;\r
+ basey = ob->y;\r
+\r
+ ob->y += ymove;\r
+\r
+ CalcBounds (ob);\r
+\r
+ xl = ob->xl>>TILESHIFT;\r
+ yl = ob->yl>>TILESHIFT;\r
+\r
+ xh = ob->xh>>TILESHIFT;\r
+ yh = ob->yh>>TILESHIFT;\r
+\r
+ for (y=yl;y<=yh;y++)\r
+ for (x=xl;x<=xh;x++)\r
+ {\r
+ check = actorat[x][y];\r
+ if (!check)\r
+ continue; // blank floor, walk ok\r
+\r
+ if ((unsigned)check <= LASTTILE)\r
+ {\r
+ if (TILE_FLAGS((unsigned)check) & tf_SPECIAL) // <=LASTSPECIALTILE)\r
+ {\r
+ HitSpecialTile (x,y,(unsigned)check-SPECTILESTART);\r
+ goto blockmove;\r
+ }\r
+\r
+ if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL)\r
+ {\r
+ invisible_present = true;\r
+ goto blockmove;\r
+ }\r
+\r
+\r
+ if (TILE_FLAGS((unsigned)check) & tf_SOLID) // LASTWALLTILE)\r
+ {\r
+ goto blockmove; // solid wall\r
+ }\r
+ }\r
+\r
+ TouchActor(ob,check); // pick up items\r
+ }\r
+\r
+//\r
+// check nearby actors\r
+//\r
+ if (LocationInActor(ob))\r
+ {\r
+ if (LocationInActor(ob))\r
+ {\r
+ ob->y -= ymove;\r
+ }\r
+ }\r
+ return; // move is OK!\r
+\r
+\r
+blockmove:\r
+\r
+// if (!SD_SoundPlaying())\r
+// SD_PlaySound (HITWALLSND);\r
+\r
+ moveok = false;\r
+\r
+ do\r
+ {\r
+ ymove /= 2;\r
+ if (moveok)\r
+ {\r
+ ob->y += ymove;\r
+ }\r
+ else\r
+ {\r
+ ob->y -= ymove;\r
+ }\r
+ CalcBounds (ob);\r
+ xl = ob->xl>>TILESHIFT;\r
+ yl = ob->yl>>TILESHIFT;\r
+ xh = ob->xh>>TILESHIFT;\r
+ yh = ob->yh>>TILESHIFT;\r
+ if (tilemap[xl][yl] || tilemap[xh][yl]\r
+ || tilemap[xh][yh] || tilemap[xl][yh] )\r
+ {\r
+ moveok = false;\r
+ if (ymove>=-2048 && ymove <=2048)\r
+ {\r
+ ob->x = basex;\r
+ ob->y = basey;\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ if (invisible_present)\r
+ {\r
+ moveok = false;\r
+ if (ymove>=-2048 && ymove <=2048)\r
+ {\r
+ ob->x = basex;\r
+ ob->y = basey;\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ if (ymove>=-2048 && ymove <=2048)\r
+ return;\r
+ moveok = true;\r
+ } while (1);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ShotClipMove\r
+=\r
+= Only checks corners, so the object better be less than one tile wide!\r
+=\r
+===================\r
+*/\r
+\r
+boolean ShotClipMove (objtype *ob, long xmove, long ymove)\r
+{\r
+ int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+ long intersect,basex,basey,pointx,pointy;\r
+ unsigned inside,total,spot,tile;\r
+ objtype *check;\r
+ boolean moveok;\r
+\r
+//\r
+// move shot and check to see if any corners are in solid tiles\r
+//\r
+ basex = ob->x;\r
+ basey = ob->y;\r
+\r
+ ob->x += xmove;\r
+ ob->y += ymove;\r
+\r
+ CalcBounds (ob);\r
+\r
+ xl = ob->xl>>TILESHIFT;\r
+ yl = ob->yl>>TILESHIFT;\r
+\r
+ xh = ob->xh>>TILESHIFT;\r
+ yh = ob->yh>>TILESHIFT;\r
+\r
+ for (y=yl;y<=yh;y++)\r
+ for (x=xl;x<=xh;x++)\r
+ {\r
+ spot = (*(mapsegs[2]+farmapylookup[y]+x)) >> 8;\r
+ if (spot == EXP_WALL_CODE)\r
+ switch (ob->obclass)\r
+ {\r
+ case pshotobj:\r
+ case bigpshotobj:\r
+ ExplodeWall (x,y);\r
+ goto blockmove;\r
+ break;\r
+ }\r
+\r
+ tile = *(mapsegs[0]+farmapylookup[y]+x);\r
+ if (TILE_FLAGS(tile) & tf_SOLID)\r
+ goto blockmove;\r
+ }\r
+ return false; // move is OK!\r
+\r
+\r
+blockmove:\r
+\r
+ SD_PlaySound (SHOOTWALLSND);\r
+\r
+ moveok = false;\r
+\r
+ do\r
+ {\r
+ xmove /= 2;\r
+ ymove /= 2;\r
+ if (moveok)\r
+ {\r
+ ob->x += xmove;\r
+ ob->y += ymove;\r
+ }\r
+ else\r
+ {\r
+ ob->x -= xmove;\r
+ ob->y -= ymove;\r
+ }\r
+ CalcBounds (ob);\r
+ xl = ob->xl>>TILESHIFT;\r
+ yl = ob->yl>>TILESHIFT;\r
+ xh = ob->xh>>TILESHIFT;\r
+ yh = ob->yh>>TILESHIFT;\r
+ if (tilemap[xl][yl] || tilemap[xh][yl]\r
+ || tilemap[xh][yh] || tilemap[xl][yh] )\r
+ {\r
+ moveok = false;\r
+ if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)\r
+ {\r
+ ob->x = basex;\r
+ ob->y = basey;\r
+ return true;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)\r
+ return true;\r
+ moveok = true;\r
+ }\r
+ } while (1);\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ PLAYER CONTROL\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+void T_Player (objtype *ob);\r
+\r
+statetype s_player = {0,0,&T_Player,&s_player};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnPlayer\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnPlayer (int tilex, int tiley, int dir)\r
+{\r
+#if 0\r
+ levelinfo *li=&gamestate.levels[gamestate.mapon];\r
+\r
+ if (li->x != -1)\r
+ {\r
+ tilex = li->x;\r
+ tiley = li->y;\r
+ player->angle = li->angle;\r
+ }\r
+ else\r
+ player->angle = (1-dir)*90;\r
+#endif\r
+\r
+ player->obclass = playerobj;\r
+ player->active = always;\r
+ player->tilex = tilex;\r
+ player->tiley = tiley;\r
+ player->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+ player->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+ player->state = &s_player;\r
+ player->size = MINDIST;\r
+ CalcBounds(player);\r
+ player->angle = (1-dir)*90;\r
+ if (player->angle<0)\r
+ player->angle += ANGLES;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= Thrust\r
+=\r
+===================\r
+*/\r
+\r
+void Thrust (int angle, unsigned speed)\r
+{\r
+ long xmove,ymove;\r
+\r
+ if (lasttimecount>>5 != ((lasttimecount-tics)>>5) )\r
+ {\r
+ //\r
+ // walk sound\r
+ //\r
+ if (lasttimecount&32)\r
+ SD_PlaySound (WALK1SND);\r
+ else\r
+ SD_PlaySound (WALK2SND);\r
+ }\r
+\r
+ xmove = FixedByFrac(speed,costable[angle]);\r
+ ymove = -FixedByFrac(speed,sintable[angle]);\r
+\r
+ ClipXMove(player,xmove);\r
+ ClipYMove(player,ymove);\r
+ player->tilex = player->x >> TILESHIFT;\r
+ player->tiley = player->y >> TILESHIFT;\r
+}\r
+\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= ControlMovement\r
+=\r
+=======================\r
+*/\r
+\r
+void ControlMovement (objtype *ob)\r
+{\r
+ int angle;\r
+ long speed;\r
+\r
+\r
+ if (control.button1)\r
+ {\r
+ //\r
+ // strafing\r
+ //\r
+ //\r
+ // side to side move\r
+ //\r
+ if (!mousexmove)\r
+ speed = 0;\r
+ else if (mousexmove<0)\r
+ speed = -(long)mousexmove*300;\r
+ else\r
+ speed = -(long)mousexmove*300;\r
+\r
+ if (control.xaxis == -1)\r
+ {\r
+ speed += PLAYERSPEED*tics;\r
+ }\r
+ else if (control.xaxis == 1)\r
+ {\r
+ speed -= PLAYERSPEED*tics;\r
+ }\r
+\r
+ if (speed > 0)\r
+ {\r
+ if (speed >= TILEGLOBAL)\r
+ speed = TILEGLOBAL-1;\r
+ angle = ob->angle + ANGLES/4;\r
+ if (angle >= ANGLES)\r
+ angle -= ANGLES;\r
+ Thrust (angle,speed); // move to left\r
+ }\r
+ else if (speed < 0)\r
+ {\r
+ if (speed <= -TILEGLOBAL)\r
+ speed = -TILEGLOBAL+1;\r
+ angle = ob->angle - ANGLES/4;\r
+ if (angle < 0)\r
+ angle += ANGLES;\r
+ Thrust (angle,-speed); // move to right\r
+ }\r
+ }\r
+ else\r
+ {\r
+ //\r
+ // not strafing\r
+ //\r
+\r
+ //\r
+ // turning\r
+ //\r
+ if (control.xaxis == 1)\r
+ {\r
+ ob->angle -= tics;\r
+ if (running) // fast turn\r
+ ob->angle -= (tics<<1);\r
+ }\r
+ else if (control.xaxis == -1)\r
+ {\r
+ ob->angle+= tics;\r
+ if (running) // fast turn\r
+ ob->angle += (tics<<1);\r
+ }\r
+\r
+ ob->angle -= (mousexmove/10);\r
+\r
+ if (ob->angle >= ANGLES)\r
+ ob->angle -= ANGLES;\r
+ if (ob->angle < 0)\r
+ ob->angle += ANGLES;\r
+\r
+ }\r
+\r
+ //\r
+ // forward/backwards move\r
+ //\r
+ if (!mouseymove)\r
+ speed = 0;\r
+ else if (mouseymove<0)\r
+ speed = -(long)mouseymove*500;\r
+ else\r
+ speed = -(long)mouseymove*200;\r
+\r
+ if (control.yaxis == -1)\r
+ {\r
+ speed += PLAYERSPEED*tics;\r
+ }\r
+ else if (control.yaxis == 1)\r
+ {\r
+ speed -= PLAYERSPEED*tics;\r
+ }\r
+\r
+ if (speed > 0)\r
+ {\r
+ if (speed >= TILEGLOBAL)\r
+ speed = TILEGLOBAL-1;\r
+ Thrust (ob->angle,speed); // move forwards\r
+ }\r
+ else if (speed < 0)\r
+ {\r
+ if (speed <= -TILEGLOBAL)\r
+ speed = -TILEGLOBAL+1;\r
+ angle = ob->angle + ANGLES/2;\r
+ if (angle >= ANGLES)\r
+ angle -= ANGLES;\r
+ Thrust (angle,-speed); // move backwards\r
+ }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Player\r
+=\r
+===============\r
+*/\r
+\r
+void T_Player (objtype *ob)\r
+{\r
+ extern boolean autofire;\r
+\r
+ int angle,speed,scroll,loop;\r
+ unsigned text,tilex,tiley;\r
+ long lspeed;\r
+\r
+// boolean radar_moved=false;\r
+\r
+\r
+ ControlMovement (ob);\r
+\r
+\r
+ //\r
+ // firing\r
+ //\r
+ if (boltsleft)\r
+ {\r
+ handheight+=(realtics<<2);\r
+ if (handheight>MAXHANDHEIGHT)\r
+ handheight = MAXHANDHEIGHT;\r
+\r
+ ContinueBolt ();\r
+ lasthand = lasttimecount;\r
+ }\r
+ else\r
+ {\r
+ if (control.button0)\r
+ {\r
+ handheight+=(realtics<<2);\r
+ if (handheight>MAXHANDHEIGHT)\r
+ handheight = MAXHANDHEIGHT;\r
+ lasthand = lasttimecount;\r
+\r
+ if (!button0down)\r
+ Shoot();\r
+\r
+ if (!autofire)\r
+ button0down=true;\r
+ }\r
+ else\r
+ {\r
+ if (lasttimecount > lasthand+HANDPAUSE)\r
+ {\r
+ handheight-=(realtics<<1);\r
+ if (handheight<0)\r
+ handheight = 0;\r
+ }\r
+\r
+ button0down = false;\r
+ }\r
+}\r
+\r
+#if 0\r
+ if (control.button0)\r
+ {\r
+ handheight+=(realtics<<2);\r
+ if (handheight>MAXHANDHEIGHT)\r
+ handheight = MAXHANDHEIGHT;\r
+\r
+ if ((unsigned)TimeCount/FIRETIME != lastfiretime)\r
+ BuildShotPower ();\r
+ lasthand = lasttimecount;\r
+ }\r
+ else\r
+ {\r
+ if (lasttimecount > lasthand+HANDPAUSE)\r
+ {\r
+ handheight-=(realtics<<1);\r
+ if (handheight<0)\r
+ handheight = 0;\r
+ }\r
+\r
+ if (gamestate.shotpower)\r
+ {\r
+ lastfiretime = (unsigned)TimeCount/FIRETIME;\r
+ Shoot ();\r
+ }\r
+ }\r
+ }\r
+#endif\r
+\r
+ //\r
+ // special actions\r
+ //\r
+\r
+ if ((Keyboard[sc_Space] || Keyboard[sc_C]) && gamestate.body != MAXBODY)\r
+ DrinkPotion ();\r
+\r
+ if (Keyboard[sc_Z] && !boltsleft)\r
+ CastBolt ();\r
+\r
+ if ( (Keyboard[sc_Enter] || Keyboard[sc_X]) && ((TimeCount-lastnuke > NUKETIME) || (autofire)))\r
+ CastNuke ();\r
+\r
+#if 0\r
+ scroll = LastScan-2;\r
+ if ( scroll>=0 && scroll<NUMSCROLLS && gamestate.scrolls[scroll])\r
+ ReadScroll (scroll);\r
+#endif\r
+\r
+ DrawText(false);\r
+ DrawHealth();\r
+ if (FreezeTime)\r
+ DrawFreezeTime();\r
+ DrawRadar();\r
+ EGAWRITEMODE(0);\r
+ DrawNSEWIcons();\r
+\r
+ if (redraw_gems)\r
+ DrawGems();\r
+\r
+#if 0\r
+// gems fade out over time...\r
+//\r
+ for (loop=0; loop<5; loop++)\r
+ if (gamestate.gems[loop])\r
+ {\r
+ gamestate.gems[loop] -= realtics;\r
+ if (gamestate.gems[loop] < 0)\r
+ {\r
+ gamestate.gems[loop] = 0;\r
+ redraw_gems = true;\r
+ }\r
+ }\r
+#endif\r
+}\r
+\r
+#if 0\r
+//------------------------------------------------------------------------\r
+// FaceDir() -\r
+//\r
+// PARAMS : x,y - pixle coords to bring in to view.\r
+//\r
+// NOTE : Params CAN NOT be shifted fracs!\r
+//------------------------------------------------------------------------\r
+void FaceDir(short x,short y,boolean StopTime)\r
+{\r
+ short diff;\r
+\r
+ RotateAngle = CalcAngle(x-(player->x>>16l),(player->y>>16l)-y);\r
+ FreezeTime = StopTime;\r
+\r
+ diff = player->angle - RotateAngle;\r
+\r
+ if (((diff>0) && (diff<180)) || ((diff<0) && (diff>-180)))\r
+ RotateSpeed = -ROTATE_SPEED;\r
+ else\r
+ RotateSpeed = ROTATE_SPEED;\r
+}\r
+#endif\r
+\r
+#if 0\r
+//------------------------------------------------------------------------\r
+// CalcAngle() -\r
+//\r
+// DESC: Calculates the angle from a given dy & dx\r
+//------------------------------------------------------------------------\r
+short CalcAngle(short dx,short dy)\r
+{\r
+ #define degtorad (180/PI)\r
+ float angle;\r
+ short diff;\r
+ float rad_angle;\r
+\r
+ if (dx)\r
+ {\r
+ angle = atan((float)dy/dx)* degtorad;\r
+ if (angle<=0)\r
+ angle += 180;\r
+ if (dy>0)\r
+ angle += 180;\r
+ }\r
+ else\r
+ {\r
+ // 90 Deg shift\r
+\r
+ if (dy < 0)\r
+ angle = 0 + 90; // Above player (NORTH)\r
+ else\r
+ angle = 180 + 90; // Below player (SOUTH)\r
+ }\r
+\r
+ if (!angle) // HACK\r
+ angle++;\r
+\r
+ return((short)abs(angle));\r
+}\r
+\r
+#endif\r
+\r
+#if 0\r
+\r
+//-------------------------------------------------------------------------\r
+// RotateView() -\r
+//\r
+// DESC : Rotates view (current view of game) to a dest angle.\r
+//-------------------------------------------------------------------------\r
+void RotateView()\r
+{\r
+ short LastPos;\r
+\r
+ // Store old angle position then change angle...\r
+ //\r
+\r
+ LastPos = player->angle;\r
+\r
+ player->angle += RotateSpeed;\r
+\r
+ // Check to see if we cranked past out dest angle...\r
+ //\r
+\r
+\r
+ if ((player->angle>ANGLES) || (!player->angle))\r
+ player->angle = 1;\r
+ else\r
+ if (player->angle<1)\r
+ player->angle = ANGLES;\r
+\r
+ // Check to see if we over shot our dest angle...\r
+ //\r
+\r
+ if (((LastPos < RotateAngle) && (player->angle > RotateAngle) && (RotateSpeed > 0)) ||\r
+ ((LastPos > RotateAngle) && (player->angle < RotateAngle) && (RotateSpeed < 0)))\r
+ player->angle = RotateAngle;\r
+\r
+ // Check for ending force turn....\r
+ //\r
+\r
+ if (player->angle == RotateAngle)\r
+ RotateAngle = -1;\r
+\r
+}\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// InitRotate()\r
+//--------------------------------------------------------------------------\r
+void InitRotate(short DestAngle)\r
+{\r
+ if (player->angle != DestAngle)\r
+ {\r
+ RotateAngle = DestAngle;\r
+\r
+ if (player->angle > DestAngle)\r
+ RotateSpeed = -ROTATE_SPEED;\r
+ else\r
+ RotateSpeed = ROTATE_SPEED;\r
+\r
+ if (abs(player->angle - RotateAngle) > 180)\r
+ RotateSpeed =- RotateSpeed;\r
+ }\r
+}\r
+\r
+\r
+\r
+//------------------------------------------------------------------------\r
+// FaceAngle() -\r
+//\r
+// PARAMS : DestAngle - Destination angle to turn to\r
+//------------------------------------------------------------------------\r
+void FaceAngle(short DestAngle)\r
+{\r
+ signed long dx,dy,radius,psin,pcos,newx,newy;\r
+ int give;\r
+ short objnum,LastPos;\r
+ signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy;\r
+ short o_radius;\r
+ void (*think)();\r
+\r
+\r
+ // Calculate the direction we want to turn to...\r
+ //\r
+\r
+ InitRotate(DestAngle);\r
+\r
+ RedrawStatusWindow();\r
+\r
+ while (RotateAngle != -1)\r
+ {\r
+\r
+ RotateView();\r
+\r
+// PollControls();\r
+\r
+ objnum=0;\r
+\r
+ for (obj = player;obj;obj = obj->next)\r
+ {\r
+ if (obj->active >= yes)\r
+ {\r
+\r
+ // keep a list of objects around the player for radar updates\r
+ //\r
+ if (obj == player)\r
+ {\r
+ px = player->x;\r
+ py = player->y;\r
+ psin = sintable[player->angle];\r
+ pcos = costable[player->angle];\r
+ xl = px-((long)RADAR_WIDTH<<TILESHIFT)/2;\r
+ xh = px+((long)RADAR_WIDTH<<TILESHIFT)/2-1;\r
+ yl = py-((long)RADAR_HEIGHT<<TILESHIFT)/2;\r
+ yh = py+((long)RADAR_HEIGHT<<TILESHIFT)/2;\r
+ }\r
+\r
+ if (objnum > MAX_RADAR_BLIPS-2)\r
+ objnum = MAX_RADAR_BLIPS-2;\r
+\r
+ ox = obj->x;\r
+ oy = obj->y;\r
+\r
+\r
+ if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh))\r
+ {\r
+ norm_dx = (dx = px-ox)>>TILESHIFT;\r
+ norm_dy = (dy = oy-py)>>TILESHIFT;\r
+\r
+ o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy));\r
+\r
+ if (o_radius < RADAR_RADIUS)\r
+ {\r
+ newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin);\r
+ newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos);\r
+\r
+ RadarXY[objnum][0]=newx>>TILESHIFT;\r
+ RadarXY[objnum][1]=newy>>TILESHIFT;\r
+\r
+ // Define color to use for this object...\r
+ //\r
+\r
+ switch (obj->obclass)\r
+ {\r
+ case playerobj:\r
+ RadarXY[objnum++][2]=15;\r
+ break;\r
+\r
+ // RED GEM\r
+ //\r
+ case godessobj:\r
+ if (gamestate.gems[B_RGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=12;\r
+ break;\r
+\r
+ // GREEN GEM\r
+ //\r
+ case fatdemonobj:\r
+ if (gamestate.gems[B_GGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=10;\r
+ break;\r
+\r
+ // YELLOW GEM\r
+ //\r
+ case skeletonobj:\r
+ if (gamestate.gems[B_YGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=14;\r
+ break;\r
+\r
+ // BLUE GEM\r
+ //\r
+ case mageobj:\r
+ case wetobj:\r
+ if (gamestate.gems[B_BGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=9;\r
+ break;\r
+\r
+ // PURPLE GEM\r
+ //\r
+ case zombieobj:\r
+ if (gamestate.gems[B_PGEM-B_RGEM])\r
+ if (obj->active == always)\r
+ RadarXY[objnum++][2]=13;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ RadarXY[objnum][2]=-1; // Signals end of RadarXY list...\r
+\r
+// refresh all\r
+//\r
+\r
+ ThreeDRefresh();\r
+ DrawRadar();\r
+ EGAWRITEMODE(0);\r
+ DrawNSEWIcons();\r
+\r
+// CheckKeys();\r
+ }\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// FaceDoor() - Turns the player to face a door (a tile) at a given TILE x & y\r
+//\r
+// RETURNS : Returns the orginal angle of the player.\r
+//------------------------------------------------------------------------\r
+short FaceDoor(short x, short y)\r
+{\r
+ short p_x,p_y,angle,old_angle;\r
+\r
+ old_angle = player->angle;\r
+\r
+ p_x = player->x>>16l;\r
+ p_y = player->y>>16l;\r
+\r
+ if (p_x != x)\r
+ {\r
+ if (p_x > x)\r
+ angle = 180; // Face Left\r
+ else\r
+ angle = 1; // Face Right\r
+ }\r
+\r
+ if (p_y != y)\r
+ {\r
+ if (p_y > y)\r
+ angle = 90; // Face Up\r
+ else\r
+ angle = 270; // Face Down\r
+ }\r
+\r
+ FaceAngle(angle);\r
+\r
+ return(old_angle);\r
+}\r
+\r
+\r
+#endif\r
+\r
+\r
+\r
+/*==========================================================================\r
+\r
+ EXPLOSION SPAWNING ROUTINES\r
+\r
+===========================================================================*/\r
+\r
+statetype s_explode = {0,1,T_ExpThink,&s_explode};\r
+\r
+//-------------------------------------------------------------------------\r
+// SpawnExplosion()\r
+//------------------------------------------------------------------------\r
+void SpawnExplosion(fixed x, fixed y, short Delay)\r
+{\r
+ DSpawnNewObjFrac(x,y,&s_explode,PIXRADIUS*7);\r
+ new->obclass = expobj;\r
+ new->active = always;\r
+ new->temp1 = Delay;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// T_ExpThink()\r
+//---------------------------------------------------------------------------\r
+void T_ExpThink(objtype *obj)\r
+{\r
+ if (obj->temp1)\r
+ {\r
+ if ((obj->temp1-=realtics) <= 0)\r
+ obj->temp1 = 0;\r
+ }\r
+ else\r
+ {\r
+ obj->state = &s_pshot_exp1;\r
+ obj->ticcount = obj->state->tictime;\r
+ SD_PlaySound(BOOMSND);\r
+ }\r
+}\r
+\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// SpawnBigExplosion()\r
+//------------------------------------------------------------------------\r
+void SpawnBigExplosion(fixed x, fixed y, short Delay, fixed Range)\r
+{\r
+ SpawnExplosion(x-random(Range),y+random(Range),random(Delay));\r
+ SpawnExplosion(x+random(Range),y-random(Range),random(Delay));\r
+ SpawnExplosion(x-random(Range),y-random(Range),random(Delay));\r
+ SpawnExplosion(x+random(Range),y+random(Range),random(Delay));\r
+}\r
+\r
--- /dev/null
+ 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.
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "ID_HEADS.H"\r
+#include <MATH.H>\r
+#include <VALUES.H>\r
+\r
+//#define PROFILE\r
+\r
+#define DEBUG_KEYS_AVAILABLE 0\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+//\r
+// SOFTLIB GFX FILENAME\r
+//\r
+#define SLIB_GFX "ARM_SLIB."EXT\r
+\r
+\r
+#define USE_INERT_LIST false\r
+\r
+\r
+//\r
+// MAX_BASE - represents 100 percent in 1st base\r
+// MAX_PERC - represents 100 percent in 2nd base\r
+// PERC - fractional portion of 2nd base\r
+// SCALE - arbitrary scaling value (bigger number means more accurate)\r
+//\r
+// ex: PERCENTAGE(320,16,8,7) returns 160\r
+//\r
+// Make sure values used won't overflow a WORD! In general, if largest number\r
+// to be used (320 in ex: above) * (1<<SCALE) is greater than 65535, use\r
+// LONG_PERCENTAGE or a lower SCALE. Using a SCALE of 8 in the example\r
+// above would overflow a WORD in some circumstances!\r
+//\r
+// LONG_PERCENTAGE is to be used for larger SCALEs, thus, giving you\r
+// massive accuracy!\r
+//\r
+#define PERCENTAGE(MAX_BASE,MAX_PERC,PERC,SCALE) ((unsigned)(MAX_BASE*((PERC<<SCALE)/MAX_PERC))>>SCALE)\r
+#define LONG_PERCENTAGE(MAX_BASE,MAX_PERC,PERC,SCALE) (((long)MAX_BASE*(((long)PERC<<SCALE)/MAX_PERC))>>SCALE)\r
+\r
+#define PI 3.141592657\r
+\r
+//#define SIZE_TEST 65000\r
+#define SIZE_TEST 0\r
+\r
+\r
+#define FL_QUICK 0x01\r
+#define FL_NOMEMCHECK 0x02\r
+#define FL_HELPTEST 0x04\r
+\r
+#define FL_CLEAR (FL_QUICK|FL_NOMEMCHECK|FL_HELPTEST)\r
+\r
+#if 0\r
+#define GEM_SHIFT 2\r
+#define FL_RGEM 0x04\r
+#define FL_GGEM 0x08\r
+#define FL_BGEM 0x10\r
+#define FL_YGEM 0x20\r
+#define FL_PGEM 0x40\r
+#endif\r
+\r
+#define FL_DEAD 0x80\r
+\r
+\r
+\r
+#define MAXBOLTS 10\r
+#define MAXNUKES 10\r
+#define MAXPOTIONS 10\r
+\r
+#define NUKE_COST (1000)\r
+#define BOLT_COST (1200)\r
+#define POTION_COST (1300)\r
+\r
+#define NUKE_COST_TXT ("1000") // Allows for Q&D positioning..\r
+#define BOLT_COST_TXT ("1200")\r
+#define POTION_COST_TXT ("1300")\r
+\r
+#define RADARX 31 // bytes\r
+#define RADARY 11 // pixels\r
+#define RADAR_WIDTH 51 // "\r
+#define RADAR_HEIGHT 51 // "\r
+#define RADAR_XCENTER ((RADARX*8)+(RADAR_WIDTH/2)+3) // "\r
+#define RADAR_YCENTER ((RADARY-8)+(RADAR_HEIGHT/2)+5) // "\r
+#define MAX_RADAR_BLIPS 60\r
+\r
+\r
+#define RADAR_RADIUS 17\r
+#define RADAR_RADIUS_NSEW 15\r
+#define RADAR_X_IRADIUS (113/5)\r
+#define RADAR_Y_IRADIUS (113/7)\r
+#define RADAR_ICON_CENTER 4 // Center offset into icon.\r
+\r
+#define NAMESTART 180\r
+#define REMOVED_DOOR_TILE NAMESTART\r
+\r
+#define NEXT_LEVEL_CODE 0xff\r
+#define REMOVE_DOOR_CODE 0xfe\r
+#define CANT_OPEN_CODE 0xfd\r
+#define EXP_WALL_CODE 0xfc\r
+\r
+\r
+#define UNMARKGRCHUNK(chunk) (grneeded[chunk]&=~ca_levelbit)\r
+\r
+#define MOUSEINT 0x33\r
+\r
+#define EXPWALLSTART 8\r
+#define NUMEXPWALLS 7\r
+#define WALLEXP 59\r
+#define WATEREXP 62\r
+#define NUMFLOORS 80 //71\r
+\r
+#define NUMLATCHPICS (FIRSTWALLPIC-FIRSTLATCHPIC) //+5\r
+#define NUMSCALEPICS (FIRSTWALLPIC-FIRSTSCALEPIC) //+5\r
+#define NUMSCALEWALLS (LASTWALLPIC-FIRSTWALLPIC) //+5\r
+\r
+\r
+#define FLASHCOLOR 12\r
+#define FLASHTICS 4\r
+\r
+\r
+#define NUMLEVELS 32 //21\r
+\r
+#define VIEWX 0 // corner of view window\r
+#define VIEWY 0\r
+#define VIEWWIDTH (40*8) // size of view window // 33\r
+#define VIEWHEIGHT (15*8) // 18\r
+#define VIEWXH (VIEWX+VIEWWIDTH-1)\r
+#define VIEWYH (VIEWY+VIEWHEIGHT-1)\r
+\r
+#define CENTERX (VIEWX+VIEWWIDTH/2-1) // middle of view window\r
+#define CENTERY (VIEWY+VIEWHEIGHT/2-1)\r
+\r
+#define GLOBAL1 (1l<<16)\r
+#define TILEGLOBAL GLOBAL1\r
+#define TILESHIFT 16l\r
+\r
+#define MINDIST (2*GLOBAL1/5)\r
+#define FOCALLENGTH (TILEGLOBAL) // in global coordinates\r
+\r
+#define ANGLES 360 // must be divisable by 4\r
+\r
+#define MAPSIZE 64 // maps are 64*64 max\r
+#define MAXACTORS 100 // max number of tanks, etc / map\r
+\r
+#define NORTH 0\r
+#define EAST 1\r
+#define SOUTH 2\r
+#define WEST 3\r
+\r
+#define SIGN(x) ((x)>0?1:-1)\r
+#define ABS(x) ((int)(x)>0?(x):-(x))\r
+#define LABS(x) ((long)(x)>0?(x):-(x))\r
+\r
+#define MAXSCALE (VIEWWIDTH/2)\r
+\r
+\r
+#define MAXBODY 100\r
+#define MAXSHOTPOWER 56\r
+\r
+#define SCREEN1START 0\r
+#define SCREEN2START 8320\r
+\r
+#define STATUSLEN 0xc80\r
+#define PAGELEN 0x1700\r
+\r
+#define PAGE1START STATUSLEN\r
+#define PAGE2START (PAGE1START+PAGELEN)\r
+#define PAGE3START (PAGE2START+PAGELEN)\r
+#define FREESTART (PAGE3START+PAGELEN)\r
+\r
+#define PIXRADIUS 512\r
+\r
+#define STATUSLINES (200-VIEWHEIGHT)\r
+\r
+enum bonusnumbers {B_BOLT,B_NUKE,B_POTION,B_RKEY,B_YKEY,B_GKEY,B_BKEY,B_SCROLL1,\r
+ B_SCROLL2,B_SCROLL3,B_SCROLL4,B_SCROLL5,B_SCROLL6,B_SCROLL7,B_SCROLL8,\r
+ B_GOAL,B_CHEST,B_RGEM,B_GGEM,B_BGEM,B_YGEM,B_PGEM};\r
+\r
+\r
+#define MAX_DOOR_STORAGE 5\r
+\r
+#define GEM_DELAY_TIME (120*60)\r
+\r
+#define ROTATE_SPEED (6)\r
+\r
+#define WALL_SKELETON_CODE 6\r
+\r
+#define MAXREALTICS (2*60)\r
+\r
+#define MAXFREEZETIME (100*30) // 50 secs (100 half)\r
+\r
+#define INVISIBLEWALL 0x46\r
+\r
+#define USE_STRIPS FALSE\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL TYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+enum {BLANKCHAR=9,BOLTCHAR,NUKECHAR,POTIONCHAR,KEYCHARS,SCROLLCHARS=17,\r
+ NUMBERCHARS=25};\r
+\r
+typedef long fixed;\r
+\r
+typedef struct {int x,y;} tilept;\r
+typedef struct {fixed x,y;} globpt;\r
+\r
+typedef struct\r
+{\r
+ int x1,x2,leftclip,rightclip;// first pixel of wall (may not be visable)\r
+ unsigned height1,height2,color,walllength,side;\r
+ long planecoord;\r
+} walltype;\r
+\r
+typedef enum\r
+ {nothing,playerobj,bonusobj,succubusobj,batobj,skeletonobj,fatdemonobj,godessobj,\r
+ mageobj,pshotobj,bigpshotobj,mshotobj,inertobj,bounceobj,grelmobj,sshotobj,\r
+ gateobj,zombieobj,antobj,wetobj,expobj,eyeobj,wallskelobj,eshotobj,treeobj,\r
+ gshotobj,reddemonobj,freezeobj,solidobj,cloudobj,dshotobj,hbunnyobj,bunnyobj,\r
+ realsolidobj} classtype;\r
+\r
+typedef enum {north,east,south,west,northeast,southeast,southwest,\r
+ northwest,nodir} dirtype; // a catacombs 2 carryover\r
+\r
+typedef struct statestruct\r
+{\r
+ int shapenum;\r
+ int tictime;\r
+ void (*think) ();\r
+ struct statestruct far *next;\r
+} statetypestruct;\r
+\r
+#define statetype statetypestruct far\r
+\r
+#define of_shootable 0x01\r
+#define of_damagedone 0x02\r
+#define of_forcefield 0x40 // defines a solid object as a forcefield???????????\r
+#define of_tree 0x80 // used to identify between a tree and a statue --\r
+ // last minute changes for Greg\r
+\r
+typedef struct objstruct\r
+{\r
+\r
+ int ticcount; //\r
+ statetype *state; // THESE MEMBERS MUST BE IN THE SAME\r
+ fixed x,y; // ORDER AS THE MEMBERS DEFINED IN\r
+ int viewx; // IOBJSTRUCT OR ALL HELL WILL BREAK\r
+ unsigned tilex,tiley; // LOOSE!!\r
+ unsigned viewheight; //\r
+ unsigned size; //\r
+ struct objstruct *next; //\r
+\r
+\r
+ struct objstruct *prev;\r
+ enum {no,noalways,yes,always} active;\r
+ classtype obclass;\r
+\r
+ unsigned char flags;\r
+\r
+ long distance;\r
+ dirtype dir;\r
+\r
+ int angle;\r
+ int hitpoints;\r
+ long speed;\r
+\r
+ fixed xl,xh,yl,yh; // hit rectangle\r
+\r
+ int temp1,temp2;\r
+} objtype;\r
+\r
+#if USE_INERT_LIST\r
+\r
+#define MAXINERTOBJ 20\r
+\r
+typedef struct iobjstruct {\r
+ int ticcount;\r
+ statetype *state;\r
+ fixed x,y;\r
+ int viewx;\r
+ unsigned tilex,tiley;\r
+ unsigned viewheight;\r
+ unsigned size;\r
+ struct iobjstruct *next;\r
+} inertobjtype;\r
+\r
+#endif\r
+\r
+\r
+typedef enum {ex_stillplaying,ex_died,ex_warped,ex_resetgame\r
+ ,ex_loadedgame,ex_victorious,ex_turning,ex_abort} exittype;\r
+\r
+\r
+typedef enum {S_NONE, S_HELP, S_SND, S_SAVING, S_RESTORING,\r
+ S_JOYSTICK, S_RETREAT, S_ADVANCE, S_SIDESTEP, S_QTURN,\r
+ S_MISSLE, S_ZAPPER, S_XTER, S_CURING, S_READ,\r
+ S_VIEWING, S_ITEMDES, S_DAMAGE, S_TURN, S_TIMESTOP} status_flags;\r
+\r
+typedef struct {\r
+ char x,y;\r
+ unsigned ondoor,underdoor;\r
+} doorinfo;\r
+\r
+typedef struct {\r
+ char x,y;\r
+ short angle;\r
+ doorinfo doors[MAX_DOOR_STORAGE];\r
+} levelinfo;\r
+\r
+typedef struct\r
+{\r
+ int difficulty;\r
+ int mapon;\r
+ int bolts,nukes,potions,keys[4],scrolls[8];\r
+\r
+ int gems[5]; // "int allgems[5]" is used for 1:1 comparison\r
+ // in play loop for radar... CHANGE IT, TOO!\r
+\r
+ long score;\r
+ int body,shotpower;\r
+ short mapwidth,mapheight;\r
+// levelinfo levels[NUMLEVELS];\r
+} gametype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_MAIN DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern char inlevel[][2];\r
+extern char str[80],str2[20];\r
+extern unsigned tedlevelnum;\r
+extern boolean tedlevel;\r
+extern gametype gamestate;\r
+extern exittype playstate;\r
+extern char SlowMode;\r
+extern unsigned Flags;\r
+extern boolean LoadShapes;\r
+extern boolean EASYMODEON;\r
+\r
+\r
+void NewGame (void);\r
+boolean SaveTheGame(int file);\r
+boolean LoadTheGame(int file);\r
+void ResetGame(void);\r
+void ShutdownId (void);\r
+void InitGame (void);\r
+void Quit (char *error, ...);\r
+void TEDDeath(void);\r
+void DemoLoop (void);\r
+void SetupScalePic (unsigned picnum);\r
+void SetupScaleWall (unsigned picnum);\r
+void SetupScaling (void);\r
+void main (void);\r
+void Display320(void);\r
+void Display640(void);\r
+void PrintHelp(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_GAME DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern unsigned latchpics[NUMLATCHPICS];\r
+extern unsigned tileoffsets[NUMTILE16];\r
+extern unsigned textstarts[27];\r
+\r
+\r
+#define L_CHARS 0\r
+#define L_NOSHOT 1\r
+#define L_SHOTBAR 2\r
+#define L_NOBODY 3\r
+#define L_BODYBAR 4\r
+\r
+\r
+void ScanInfoPlane (void);\r
+void ScanText (void);\r
+void SetupGameLevel(void);\r
+void Victory (boolean playsounds);\r
+void Died (void);\r
+void NormalScreen (void);\r
+void DrawPlayScreen (void);\r
+void LoadLatchMem (void);\r
+void FizzleFade (unsigned source, unsigned dest,\r
+ unsigned width,unsigned height, boolean abortable);\r
+void FizzleOut (int showlevel);\r
+void FreeUpMemory (void);\r
+void GameLoop (void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_PLAY DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define BGF_NIGHT 0x01 // it is officially night\r
+#define BGF_NOT_LIGHTNING 0x02 // lightning flash has ended\r
+\r
+extern byte BGFLAGS,bcolor;\r
+\r
+extern unsigned *skycolor,*groundcolor;\r
+\r
+extern ControlInfo control;\r
+extern boolean running,slowturn;\r
+\r
+extern int bordertime;\r
+\r
+extern byte tilemap[MAPSIZE][MAPSIZE];\r
+extern objtype *actorat[MAPSIZE][MAPSIZE];\r
+extern byte spotvis[MAPSIZE][MAPSIZE];\r
+\r
+extern objtype objlist[MAXACTORS],*new,*obj,*player;\r
+\r
+extern unsigned farmapylookup[MAPSIZE];\r
+extern byte *nearmapylookup[MAPSIZE];\r
+extern byte update[];\r
+\r
+extern boolean godmode,singlestep;\r
+extern int extravbls;\r
+\r
+extern int mousexmove,mouseymove;\r
+extern int pointcount,pointsleft;\r
+extern status_flags status_flag;\r
+extern int status_delay;\r
+\r
+extern objtype dummyobj;\r
+extern short BeepTime;\r
+extern unsigned scolor,gcolor;\r
+\r
+void CenterWindow(word w,word h);\r
+void DebugMemory (void);\r
+void PicturePause (void);\r
+int DebugKeys (void);\r
+void CheckKeys (void);\r
+void InitObjList (void);\r
+void GetNewObj (boolean usedummy);\r
+void RemoveObj (objtype *gone);\r
+void PollControlls (void);\r
+void PlayLoop (void);\r
+void InitBgChange(short stimer, unsigned *scolors, short gtimer, unsigned *gcolors, byte flag);\r
+\r
+void DisplayStatus (status_flags *stat_flag);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_STATE DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+//void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size);\r
+//void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size);\r
+\r
+void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat);\r
+void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy);\r
+\r
+#define DSpawnNewObj(x, y, state, size) Internal_SpawnNewObj(x,y,state,size,true,true)\r
+#define SpawnNewObj(x, y, state, size) Internal_SpawnNewObj(x,y,state,size,false,true)\r
+#define ASpawnNewObj(x, y, state, size) Internal_SpawnNewObj(x,y,state,size,false,false)\r
+#define SpawnNewObjFrac(x, y, state, size,Dummy) Internal_SpawnNewObjFrac(x, y, state, size,false)\r
+#define DSpawnNewObjFrac(x, y, state, size) Internal_SpawnNewObjFrac(x, y, state, size,true)\r
+\r
+boolean CheckHandAttack (objtype *ob);\r
+void T_DoDamage (objtype *ob);\r
+boolean Walk (objtype *ob);\r
+void ChaseThink (objtype *obj, boolean diagonal);\r
+void MoveObj (objtype *ob, long move);\r
+boolean Chase (objtype *ob, boolean diagonal);\r
+\r
+extern dirtype opposite[9];\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_TRACE DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);\r
+int BackTrace (int finish);\r
+void ForwardTrace (void);\r
+int FinishWall (void);\r
+void InsideCorner (void);\r
+void OutsideCorner (void);\r
+void FollowWalls (void);\r
+\r
+extern boolean aborttrace;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_DRAW DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define MAXWALLS 50\r
+#define DANGERHIGH 45\r
+\r
+#define MIDWALL (MAXWALLS/2)\r
+\r
+//==========================================================================\r
+\r
+extern tilept tile,lasttile,focal,left,mid,right;\r
+\r
+extern globpt edge,view;\r
+\r
+extern unsigned screenloc[3];\r
+extern unsigned freelatch;\r
+\r
+extern int screenpage;\r
+\r
+extern boolean fizzlein;\r
+\r
+extern long lasttimecount;\r
+\r
+extern int firstangle,lastangle;\r
+\r
+extern fixed prestep;\r
+\r
+extern int traceclip,tracetop;\r
+\r
+extern fixed sintable[ANGLES+ANGLES/4],*costable;\r
+\r
+extern fixed viewx,viewy,viewsin,viewcos; // the focal point\r
+extern int viewangle;\r
+\r
+extern fixed scale,scaleglobal;\r
+extern unsigned slideofs;\r
+\r
+extern int zbuffer[VIEWXH+1];\r
+\r
+extern walltype walls[MAXWALLS],*leftwall,*rightwall;\r
+\r
+\r
+extern fixed tileglobal;\r
+extern fixed focallength;\r
+extern fixed mindist;\r
+extern int viewheight;\r
+extern fixed scale;\r
+\r
+extern int far walllight1[NUMFLOORS];\r
+extern int far walldark1[NUMFLOORS];\r
+\r
+extern unsigned topcolor,bottomcolor;\r
+\r
+extern char wall_anim_info[NUMFLOORS];\r
+extern char wall_anim_pos[NUMFLOORS];\r
+\r
+//==========================================================================\r
+\r
+void DrawLine (int xl, int xh, int y,int color);\r
+void DrawWall (walltype *wallptr);\r
+void TraceRay (unsigned angle);\r
+fixed FixedByFrac (fixed a, fixed b);\r
+void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight);\r
+fixed TransformX (fixed gx, fixed gy);\r
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);\r
+void ForwardTrace (void);\r
+int FinishWall (void);\r
+int TurnClockwise (void);\r
+int TurnCounterClockwise (void);\r
+void FollowWall (void);\r
+\r
+void NewScene (void);\r
+void BuildTables (void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_SCALE DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+#define COMPSCALECODESTART (65*6) // offset to start of code in comp scaler\r
+\r
+typedef struct\r
+{\r
+ unsigned codeofs[65];\r
+ unsigned start[65];\r
+ unsigned width[65];\r
+ byte code[];\r
+} t_compscale;\r
+\r
+typedef struct\r
+{\r
+ unsigned width;\r
+ unsigned codeofs[64];\r
+} t_compshape;\r
+\r
+\r
+extern unsigned scaleblockwidth,\r
+ scaleblockheight,\r
+ scaleblockdest;\r
+\r
+extern byte plotpix[8];\r
+extern byte bitmasks1[8][8];\r
+extern byte bitmasks2[8][8];\r
+\r
+\r
+extern t_compscale _seg *scaledirectory[NUMSCALEPICS];\r
+extern t_compshape _seg *shapedirectory[NUMSCALEPICS];\r
+extern memptr walldirectory[NUMSCALEWALLS];\r
+extern unsigned shapesize[NUMSCALEPICS];\r
+\r
+void DeplanePic (int picnum);\r
+void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale);\r
+unsigned BuildCompShape (t_compshape _seg **finalspot);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_ASM DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern unsigned wallheight [VIEWWIDTH];\r
+extern unsigned wallwidth [VIEWWIDTH];\r
+extern unsigned wallseg [VIEWWIDTH];\r
+extern unsigned wallofs [VIEWWIDTH];\r
+extern unsigned screenbyte [VIEWWIDTH];\r
+extern unsigned screenbit [VIEWWIDTH];\r
+extern unsigned bitmasks [64];\r
+\r
+extern long wallscalecall;\r
+\r
+void ScaleWalls (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_WIZ DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define MAXHANDHEIGHT 72\r
+\r
+extern statetype s_pshot_exp1;\r
+extern statetype s_pshot_exp2;\r
+extern statetype s_pshot_exp3;\r
+\r
+extern long lastnuke;\r
+extern int lasttext;\r
+extern int handheight;\r
+extern int boltsleft,bolttimer;\r
+extern short RadarXY[][3];\r
+\r
+extern short RotateAngle;\r
+extern short FreezeTime;\r
+\r
+//void FaceDir(short x, short y, boolean StopTime);\r
+//short CalcAngle(short dx, short dy);\r
+\r
+void FaceAngle(short DestAngle);\r
+void RotateView();\r
+void InitRotate(short DestAngle);\r
+short FaceDoor(short x, short y);\r
+\r
+char DisplayMsg(char *text,char *choices);\r
+char DisplaySMsg(char *text,char *choices);\r
+\r
+extern statetype s_explode;\r
+\r
+void SpawnExplosion(fixed x, fixed y,short Delay);\r
+void T_ExpThink(objtype *obj);\r
+void SpawnBigExplosion(fixed x, fixed y, short Delay, fixed Range);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ C3_ACT1 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+int EasyHitPoints(int NrmHitPts);\r
+int EasyDoDamage(int Damage);\r
+\r
+#define zombie_mode ob->temp1\r
+#define zombie_delay ob->temp2\r
+\r
+enum zombie_modes {zm_wait_for_dark,zm_wait_to_rise,zm_active};\r
+\r
+enum eye_modes {em_other1,em_player1,em_other2,em_player2,em_other3,em_player3,em_other4,em_player4,em_dummy};\r
+\r
+#define MSHOTDAMAGE 2\r
+#define MSHOTSPEED 10000\r
+\r
+#define ESHOTDAMAGE 1\r
+#define ESHOTSPEED 5000\r
+\r
+#define SSHOTDAMAGE 3\r
+#define SSHOTSPEED 6500\r
+\r
+#define RANDOM_ATTACK 20\r
+\r
+extern dirtype dirtable[];\r
+extern short other_x[], other_y[];\r
+extern short zombie_base_delay;\r
+\r
+extern statetype s_fatdemon_ouch;\r
+extern statetype s_fatdemon_blowup1;\r
+\r
+extern statetype s_succubus_ouch;\r
+extern statetype s_succubus_death1;\r
+\r
+extern statetype s_godessouch;\r
+extern statetype s_godessdie1;\r
+\r
+extern statetype s_mageouch;\r
+extern statetype s_magedie1;\r
+\r
+extern statetype s_grelouch;\r
+extern statetype s_greldie1;\r
+\r
+extern statetype s_batdie1;\r
+\r
+extern statetype s_zombie_death1;\r
+extern statetype s_zombie_ouch;\r
+\r
+extern statetype s_zombie_rise1;\r
+extern statetype s_zombie_rise2;\r
+extern statetype s_zombie_rise3;\r
+extern statetype s_zombie_rise4;\r
+\r
+extern statetype s_ant_wait;\r
+extern statetype s_ant_egg;\r
+extern statetype s_ant_walk1;\r
+extern statetype s_ant_walk2;\r
+extern statetype s_ant_walk3;\r
+extern statetype s_ant_attack1;\r
+extern statetype s_ant_pause;\r
+extern statetype s_ant_ouch;\r
+extern statetype s_ant_die1;\r
+extern statetype s_ant_die2;\r
+extern statetype s_ant_die3;\r
+\r
+extern statetype s_skel_pause;\r
+extern statetype s_skel_1;\r
+extern statetype s_skel_2;\r
+extern statetype s_skel_3;\r
+extern statetype s_skel_4;\r
+extern statetype s_skel_attack1;\r
+extern statetype s_skel_attack2;\r
+extern statetype s_skel_attack3;\r
+extern statetype s_skel_ouch;\r
+extern statetype s_skel_die1;\r
+extern statetype s_skel_die2;\r
+extern statetype s_skel_die3;\r
+\r
+extern statetype s_wet_pause;\r
+\r
+extern statetype s_wet_bubbles1;\r
+extern statetype s_wet_bubbles2;\r
+extern statetype s_wet_bubbles3;\r
+extern statetype s_wet_bubbles4;\r
+\r
+extern statetype s_wet_peek;\r
+\r
+extern statetype s_wet_rise1;\r
+extern statetype s_wet_rise2;\r
+extern statetype s_wet_rise3;\r
+extern statetype s_wet_rise4;\r
+extern statetype s_wet_rise5;\r
+\r
+extern statetype s_wet_sink1;\r
+extern statetype s_wet_sink2;\r
+extern statetype s_wet_sink3;\r
+\r
+extern statetype s_wet_walk1;\r
+extern statetype s_wet_walk2;\r
+extern statetype s_wet_walk3;\r
+extern statetype s_wet_walk4;\r
+\r
+extern statetype s_wet_attack1;\r
+extern statetype s_wet_attack2;\r
+extern statetype s_wet_attack3;\r
+extern statetype s_wet_attack4;\r
+\r
+extern statetype s_wet_ouch;\r
+\r
+extern statetype s_wet_die1;\r
+extern statetype s_wet_die2;\r
+extern statetype s_wet_die3;\r
+extern statetype s_wet_die4;\r
+extern statetype s_wet_die5;\r
+\r
+extern statetype s_obj_gate1;\r
+extern statetype s_obj_gate2;\r
+extern statetype s_obj_gate3;\r
+extern statetype s_obj_gate4;\r
+\r
+extern statetype s_eye_pause;\r
+\r
+extern statetype s_eye_1;\r
+extern statetype s_eye_2;\r
+extern statetype s_eye_3;\r
+extern statetype s_eye_4;\r
+\r
+extern statetype s_eye_ouch;\r
+extern statetype s_eye_ouch2;\r
+\r
+extern statetype s_eye_die1;\r
+extern statetype s_eye_die2;\r
+extern statetype s_eye_die3;\r
+\r
+extern statetype s_mshot1;\r
+extern statetype s_mshot2;\r
+\r
+extern statetype s_bonus_die;\r
+\r
+extern statetype s_red_demonouch;\r
+extern statetype s_red_demondie1;\r
+\r
+extern statetype s_bunny_death1;\r
+extern statetype s_bunny_ouch;\r
+\r
+extern statetype s_tree_death1;\r
+extern statetype s_tree_ouch;\r
+\r
+extern statetype s_force_field_die;
\ No newline at end of file
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include <dos.h>\r
+#include <conio.h>\r
+#include <stdio.h>\r
+#include <dir.h>\r
+#include "mem.h"\r
+#include "string.h"\r
+#include "time.h"\r
+#include "stdarg.h"\r
+#include "io.h"\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#include "sl_file.h"\r
+\r
+\r
+#define MAX_GAMELIST_NAMES 20\r
+#define FNAME_LEN 9\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Global variables\r
+//\r
+boolean InLoadSaveGame = false;\r
+//AudioDeviceType ge_DigiMode;\r
+boolean ConserveMemory = false;\r
+char GameListNames[MAX_GAMELIST_NAMES+1][FNAME_LEN],current_disk=1;\r
+short NumGames;\r
+short PPT_LeftEdge=0,PPT_RightEdge=320;\r
+boolean LeaveDriveOn=false,ge_textmode=true;\r
+char Filename[FILENAME_LEN+1], ID[sizeof(GAMENAME)], VER[sizeof(SAVEVER_DATA)];\r
+short wall_anim_delay,wall_anim_time = 7;\r
+BufferedIO lzwBIO;\r
+\r
+\r
+\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// CalibrateJoystick()\r
+//\r
+void CalibrateJoystick(short joynum)\r
+{\r
+ word minx,maxx,\r
+ miny,maxy;\r
+\r
+ IN_ClearKeysDown();\r
+\r
+ VW_HideCursor();\r
+\r
+ VW_FixRefreshBuffer();\r
+ CenterWindow(30,8);\r
+\r
+ US_Print("\n");\r
+ US_CPrintLine("Move joystick to the upper-left");\r
+ US_CPrintLine("and press one of the buttons.");\r
+ VW_UpdateScreen();\r
+\r
+ while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joynum));\r
+ if (LastScan == sc_Escape)\r
+ return;\r
+\r
+ IN_GetJoyAbs(joynum,&minx,&miny);\r
+ while (IN_GetJoyButtonsDB(joynum));\r
+\r
+ US_Print("\n");\r
+ US_CPrintLine("Move joystick to the lower-right");\r
+ US_CPrintLine("and press one of the buttons.");\r
+ VW_UpdateScreen();\r
+\r
+ while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joynum));\r
+ if (LastScan == sc_Escape)\r
+ return;\r
+\r
+ IN_GetJoyAbs(joynum,&maxx,&maxy);\r
+ if ((minx == maxx) && (miny == maxy))\r
+ return;\r
+\r
+ IN_SetupJoy(joynum,minx,maxx,miny,maxy);\r
+\r
+ while (IN_GetJoyButtonsDB(joynum));\r
+ if (LastScan)\r
+ IN_ClearKeysDown();\r
+\r
+ JoystickCalibrated = true;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// WaitKeyVBL()\r
+//\r
+void WaitKeyVBL(short key, short vbls)\r
+{\r
+ while (vbls--)\r
+ {\r
+ VW_WaitVBL(1);\r
+ IN_ReadControl(0,&control);\r
+ if ((control.button0|control.button1)||(Keyboard[key]))\r
+ break;\r
+ }\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// MoveScreen()\r
+//\r
+// panadjust must be saved and restored if MoveScreen is being called from\r
+// inside a level.\r
+//\r
+void MoveScreen(short x, short y)\r
+{\r
+ unsigned address;\r
+\r
+ address = (y*linewidth)+(x/8);\r
+ VW_SetScreen(address,0);\r
+ bufferofs = displayofs = address;\r
+ panadjust=0;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// MoveGfxDst()\r
+//\r
+void MoveGfxDst(short x, short y)\r
+{\r
+ unsigned address;\r
+\r
+ address = (y*linewidth)+(x/8);\r
+ bufferofs = displayofs = address;\r
+}\r
+\r
+#if 0\r
+\r
+#if GRAPHIC_PIRATE\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// DoPiracy() - Graphics piracy code...\r
+//\r
+void DoPiracy()\r
+{\r
+ struct Shape Pirate1Shp;\r
+ struct Shape Pirate2Shp;\r
+\r
+ VW_SetScreenMode (EGA320GR);\r
+ VW_ClearVideo(BLACK);\r
+\r
+ // Load shapes...\r
+ //\r
+ if (LoadShape("PIRATE1E."EXT,&Pirate1Shp))\r
+ TrashProg("Can't load PIRATE1E.BIO");\r
+\r
+ if (LoadShape("PIRATE2E."EXT,&Pirate2Shp))\r
+ TrashProg("Can't load PIRATE2E.BIO");\r
+\r
+ // Deal with shapes...\r
+ //\r
+ VW_SetLineWidth(40);\r
+\r
+ VW_FadeOut();\r
+\r
+ MoveScreen(0,0);\r
+ UnpackEGAShapeToScreen(&Pirate1Shp,(linewidth-Pirate1Shp.BPR)<<2,0);\r
+\r
+ MoveScreen(0,200);\r
+ UnpackEGAShapeToScreen(&Pirate2Shp,(linewidth-Pirate2Shp.BPR)<<2,0);\r
+\r
+ MoveScreen(0,0);\r
+ VW_FadeIn();\r
+ WaitKeyVBL(57,200);\r
+ while (Keyboard[57]);\r
+\r
+ SD_PlaySound(GOOD_PICKSND);\r
+\r
+ MoveScreen(0,200);\r
+ WaitKeyVBL(57,300);\r
+ while (Keyboard[57]);\r
+ VW_FadeOut();\r
+\r
+ FreeShape(&Pirate1Shp);\r
+ FreeShape(&Pirate2Shp);\r
+}\r
+\r
+#else\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// DoPiracy() - Text-based piracy code...\r
+//\r
+void DoPiracy()\r
+{\r
+}\r
+\r
+#endif\r
+#endif\r
+\r
+//--------------------------------------------------------------------------\r
+// BlackPalette()\r
+//--------------------------------------------------------------------------\r
+void BlackPalette()\r
+{\r
+ extern char colors[7][17];\r
+\r
+ _ES=FP_SEG(&colors[0]);\r
+ _DX=FP_OFF(&colors[0]);\r
+ _AX=0x1002;\r
+ geninterrupt(0x10);\r
+ screenfaded = true;\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// ColoredPalette()\r
+//--------------------------------------------------------------------------\r
+void ColoredPalette()\r
+{\r
+ extern char colors[7][17];\r
+\r
+ _ES=FP_SEG(&colors[3]);\r
+ _DX=FP_OFF(&colors[3]);\r
+ _AX=0x1002;\r
+ geninterrupt(0x10);\r
+ screenfaded = false;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Verify()\r
+//\r
+long Verify(char *filename)\r
+{\r
+ int handle;\r
+ long size;\r
+\r
+ if ((handle=open(filename,O_BINARY))==-1)\r
+ return (0);\r
+ size=filelength(handle);\r
+ close(handle);\r
+ return(size);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_SaveGame\r
+//\r
+// Handles user i/o for saving a game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+void GE_SaveGame()\r
+{\r
+ boolean GettingFilename=true;\r
+// char Filename[FILENAME_LEN+1]; //, ID[sizeof(GAMENAME)], VER[sizeof(SAVEVER_DATA)];\r
+ int handle;\r
+ struct dfree dfree;\r
+ long davail;\r
+\r
+ VW_FixRefreshBuffer();\r
+ ReadGameList();\r
+ while (GettingFilename)\r
+ {\r
+ DisplayGameList(2,7,3,10);\r
+ US_DrawWindow(5,1,30,3);\r
+ memset(Filename,0,sizeof(Filename));\r
+ US_CPrint("Enter name to SAVE this game:");\r
+ VW_UpdateScreen();\r
+ if (screenfaded)\r
+ VW_FadeIn();\r
+ if (!US_LineInput((linewidth<<2)-32,20,Filename,"",true,8,0))\r
+ goto EXIT_FUNC;\r
+ if (!strlen(Filename))\r
+ goto EXIT_FUNC;\r
+ getdfree(getdisk()+1,&dfree);\r
+ davail = (long)dfree.df_avail*(long)dfree.df_bsec*(long)dfree.df_sclus;\r
+ if (davail < 10000)\r
+ {\r
+ US_CenterWindow(22,4);\r
+ US_Print("\n");\r
+ US_CPrintLine("Disk Full: Can't save game.");\r
+ US_CPrintLine("Try inserting another disk.");\r
+ VW_UpdateScreen();\r
+\r
+ IN_Ack();\r
+ }\r
+ else\r
+ {\r
+ strcat(Filename,".SAV");\r
+ GettingFilename = false;\r
+ if (Verify(Filename)) // FILE EXISTS\r
+ {\r
+ US_CenterWindow(22,4);\r
+ US_CPrintLine("That file already exists...");\r
+ US_CPrintLine("Overwrite it ????");\r
+ US_CPrintLine("(Y)es or (N)o?");\r
+ VW_UpdateScreen();\r
+\r
+ while((!Keyboard[21]) && (!Keyboard[49]) && !Keyboard[27]);\r
+\r
+ if (Keyboard[27])\r
+ goto EXIT_FUNC;\r
+ if (Keyboard[49])\r
+ {\r
+ GettingFilename = true;\r
+ VW_UpdateScreen();\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ handle = open(Filename,O_RDWR|O_CREAT|O_BINARY,S_IREAD|S_IWRITE);\r
+ if (handle==-1)\r
+ goto EXIT_FUNC;\r
+\r
+ if ((!CA_FarWrite(handle,(void far *)GAMENAME,sizeof(GAMENAME))) || (!CA_FarWrite(handle,(void far *)SAVEVER_DATA,sizeof(SAVEVER_DATA))))\r
+ {\r
+ if (!screenfaded)\r
+ VW_FadeOut();\r
+\r
+ return;\r
+ }\r
+\r
+ if (!USL_SaveGame(handle))\r
+ Quit("Save game error");\r
+\r
+\r
+\r
+EXIT_FUNC:;\r
+\r
+ if (handle!=-1)\r
+ close(handle);\r
+\r
+ if (handle==-1)\r
+ {\r
+ remove(Filename);\r
+ US_CenterWindow(22,6);\r
+ US_CPrintLine("DISK ERROR");\r
+ US_CPrintLine("Check: Write protect...");\r
+ US_CPrintLine("File name...");\r
+ US_CPrintLine("Bytes free on disk...");\r
+ US_CPrintLine("Press SPACE to continue.");\r
+ VW_UpdateScreen();\r
+ while (!Keyboard[57]);\r
+ while (Keyboard[57]);\r
+ }\r
+\r
+ while (Keyboard[1]);\r
+\r
+ if (!screenfaded)\r
+ VW_FadeOut();\r
+}\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_LoadGame\r
+//\r
+// Handles user i/o for loading a game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+boolean GE_LoadGame()\r
+{\r
+ boolean GettingFilename=true,rt_code=false;\r
+ int handle;\r
+\r
+ IN_ClearKeysDown();\r
+ memset(ID,0,sizeof(ID));\r
+ memset(VER,0,sizeof(VER));\r
+ VW_FixRefreshBuffer();\r
+ ReadGameList();\r
+ while (GettingFilename)\r
+ {\r
+ DisplayGameList(2,7,3,10);\r
+ US_DrawWindow(5,1,30,3);\r
+ memset(Filename,0,sizeof(Filename));\r
+ US_CPrint("Enter name of game to RESTORE:");\r
+ VW_UpdateScreen();\r
+ if (screenfaded)\r
+ VW_FadeIn();\r
+ if (!US_LineInput((linewidth<<2)-32,20,Filename,"",true,8,0))\r
+ goto EXIT_FUNC;\r
+ strcat(Filename,".SAV");\r
+ GettingFilename = false;\r
+\r
+ if (!Verify(Filename)) // FILE DOESN'T EXIST\r
+ {\r
+ US_CenterWindow(22,3);\r
+ US_CPrintLine(" That file doesn't exist....");\r
+ US_CPrintLine("Press SPACE to try again.");\r
+ VW_UpdateScreen();\r
+\r
+ while (!Keyboard[57]);\r
+ while (Keyboard[57]);\r
+ GettingFilename = true;\r
+ }\r
+ }\r
+\r
+ handle = open(Filename,O_RDWR|O_BINARY);\r
+ if (handle==-1)\r
+ goto EXIT_FUNC;\r
+\r
+ if ((!CA_FarRead(handle,(void far *)&ID,sizeof(ID))) || (!CA_FarRead(handle,(void far *)&VER,sizeof(VER))))\r
+ return(false);\r
+\r
+ if ((strcmp(ID,GAMENAME)) || (strcmp(VER,SAVEVER_DATA)))\r
+ {\r
+ US_CenterWindow(32,4);\r
+ US_CPrintLine("That isn't a "GAMENAME);\r
+ US_CPrintLine(".SAV file.");\r
+ US_CPrintLine("Press SPACE to continue.");\r
+ VW_UpdateScreen();\r
+ while (!Keyboard[57]);\r
+ while (Keyboard[57]);\r
+\r
+ if (!screenfaded)\r
+ VW_FadeOut();\r
+\r
+ return(false);\r
+ }\r
+\r
+ if (!USL_LoadGame(handle))\r
+ Quit("Load game error.");\r
+\r
+ rt_code = true;\r
+\r
+\r
+EXIT_FUNC:;\r
+ if (handle==-1)\r
+ {\r
+ US_CenterWindow(22,3);\r
+ US_CPrintLine("DISK ERROR ** LOAD **");\r
+ US_CPrintLine("Press SPACE to continue.");\r
+ while (!Keyboard[57]);\r
+ while (Keyboard[57]);\r
+ }\r
+ else\r
+ close(handle);\r
+\r
+ if (!screenfaded)\r
+ VW_FadeOut();\r
+\r
+ return(rt_code);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_HardError() - Handles the Abort/Retry/Fail sort of errors passed\r
+// from DOS. Hard coded to ignore if during Load/Save Game.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma warn -par\r
+#pragma warn -rch\r
+int GE_HardError(word errval,int ax,int bp,int si)\r
+{\r
+#define IGNORE 0\r
+#define RETRY 1\r
+#define ABORT 2\r
+extern void ShutdownId(void);\r
+\r
+static char buf[32];\r
+static WindowRec wr;\r
+static boolean oldleavedriveon;\r
+ int di;\r
+ char c,*s,*t;\r
+boolean holdscreenfaded;\r
+\r
+ if (InLoadSaveGame)\r
+ hardresume(IGNORE);\r
+\r
+\r
+ di = _DI;\r
+\r
+ oldleavedriveon = LeaveDriveOn;\r
+ LeaveDriveOn = false;\r
+\r
+ if (ax < 0)\r
+ s = "Device Error";\r
+ else\r
+ {\r
+ if ((di & 0x00ff) == 0)\r
+ s = "Drive ~ is Write Protected";\r
+ else\r
+ s = "Error on Drive ~";\r
+ for (t = buf;*s;s++,t++) // Can't use sprintf()\r
+ if ((*t = *s) == '~')\r
+ *t = (ax & 0x00ff) + 'A';\r
+ *t = '\0';\r
+ s = buf;\r
+ }\r
+\r
+ c = peekb(0x40,0x49); // Get the current screen mode\r
+ if ((c < 4) || (c == 7))\r
+ goto oh_kill_me;\r
+\r
+ // DEBUG - handle screen cleanup\r
+ holdscreenfaded=screenfaded;\r
+\r
+ US_SaveWindow(&wr);\r
+ VW_ClearVideo(0); ////////////// added for exiting\r
+ US_CenterWindow(30,3);\r
+ US_CPrint(s);\r
+ US_CPrint("(R)etry or (A)bort?");\r
+ VW_UpdateScreen();\r
+ if (holdscreenfaded)\r
+ VW_FadeIn();\r
+ IN_ClearKeysDown();\r
+\r
+asm sti // Let the keyboard interrupts come through\r
+\r
+ while (true)\r
+ {\r
+ switch (IN_WaitForASCII())\r
+ {\r
+ case key_Escape:\r
+ case 'a':\r
+ case 'A':\r
+ goto oh_kill_me;\r
+ break;\r
+ case key_Return:\r
+ case key_Space:\r
+ case 'r':\r
+ case 'R':\r
+ if (holdscreenfaded)\r
+ VW_FadeOut();\r
+ US_ClearWindow();\r
+ VW_UpdateScreen();\r
+ US_RestoreWindow(&wr);\r
+ LeaveDriveOn = oldleavedriveon;\r
+ return(RETRY);\r
+ break;\r
+ }\r
+ }\r
+\r
+oh_kill_me:\r
+ abortprogram = s;\r
+ TrashProg("Terminal Error: %s\n",s);\r
+// if (tedlevel)\r
+// fprintf(stderr,"You launched from TED. I suggest that you reboot...\n");\r
+\r
+ return(ABORT);\r
+#undef IGNORE\r
+#undef RETRY\r
+#undef ABORT\r
+}\r
+#pragma warn +par\r
+#pragma warn +rch\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+//\r
+// B O B ROUTINES\r
+//\r
+//\r
+//--------------------------------------------------------------------------\r
+\r
+\r
+\r
+#ifdef BOBLIST\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// UpdateBOBList() - Adds a sprite to an objects BOBlist. The BOB List\r
+// must already be allocated and have an available slot.\r
+//\r
+// RETURNS : true = Success adding Sprite / false = Failure.\r
+//\r
+// NOTE : This also sets the users 'needtoreact' flag to true.\r
+//\r
+boolean UpdateBOBList(objtype *obj,struct Simple_Shape *Shape,shapeclass Class, short priority, spriteflags sprflags)\r
+{\r
+ struct BOB_Shape *CurBOBShape = NULL;\r
+\r
+#pragma warn -pia\r
+\r
+ if (CurBOBShape = obj->nextshape)\r
+ {\r
+ // Treverse down BOBList looking for a sprite with the same class\r
+ // OR an empty shape struct to store the new shape.\r
+\r
+ while ((CurBOBShape->class != Class) && (CurBOBShape->class) && CurBOBShape)\r
+ {\r
+ CurBOBShape = CurBOBShape->nextshape;\r
+ }\r
+\r
+ if (CurBOBShape)\r
+ {\r
+ RF_RemoveSprite(&CurBOBShape->sprite);\r
+ CurBOBShape->shapenum = Shape->shapenum;\r
+ CurBOBShape->x_offset = Shape->x_offset;\r
+ CurBOBShape->y_offset = Shape->y_offset;\r
+ CurBOBShape->priority = priority;\r
+ CurBOBShape->sprflags = sprflags;\r
+ CurBOBShape->class = Class;\r
+ return(true);\r
+ }\r
+ }\r
+ return(false);\r
+\r
+#pragma warn +pia\r
+\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// RemoveBOBShape() - Removes a sprite from a BOBList.\r
+//\r
+// RETURNS : true = Success / false = Failure (shape not found)\r
+//\r
+boolean RemoveBOBShape(objtype *obj, shapeclass Class)\r
+{\r
+ struct BOB_Shape *CurBOBShape = NULL;\r
+\r
+#pragma warn -pia\r
+\r
+ if (CurBOBShape = obj->nextshape)\r
+ {\r
+ while ((CurBOBShape->class != Class) && (!CurBOBShape->class) && CurBOBShape)\r
+ {\r
+ CurBOBShape = CurBOBShape->nextshape;\r
+ }\r
+\r
+ if (CurBOBShape)\r
+ {\r
+ CurBOBShape->class = noshape;\r
+ return(true);\r
+ }\r
+ }\r
+ return(false);\r
+\r
+#pragma warn +pia\r
+\r
+}\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// RemoveBOBList() - Removes an entire BOBList attached to an object.\r
+//\r
+//\r
+void RemoveBOBList(objtype *obj)\r
+{\r
+ struct BOB_Shape *CurBOBShape;\r
+\r
+#pragma warn -pia\r
+\r
+ if (CurBOBShape = obj->nextshape)\r
+ {\r
+ // Treverse down BOBList looking for a sprite with the same class\r
+ // OR an empty shape struct to store the new shape.\r
+\r
+ while (CurBOBShape)\r
+ {\r
+ if (CurBOBShape->class)\r
+ {\r
+ CurBOBShape->class = noshape;\r
+ RF_RemoveSprite (&CurBOBShape->sprite);\r
+ }\r
+ CurBOBShape = CurBOBShape->nextshape;\r
+ }\r
+ }\r
+\r
+#pragma warn +pia\r
+\r
+}\r
+\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// InitBOBList() -- This initializes a BOB list for all the possible shapes\r
+// attached at one time. This is done with an array of\r
+// BOB_Shape structs and links the 'nextshape' pointer to\r
+// to the next element.\r
+//\r
+//\r
+void InitBOBList(objtype *obj, struct BOB_Shape *BOB_Shape, short NumElements)\r
+{\r
+ struct BOB_Shape *CurShape;\r
+ short loop;\r
+\r
+ obj->nextshape = BOB_Shape;\r
+\r
+ for (loop=1;loop<NumElements;loop++)\r
+ {\r
+ CurShape = BOB_Shape++;\r
+ CurShape->nextshape = BOB_Shape;\r
+ }\r
+\r
+ BOB_Shape->nextshape = NULL;\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// RefreshBOBList() -- This routine updates all sprites attached to the\r
+// BOBList and refreshes there position in the sprite\r
+// list.\r
+//\r
+void RefreshBOBList(objtype *obj)\r
+{\r
+ struct BOB_Shape *Shape;\r
+\r
+ Shape = obj->nextshape;\r
+\r
+ while (Shape)\r
+ {\r
+ if (Shape->class)\r
+ RF_PlaceSprite(&Shape->sprite,obj->x+Shape->x_offset,obj->y+Shape->y_offset, Shape->shapenum, spritedraw,Shape->priority,Shape->sprflags);\r
+ Shape = Shape->nextshape;\r
+ }\r
+}\r
+#endif\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// InitBufferedIO()\r
+//--------------------------------------------------------------------------\r
+memptr InitBufferedIO(int handle, BufferedIO *bio)\r
+{\r
+ bio->handle = handle;\r
+ bio->offset = BIO_BUFFER_LEN;\r
+ bio->status = 0;\r
+ MM_GetPtr(&bio->buffer,BIO_BUFFER_LEN);\r
+\r
+ return(bio->buffer);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// FreeBufferedIO()\r
+//--------------------------------------------------------------------------\r
+void FreeBufferedIO(BufferedIO *bio)\r
+{\r
+ if (bio->buffer)\r
+ MM_FreePtr(&bio->buffer);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// bio_readch()\r
+//--------------------------------------------------------------------------\r
+byte bio_readch(BufferedIO *bio)\r
+{\r
+ byte far *buffer;\r
+\r
+ if (bio->offset == BIO_BUFFER_LEN)\r
+ {\r
+ bio->offset = 0;\r
+ bio_fillbuffer(bio);\r
+ }\r
+\r
+ buffer = MK_FP(bio->buffer,bio->offset++);\r
+\r
+ return(*buffer);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// bio_fillbuffer()\r
+//\r
+// BUGS (Not really bugs... More like RULES!)\r
+//\r
+// 1) This code assumes BIO_BUFFER_LEN is no smaller than\r
+// NEAR_BUFFER_LEN!!\r
+//\r
+// 2) BufferedIO.status should be altered by this code to report\r
+// read errors, end of file, etc... If you know how big the file\r
+// is you're reading, determining EOF should be no problem.\r
+//\r
+//--------------------------------------------------------------------------\r
+void bio_fillbuffer(BufferedIO *bio)\r
+{\r
+ #define NEAR_BUFFER_LEN (64)\r
+ byte near_buffer[NEAR_BUFFER_LEN];\r
+ short bio_length,bytes_read,bytes_requested;\r
+\r
+ bytes_read = 0;\r
+ bio_length = BIO_BUFFER_LEN;\r
+ while (bio_length)\r
+ {\r
+ if (bio_length > NEAR_BUFFER_LEN-1)\r
+ bytes_requested = NEAR_BUFFER_LEN;\r
+ else\r
+ bytes_requested = bio_length;\r
+\r
+ read(bio->handle,near_buffer,bytes_requested);\r
+ _fmemcpy(MK_FP(bio->buffer,bytes_read),near_buffer,bytes_requested);\r
+\r
+ bio_length -= bytes_requested;\r
+ bytes_read += bytes_requested;\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SwapLong()\r
+//\r
+void SwapLong(long far *Var)\r
+{\r
+ asm les bx,Var\r
+ asm mov ax,[es:bx]\r
+ asm xchg ah,al\r
+ asm xchg ax,[es:bx+2]\r
+ asm xchg ah,al\r
+ asm mov [es:bx],ax\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SwapWord()\r
+//\r
+void SwapWord(unsigned int far *Var)\r
+{\r
+ asm les bx,Var\r
+ asm mov ax,[es:bx]\r
+ asm xchg ah,al\r
+ asm mov [es:bx],ax\r
+}\r
+\r
+\r
+#if 0\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// LoadShape()\r
+//\r
+int LoadShape(char *Filename,struct Shape *SHP)\r
+{\r
+ #define CHUNK(Name) (*ptr == *Name) && \\r
+ (*(ptr+1) == *(Name+1)) && \\r
+ (*(ptr+2) == *(Name+2)) && \\r
+ (*(ptr+3) == *(Name+3))\r
+\r
+\r
+ int RT_CODE;\r
+// struct ffblk ffblk;\r
+ FILE *fp;\r
+ char CHUNK[5];\r
+ char far *ptr;\r
+ memptr IFFfile = NULL;\r
+ unsigned long FileLen, size, ChunkLen;\r
+ int loop;\r
+\r
+\r
+ RT_CODE = 1;\r
+\r
+ // Decompress to ram and return ptr to data and return len of data in\r
+ // passed variable...\r
+\r
+ if (!(FileLen = BLoad(Filename,&IFFfile)))\r
+ TrashProg("Can't load Compressed Shape - Possibly corrupt file!");\r
+\r
+ // Evaluate the file\r
+ //\r
+ ptr = MK_FP(IFFfile,0);\r
+ if (!CHUNK("FORM"))\r
+ goto EXIT_FUNC;\r
+ ptr += 4;\r
+\r
+ FileLen = *(long far *)ptr;\r
+ SwapLong((long far *)&FileLen);\r
+ ptr += 4;\r
+\r
+ if (!CHUNK("ILBM"))\r
+ goto EXIT_FUNC;\r
+ ptr += 4;\r
+\r
+ FileLen += 4;\r
+ while (FileLen)\r
+ {\r
+ ChunkLen = *(long far *)(ptr+4);\r
+ SwapLong((long far *)&ChunkLen);\r
+ ChunkLen = (ChunkLen+1) & 0xFFFFFFFE;\r
+\r
+ if (CHUNK("BMHD"))\r
+ {\r
+ ptr += 8;\r
+ SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w;\r
+ SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h;\r
+ SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x;\r
+ SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y;\r
+ SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d;\r
+ SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans;\r
+ SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp;\r
+ SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad;\r
+ SwapWord(&SHP->bmHdr.w);\r
+ SwapWord(&SHP->bmHdr.h);\r
+ SwapWord(&SHP->bmHdr.x);\r
+ SwapWord(&SHP->bmHdr.y);\r
+ ptr += ChunkLen;\r
+ }\r
+ else\r
+ if (CHUNK("BODY"))\r
+ {\r
+ ptr += 4;\r
+ size = *((long far *)ptr);\r
+ ptr += 4;\r
+ SwapLong((long far *)&size);\r
+ SHP->BPR = (SHP->bmHdr.w+7) >> 3;\r
+ MM_GetPtr(&SHP->Data,size);\r
+ if (!SHP->Data)\r
+ goto EXIT_FUNC;\r
+ movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size);\r
+ ptr += ChunkLen;\r
+\r
+ break;\r
+ }\r
+ else\r
+ ptr += ChunkLen+8;\r
+\r
+ FileLen -= ChunkLen+8;\r
+ }\r
+\r
+ RT_CODE = 0;\r
+\r
+EXIT_FUNC:;\r
+ if (IFFfile)\r
+ {\r
+// segptr = (memptr)FP_SEG(IFFfile);\r
+ MM_FreePtr(&IFFfile);\r
+ }\r
+\r
+ return (RT_CODE);\r
+}\r
+\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// FreeShape()\r
+//\r
+void FreeShape(struct Shape *shape)\r
+{\r
+ if (shape->Data)\r
+ MM_FreePtr(&shape->Data);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// UnpackEGAShapeToScreen()\r
+//\r
+int UnpackEGAShapeToScreen(struct Shape *SHP,int startx,int starty)\r
+{\r
+ int currenty;\r
+ signed char n, Rep, far *Src, far *Dst[8], loop, Plane;\r
+ unsigned int BPR, Height;\r
+ boolean NotWordAligned;\r
+\r
+ NotWordAligned = SHP->BPR & 1;\r
+ startx>>=3;\r
+ Src = MK_FP(SHP->Data,0);\r
+ currenty = starty;\r
+ Plane = 0;\r
+ Height = SHP->bmHdr.h;\r
+ while (Height--)\r
+ {\r
+ Dst[0] = (MK_FP(0xA000,displayofs));\r
+ Dst[0] += ylookup[currenty];\r
+ Dst[0] += startx;\r
+ for (loop=1; loop<SHP->bmHdr.d; loop++)\r
+ Dst[loop] = Dst[0];\r
+\r
+\r
+ for (Plane=0; Plane<SHP->bmHdr.d; Plane++)\r
+ {\r
+ outport(0x3c4,((1<<Plane)<<8)|2);\r
+\r
+ BPR = ((SHP->BPR+1) >> 1) << 1; // IGNORE WORD ALIGN\r
+ while (BPR)\r
+ {\r
+ if (SHP->bmHdr.comp)\r
+ n = *Src++;\r
+ else\r
+ n = BPR-1;\r
+\r
+ if (n < 0)\r
+ {\r
+ if (n != -128)\r
+ {\r
+ n = (-n)+1;\r
+ BPR -= n;\r
+ Rep = *Src++;\r
+ if ((!BPR) && (NotWordAligned)) // IGNORE WORD ALIGN\r
+ n--;\r
+\r
+ while (n--)\r
+ *Dst[Plane]++ = Rep;\r
+ }\r
+ else\r
+ BPR--;\r
+ }\r
+ else\r
+ {\r
+ n++;\r
+ BPR -= n;\r
+ if ((!BPR) && (NotWordAligned)) // IGNORE WORD ALIGN\r
+ n--;\r
+\r
+ while (n--)\r
+ *Dst[Plane]++ = *Src++;\r
+\r
+ if ((!BPR) && (NotWordAligned)) // IGNORE WORD ALIGN\r
+ Src++;\r
+ }\r
+ }\r
+ }\r
+ currenty++;\r
+ }\r
+\r
+ return(0);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// GetKeyChoice()\r
+//\r
+char GetKeyChoice(char *choices,boolean clear)\r
+{\r
+ extern void DoEvents(void);\r
+\r
+ boolean waiting;\r
+ char *s,*ss;\r
+\r
+ IN_ClearKeysDown();\r
+\r
+ waiting = true;\r
+ while (waiting)\r
+ {\r
+ s = choices;\r
+ while (*s)\r
+ {\r
+ if (Keyboard[*s++])\r
+ {\r
+ waiting=false;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ IN_ClearKeysDown();\r
+\r
+ return(*(--s));\r
+}\r
+\r
+#if 0\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// AnimateObj()\r
+//\r
+boolean AnimateObj(objtype *obj)\r
+{\r
+ boolean Done;\r
+\r
+ Done = false;\r
+\r
+ if (obj->animtype == at_NONE) // Animation finished?\r
+ return(true); // YEP!\r
+\r
+ if (obj->animdelay) // Check animation delay.\r
+ {\r
+ obj->animdelay -= tics;\r
+ if (obj->animdelay < 0)\r
+ obj->animdelay = 0;\r
+ return(false);\r
+ }\r
+\r
+ switch (obj->animtype) // Animate this object!\r
+ {\r
+ case at_ONCE:\r
+ case at_CYCLE:\r
+ switch (obj->animdir)\r
+ {\r
+ case at_FWD:\r
+ if (obj->curframe < obj->maxframe)\r
+ AdvanceAnimFWD(obj);\r
+ else\r
+ if (obj->animtype == at_CYCLE)\r
+ {\r
+ obj->curframe = 0; // RESET CYCLE ANIMATION\r
+ obj->animdelay=1;\r
+ }\r
+ else\r
+ {\r
+ obj->animtype = at_NONE; // TERMINATE ONCE ANIM\r
+ Done = true;\r
+ }\r
+ break;\r
+\r
+ case at_REV:\r
+ if (obj->curframe > 0)\r
+ AdvanceAnimREV(obj);\r
+ else\r
+ if (obj->animtype == at_CYCLE)\r
+ {\r
+ obj->curframe = obj->maxframe; // RESET CYCLE ANIMATION\r
+ obj->animdelay = 1;\r
+ }\r
+ else\r
+ {\r
+ obj->animtype = at_NONE; // TERMINATE ONCE ANIM\r
+ Done = true;\r
+ }\r
+ break; // REV\r
+ }\r
+ break;\r
+\r
+ case at_REBOUND:\r
+ switch (obj->animdir)\r
+ {\r
+ case at_FWD:\r
+ if (obj->curframe < obj->maxframe)\r
+ AdvanceAnimFWD(obj);\r
+ else\r
+ {\r
+ obj->animdir = at_REV;\r
+ obj->animdelay = 1;\r
+ }\r
+ break;\r
+\r
+ case at_REV:\r
+ if (obj->curframe > 0)\r
+ AdvanceAnimREV(obj);\r
+ else\r
+ {\r
+ obj->animdir = at_FWD;\r
+ obj->animdelay = 1;\r
+ Done = true;\r
+ }\r
+ break;\r
+ }\r
+ break; /* REBOUND */\r
+\r
+ case at_WAIT:\r
+ Done = true;\r
+ break;\r
+ }\r
+\r
+ return(Done);\r
+}\r
+\r
+void AdvanceAnimFWD(objtype *obj) // Advances a Frame of ANIM for ONCE,CYCLE, REBOUND\r
+{\r
+ obj->curframe++; // INC frames\r
+ obj->animdelay = obj->maxdelay; // Init Delay Counter.\r
+ obj->needtoreact = true;\r
+}\r
+\r
+\r
+void AdvanceAnimREV(objtype *obj) // Advances a Frame of ANIM for ONCE,CYCLE, REBOUND\r
+{\r
+ obj->curframe--; // DEC frames\r
+ obj->animdelay = obj->maxdelay; // Init Delay Counter.\r
+ obj->needtoreact = true;\r
+}\r
+#endif\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// LoadASArray() - Load an array of audio samples in FAR memory.\r
+//\r
+void LoadASArray(struct Sample *ASArray)\r
+{\r
+ int loop = 0;\r
+\r
+ while (ASArray[loop].filename)\r
+ {\r
+ if (!BLoad(ASArray[loop].filename,(memptr *)&ASArray[loop].data))\r
+ TrashProg("Unable to load sample in LoadASArray()");\r
+ loop++;\r
+ }\r
+}\r
+\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// FreeASArray() - Frees an ASArray from memory that has been loaded with\r
+// LoadASArray()\r
+//\r
+void FreeASArray(struct Sample *ASArray)\r
+{\r
+ unsigned loop = 0;\r
+\r
+ while (ASArray[loop].data)\r
+ {\r
+ MM_SetPurge((memptr *)&ASArray[loop++].data,3);\r
+ MM_FreePtr((memptr *)&ASArray[loop++].data);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_LoadAllDigiSounds() - This is a hook that CA_LoadAllSounds()\r
+// calls to load all of the Digitized sounds for\r
+// Sound Blaster & Sound Source.\r
+//\r
+// NOTE : This stub would do any other necessary routines for DigiSounds\r
+// specific to GAMERS EDGE code. (Keeping seperate from ID's)\r
+//\r
+void GE_LoadAllDigiSounds()\r
+{\r
+ LoadASArray(DigiSounds);\r
+}\r
+\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_FreeAllDigiSounds() -- This is a hook that CA_LoadAllSounds()\r
+// calls to free all digitized sounds for\r
+// which ever hardware and allow for any necessary\r
+// clean up.\r
+//\r
+//\r
+// NOTE : This stub would do any other necessary routines for DigiSounds\r
+// specific to GAMERS EDGE code. (Keeping seperate from ID's)\r
+//\r
+void GE_FreeAllDigiSounds()\r
+{\r
+ FreeASArray(DigiSounds);\r
+}\r
+\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_LoadAllSounds() - Loads ALL sounds needed for detected hardware.\r
+//\r
+void GE_LoadAllSounds()\r
+{\r
+ unsigned i,start;\r
+\r
+ start = STARTPCSOUNDS;\r
+ for (i=0;i<NUMSOUNDS;i++,start++)\r
+ CA_CacheAudioChunk (start);\r
+\r
+ if (AdLibPresent)\r
+ {\r
+ start = STARTADLIBSOUNDS;\r
+ for (i=0;i<NUMSOUNDS;i++,start++)\r
+ CA_CacheAudioChunk (start);\r
+ }\r
+\r
+ if (SoundBlasterPresent)\r
+ LoadASArray(DigiSounds);\r
+}\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_PurgeAllSounds() - Frees all sounds that were loaded.\r
+//\r
+void GE_PurgeAllSounds()\r
+{\r
+ unsigned start,i;\r
+\r
+ start = STARTPCSOUNDS;\r
+ for (i=0;i<NUMSOUNDS;i++,start++)\r
+ if (audiosegs[start])\r
+ MM_SetPurge (&(memptr)audiosegs[start],3); // make purgable\r
+\r
+\r
+ if (AdLibPresent)\r
+ {\r
+ start = STARTADLIBSOUNDS;\r
+ for (i=0;i<NUMSOUNDS;i++,start++)\r
+ if (audiosegs[start])\r
+ MM_SetPurge (&(memptr)audiosegs[start],3); // make purgable\r
+ }\r
+\r
+ if (SoundBlasterPresent)\r
+ GE_FreeAllDigiSounds();\r
+}\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// PlaySample() -- Plays a DIGITIZED sample using SoundBlaster OR SoundSource\r
+//\r
+// PARAMETERS : Sample Number (Corresponding to ASArray "DigiSounds[]".\r
+//\r
+void PlaySample(unsigned SampleNum)\r
+{\r
+\r
+ if (!DigiSounds[SampleNum].data)\r
+ TrashProg("PlaySample - Trying to play an unloaded digi sound!");\r
+\r
+\r
+ switch (SoundMode) // external variable in ID_SD for sound mode..\r
+ {\r
+ case sdm_SoundBlaster:\r
+ case sdm_SoundBlasterAdLib:\r
+ SDL_SBPlaySample(MK_FP(DigiSounds[SampleNum].data,0));\r
+ break;\r
+\r
+ default:\r
+ TrashProg("PlaySample() - incorrect SoundMode for PlaySample()");\r
+ break;\r
+ }\r
+}\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// SelectDigiAudio() -- This routine intergrates multi sound hardware with\r
+// id's routines.\r
+//\r
+void SelectDigiAudio(AudioDeviceType Device)\r
+{\r
+ switch (Device)\r
+ {\r
+ case ged_SoundSource:\r
+ case ged_SoundBlaster:\r
+ break;\r
+ }\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// DisplayGameList()\r
+//\r
+void DisplayGameList(short winx, short winy, short list_width, short list_height)\r
+{\r
+ #define SPACES 2\r
+\r
+ short width,col,row,orgcol,games_printed=0,h;\r
+\r
+ // Possibly shrink window.\r
+ //\r
+ h = (NumGames / list_width) + ((NumGames % list_width) > 0);\r
+ if (h < list_height)\r
+ list_height = h;\r
+\r
+ // Open window and print header...\r
+ //\r
+ US_DrawWindow(winx,winy,list_width*(8+SPACES*2),list_height+3);\r
+ US_CPrintLine("LIST OF SAVED GAMES");\r
+ US_Print("\n");\r
+\r
+ col = orgcol = PrintX;\r
+ row = PrintY;\r
+\r
+ // Display as many 'save game' files as can fit in the window.\r
+ //\r
+ width = list_width;\r
+ while ((games_printed<NumGames) && (list_height))\r
+ {\r
+ // Print filename and padding spaces.\r
+ //\r
+ US_Printxy(col+(SPACES*8),row,GameListNames[games_printed]);\r
+ col += 8*((SPACES*2)+8);\r
+\r
+ // Check for end-of-line or end-of-window.\r
+ //\r
+ width--;\r
+ if (!width)\r
+ {\r
+ col = orgcol;\r
+ row += 8;\r
+ width = list_width;\r
+ list_height--;\r
+ US_Print("\n");\r
+ }\r
+\r
+ games_printed++;\r
+ }\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// ReadGameList()\r
+//\r
+void ReadGameList()\r
+{\r
+ struct ffblk ffblk;\r
+ short done,len;\r
+\r
+ NumGames = -1;\r
+ done = findfirst("*.sav",&ffblk,0);\r
+\r
+ while (!done)\r
+ {\r
+ if (NumGames == MAX_GAMELIST_NAMES)\r
+ memcpy(GameListNames,GameListNames[1],MAX_GAMELIST_NAMES*sizeof(GameListNames[0]));\r
+ else\r
+ NumGames++;\r
+\r
+ fnsplit(ffblk.ff_name,NULL,NULL,GameListNames[NumGames],NULL);\r
+\r
+ done=findnext(&ffblk);\r
+ }\r
+\r
+ NumGames++;\r
+}\r
+\r
+#if 0\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// CenterObj()\r
+//\r
+void CenterObj(objtype *obj, unsigned x, unsigned y)\r
+{\r
+ spritetabletype far *sprite;\r
+ unsigned width, height;\r
+\r
+ sprite=&spritetable[obj->baseshape+obj->curframe-STARTSPRITES];\r
+\r
+ width = (sprite->xh-sprite->xl+(1<<G_P_SHIFT)) >> 1;\r
+ if (obj->sprflags&sf_vertflip)\r
+ {\r
+ height = (sprite->yl-(sprite->height<<G_P_SHIFT) + sprite->yh+(1<<G_P_SHIFT)) >> 1;\r
+ }\r
+ else\r
+ height = (sprite->yh-sprite->yl+(1<<G_P_SHIFT)) >> 1;\r
+\r
+ obj->x = x-width;\r
+ obj->y = y-height;\r
+}\r
+#endif\r
+\r
+#if 0\r
+//-------------------------------------------------------------------------\r
+// cacheout()\r
+//-------------------------------------------------------------------------\r
+void cacheout(short s,short e)\r
+{\r
+ short i;\r
+\r
+ for(i=(s);i<=(e);i++)\r
+ {\r
+ grneeded[i]&=~ca_levelbit;\r
+ if (grsegs[i])\r
+ MM_SetPurge(&grsegs[i],3);\r
+ }\r
+\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// cachein()\r
+//-------------------------------------------------------------------------\r
+void cachein(short s,short e)\r
+{\r
+ short i;\r
+\r
+ for(i=(s);i<=(e);i++)\r
+ {\r
+ CA_MarkGrChunk(i);\r
+ if (grsegs[i])\r
+ MM_SetPurge(&grsegs[i],0);\r
+ }\r
+}\r
+#endif\r
+\r
+#if 0\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// SetUpdateBlock()\r
+//\r
+void SetUpdateBlock(unsigned x,unsigned y,unsigned width,unsigned height,char refresh)\r
+{\r
+ eraseblocktype *erase;\r
+\r
+#if 0 //SP unsure if this is needed\r
+ x = (x+((MAPBORDER+MAPXLEFTOFFSET)<<4))>>3;\r
+ y += ((MAPBORDER+MAPYTOPOFFSET)<<4);\r
+#else\r
+ x = (x+(MAPBORDER<<4))>>3;\r
+ y += (MAPBORDER<<4);\r
+#endif\r
+ width >>= 3;\r
+\r
+ if (refresh & 1)\r
+ {\r
+ erase = eraselistptr[0]++;\r
+ erase->screenx=x;\r
+ erase->screeny=y;\r
+ erase->width=width;\r
+ erase->height=height;\r
+ }\r
+\r
+ if (refresh & 2)\r
+ {\r
+ erase = eraselistptr[1]++;\r
+ erase->screenx=x;\r
+ erase->screeny=y;\r
+ erase->width=width;\r
+ erase->height=height;\r
+ }\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// ObjHeight\r
+//\r
+unsigned ObjHeight(objtype *obj)\r
+{\r
+ spritetabletype far *sprite;\r
+\r
+ sprite=&spritetable[obj->baseshape+obj->curframe-STARTSPRITES];\r
+\r
+ if (obj->sprflags&sf_vertflip)\r
+ {\r
+ return((sprite->yl-(sprite->height<<G_P_SHIFT) + sprite->yh+(1<<G_P_SHIFT)) >> 1);\r
+ }\r
+ else\r
+ return((sprite->yh-sprite->yl+(1<<G_P_SHIFT)) >> 1);\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// ObjWidth\r
+//\r
+unsigned ObjWidth(objtype *obj)\r
+{\r
+ spritetabletype far *sprite;\r
+\r
+ sprite=&spritetable[obj->baseshape+obj->curframe-STARTSPRITES];\r
+\r
+ return((sprite->xh-sprite->xl+(1<<G_P_SHIFT)) >> 1);\r
+}\r
+#endif\r
+\r
+#if 0\r
+//--------------------------------------------------------------------------\r
+// visible_on()\r
+//--------------------------------------------------------------------------\r
+boolean visible_on(objtype *obj)\r
+{\r
+ if (!(obj->flags & of_visible))\r
+ {\r
+ obj->needtoreact=true;\r
+ obj->flags |= of_visible;\r
+ return(true);\r
+ }\r
+\r
+ return(false);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// visible_off()\r
+//--------------------------------------------------------------------------\r
+boolean visible_off(objtype *obj)\r
+{\r
+ if (obj->flags & of_visible)\r
+ {\r
+ obj->needtoreact=true;\r
+ obj->flags &= ~of_visible;\r
+ return(true);\r
+ }\r
+\r
+ return(false);\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= FizzleFade\r
+=\r
+===================\r
+*/\r
+\r
+#define PIXPERFRAME 10000\r
+\r
+void FizzleFade (unsigned source, unsigned dest,\r
+ unsigned width,unsigned height, boolean abortable)\r
+{\r
+ unsigned drawofs,pagedelta;\r
+ unsigned char maskb[8] = {1,2,4,8,16,32,64,128};\r
+ unsigned x,y,p,frame;\r
+ long rndval;\r
+ ScanCode lastLastScan=LastScan=0;\r
+\r
+ width--;\r
+ height--;\r
+\r
+ pagedelta = dest-source;\r
+// VW_SetScreen (dest,0);\r
+ rndval = 1;\r
+ y = 0;\r
+\r
+asm mov es,[screenseg]\r
+asm mov dx,SC_INDEX\r
+asm mov al,SC_MAPMASK\r
+asm out dx,al\r
+\r
+ TimeCount=frame=0;\r
+ do // while (1)\r
+ {\r
+ if ((abortable) || (Flags & FL_QUICK))\r
+ {\r
+ IN_ReadControl(0,&control);\r
+ if (control.button0 || control.button1 || (lastLastScan != LastScan)\r
+ || Keyboard[sc_Escape] || (Flags & FL_QUICK))\r
+ {\r
+ VW_ScreenToScreen (source,dest,(width+1)/8,height+1);\r
+ goto exitfunc;\r
+ }\r
+ }\r
+\r
+ for (p=0;p<PIXPERFRAME;p++)\r
+ {\r
+ //\r
+ // seperate random value into x/y pair\r
+ //\r
+ asm mov ax,[WORD PTR rndval]\r
+ asm mov dx,[WORD PTR rndval+2]\r
+ asm mov bx,ax\r
+ asm dec bl\r
+ asm mov [BYTE PTR y],bl // low 8 bits - 1 = y xoordinate\r
+ asm mov bx,ax\r
+ asm mov cx,dx\r
+ asm shr cx,1\r
+ asm rcr bx,1\r
+ asm shr bx,1\r
+ asm shr bx,1\r
+ asm shr bx,1\r
+ asm shr bx,1\r
+ asm shr bx,1\r
+ asm shr bx,1\r
+ asm shr bx,1\r
+ asm mov [x],bx // next 9 bits = x xoordinate\r
+ //\r
+ // advance to next random element\r
+ //\r
+ asm shr dx,1\r
+ asm rcr ax,1\r
+ asm jnc noxor\r
+ asm xor dx,0x0001\r
+ asm xor ax,0x2000\r
+noxor:\r
+ asm mov [WORD PTR rndval],ax\r
+ asm mov [WORD PTR rndval+2],dx\r
+\r
+ if (x>width || y>height)\r
+ continue;\r
+ drawofs = source+ylookup[y];\r
+\r
+ asm mov cx,[x]\r
+ asm mov si,cx\r
+ asm and si,7\r
+ asm mov dx,GC_INDEX\r
+ asm mov al,GC_BITMASK\r
+ asm mov ah,BYTE PTR [maskb+si]\r
+ asm out dx,ax\r
+\r
+ asm mov si,[drawofs]\r
+ asm shr cx,1\r
+ asm shr cx,1\r
+ asm shr cx,1\r
+ asm add si,cx\r
+ asm mov di,si\r
+ asm add di,[pagedelta]\r
+\r
+ asm mov dx,GC_INDEX\r
+ asm mov al,GC_READMAP // leave GC_INDEX set to READMAP\r
+ asm out dx,al\r
+\r
+ asm mov dx,SC_INDEX+1\r
+ asm mov al,1\r
+ asm out dx,al\r
+ asm mov dx,GC_INDEX+1\r
+ asm mov al,0\r
+ asm out dx,al\r
+\r
+ asm mov bl,[es:si]\r
+ asm xchg [es:di],bl\r
+\r
+ asm mov dx,SC_INDEX+1\r
+ asm mov al,2\r
+ asm out dx,al\r
+ asm mov dx,GC_INDEX+1\r
+ asm mov al,1\r
+ asm out dx,al\r
+\r
+ asm mov bl,[es:si]\r
+ asm xchg [es:di],bl\r
+\r
+ asm mov dx,SC_INDEX+1\r
+ asm mov al,4\r
+ asm out dx,al\r
+ asm mov dx,GC_INDEX+1\r
+ asm mov al,2\r
+ asm out dx,al\r
+\r
+ asm mov bl,[es:si]\r
+ asm xchg [es:di],bl\r
+\r
+ asm mov dx,SC_INDEX+1\r
+ asm mov al,8\r
+ asm out dx,al\r
+ asm mov dx,GC_INDEX+1\r
+ asm mov al,3\r
+ asm out dx,al\r
+\r
+ asm mov bl,[es:si]\r
+ asm xchg [es:di],bl\r
+\r
+ if (rndval == 1) // entire sequence has been completed\r
+ goto exitfunc;\r
+ }\r
+// frame++;\r
+// while (TimeCount<frame); // don't go too fast\r
+\r
+ } while (1);\r
+\r
+exitfunc:;\r
+ EGABITMASK(255);\r
+ EGAMAPMASK(15);\r
+ return;\r
+}\r
+\r
+#if 0\r
+//-------------------------------------------------------------------------\r
+// mprintf()\r
+//-------------------------------------------------------------------------\r
+void mprintf(char *msg, ...)\r
+{\r
+ static char x=0;\r
+ static char y=0;\r
+ static char far *video = MK_FP(0xb000,0x0000);\r
+ char buffer[100],*ptr;\r
+\r
+ va_list(ap);\r
+\r
+ va_start(ap,msg);\r
+\r
+ vsprintf(buffer,msg,ap);\r
+\r
+ ptr = buffer;\r
+ while (*ptr)\r
+ {\r
+ switch (*ptr)\r
+ {\r
+ case '\n':\r
+ if (y >= 23)\r
+ {\r
+ video -= (x<<1);\r
+ _fmemcpy(MK_FP(0xb000,0x0000),MK_FP(0xb000,0x00a0),3840);\r
+ }\r
+ else\r
+ {\r
+ y++;\r
+ video += ((80-x)<<1);\r
+ }\r
+ x=0;\r
+ break;\r
+\r
+ default:\r
+ *video = *ptr;\r
+ video[1] = 15;\r
+ video += 2;\r
+ x++;\r
+ break;\r
+ }\r
+ ptr++;\r
+ }\r
+\r
+ va_end(ap);\r
+}\r
+#endif\r
+\r
+#if 0\r
+\r
+//--------------------------------------------------------------------------\r
+// FULL SCREEN REFRESH/ANIM MANAGERS\r
+//--------------------------------------------------------------------------\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// InitLatchRefresh() -- Loads an ILBM (JAMPAK'd) into the Master Latch\r
+// to be used as the background refresh screen...\r
+//\r
+void InitLatchRefresh(char *filename)\r
+{\r
+ struct Shape RefreshShp;\r
+ short yofs;\r
+\r
+ VW_ClearVideo(0);\r
+\r
+ if (LoadShape(filename,&RefreshShp))\r
+ TrashProg("Can't load %s",filename);\r
+\r
+ VW_SetLineWidth(RefreshShp.BPR);\r
+\r
+ yofs = masterswap/SCREENWIDTH;\r
+ MoveGfxDst(0,yofs); // Handle title screen\r
+ UnpackEGAShapeToScreen(&RefreshShp,0,0);\r
+ FreeShape(&RefreshShp);\r
+\r
+ MoveScreen(0,0);\r
+ VW_InitDoubleBuffer();\r
+\r
+ RF_NewPosition(0,0);\r
+\r
+ RF_Refresh();\r
+\r
+ SetUpdateBlock(0,0,RefreshShp.bmHdr.w,RefreshShp.bmHdr.h,3);\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// InitFullScreenAnim() -- Initialize ALL necessary functions for full screen\r
+// animation types.\r
+//\r
+// filename - filename for background lbm.\r
+// SpawnAll - spawn function to call to spawn all inital objects..\r
+//\r
+void InitFullScreenAnim(char *filename, void (*SpawnAll)())\r
+{\r
+ unsigned old_flags;\r
+\r
+ old_flags = GE_SystemFlags.flags;\r
+ GE_SystemFlags.flags &= ~(GEsf_Tiles | GEsf_Panning | GEsf_RefreshVec);\r
+\r
+ CA_ClearMarks();\r
+\r
+ RFL_InitSpriteList();\r
+ InitObjArray();\r
+\r
+ if (SpawnAll)\r
+ SpawnAll();\r
+\r
+ CA_CacheMarks(NULL);\r
+\r
+ CalcInactivate();\r
+ CalcSprites();\r
+\r
+ InitLatchRefresh(filename);\r
+\r
+ RF_ForceRefresh();\r
+ RF_ForceRefresh();\r
+\r
+ GE_SystemFlags.flags = old_flags;\r
+}\r
+\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// DoFullScreenAnim() -- a General "Do-Every-Thing" function that\r
+// displays a full screen animation...\r
+//\r
+// filename - Filename of background lbm\r
+// SpawnAll - Function to call to spawn all inital objects (REQUIRED)\r
+// CheckKey - Function to call every cycle - The function called must\r
+// return a value of greater than zero (0) to terminate the\r
+// animation cycle. (REQUIRED)\r
+// CleanUp - Function to call upon exiting the animation. (OPTIONAL)\r
+//\r
+void DoFullScreenAnim(char *filename, void (*SpawnAll)(), short (*CheckKey)(),void (*CleanUp)())\r
+{\r
+ unsigned old_flags;\r
+ boolean ExitAnim = false;\r
+\r
+ // Save off the current system flags....\r
+\r
+ old_flags = GE_SystemFlags.flags;\r
+ GE_SystemFlags.flags &= ~(GEsf_Tiles | GEsf_Panning);\r
+\r
+// randomize();\r
+\r
+ // Init refresh latch screen, initalize all object, and setup video mode.\r
+\r
+ InitFullScreenAnim(filename,SpawnAll);\r
+\r
+ VW_FadeIn();\r
+\r
+ while (!ExitAnim)\r
+ {\r
+ CalcInactivate();\r
+ CalcSprites();\r
+ RF_Refresh();\r
+\r
+ ExitAnim = (boolean)CheckKey();\r
+ }\r
+\r
+// RemoveBOBList(player);\r
+// CalcInactivate();\r
+\r
+ if (CleanUp)\r
+ CleanUp();\r
+\r
+ // Restore old system flags...\r
+\r
+ GE_SystemFlags.flags = old_flags;\r
+}\r
+\r
+#endif\r
+\r
+//--------------------------------------------------------------------------\r
+// FindFile()\r
+//--------------------------------------------------------------------------\r
+boolean FindFile(char *filename,char *disktext,char disknum)\r
+{\r
+ extern boolean splitscreen;\r
+\r
+ char command[100];\r
+ char choices[]={sc_Escape,sc_Space,0},drive[2];\r
+ boolean fadeitout=false,rt_code=2;\r
+\r
+ if (!disktext)\r
+ disktext = GAMENAME;\r
+ drive[0] = getdisk() + 'A';\r
+ drive[1] = 0;\r
+ while (rt_code == 2)\r
+ {\r
+ if (Verify(filename))\r
+ rt_code = true;\r
+ else\r
+ {\r
+ if (ge_textmode)\r
+ {\r
+ clrscr();\r
+ gotoxy(1,1);\r
+ printf("\nInsert %s disk %d into drive %s.\n",disktext,disknum,drive);\r
+ printf("Press SPACE to continue, ESC to abort.\n");\r
+ }\r
+ else\r
+ {\r
+ if (splitscreen)\r
+ {\r
+ bufferofs = displayofs = screenloc[screenpage];\r
+ CenterWindow(38,5);\r
+ }\r
+ else\r
+ {\r
+ bufferofs = displayofs = 0;\r
+ US_CenterWindow(38,5);\r
+ }\r
+\r
+ strcpy(command,"\nInsert ");\r
+ strcat(command,disktext);\r
+ strcat(command," disk");\r
+ if (disknum != -1)\r
+ {\r
+ strcat(command," ");\r
+ itoa(disknum,command+strlen(command),10);\r
+ }\r
+ strcat(command," into drive ");\r
+ strcat(command,drive);\r
+ strcat(command,".");\r
+ US_CPrint(command);\r
+ US_CPrint("Press SPACE to continue, ESC to abort.");\r
+ }\r
+\r
+ sound(300);\r
+ VW_WaitVBL(20);\r
+ nosound();\r
+\r
+ if (!ge_textmode)\r
+ {\r
+ if (screenfaded)\r
+ {\r
+ VW_FadeIn();\r
+ fadeitout=true;\r
+ }\r
+ }\r
+ if (GetKeyChoice(choices,true) == sc_Escape)\r
+ rt_code = false;\r
+ }\r
+ }\r
+\r
+ if (!ge_textmode)\r
+ if (fadeitout)\r
+ VW_FadeOut();\r
+\r
+ return(rt_code);\r
+}\r
+\r
+#if 0\r
+//--------------------------------------------------------------------------\r
+// CacheAll()\r
+//--------------------------------------------------------------------------\r
+void CacheAV(char *title)\r
+{\r
+ if (Verify("EGAGRAPH."EXT))\r
+ {\r
+ CA_CacheMarks(title);\r
+ if (!FindFile("EGAGRAPH."EXT,AUDIO_DISK))\r
+ TrashProg("Can't find graphics files.");\r
+\r
+// cache in audio\r
+\r
+ current_disk = AUDIO_DISK;\r
+ }\r
+ else\r
+ {\r
+\r
+// cache in audio\r
+\r
+ if (!FindFile("EGAGRAPH."EXT,VIDEO_DISK))\r
+ TrashProg("Can't find audio files.");\r
+ CA_CacheMarks(title);\r
+\r
+ current_disk = VIDEO_DISK;\r
+ }\r
+}\r
+#endif\r
+\r
+#ifdef TEXT_PRESENTER\r
+//--------------------------------------------------------------------------\r
+//\r
+// TEXT PRESENTER CODE\r
+//\r
+//--------------------------------------------------------------------------\r
+\r
+typedef enum pi_stype {pis_pic2x,pis_latch_pic} pi_stype;\r
+\r
+\r
+typedef struct { // 4 bytes\r
+ unsigned shapenum;\r
+ pi_stype shape_type;\r
+} pi_shape_info;\r
+\r
+#define pia_active 0x01\r
+\r
+typedef struct { // 10 bytes\r
+ char baseshape;\r
+ char frame;\r
+ char maxframes;\r
+ short delay;\r
+ short maxdelay;\r
+ short x,y;\r
+} pi_anim_info;\r
+\r
+ #define PI_CASE_SENSITIVE\r
+\r
+ #define PI_RETURN_CHAR '\n'\r
+ #define PI_CONTROL_CHAR '^'\r
+\r
+ #define PI_CNVT_CODE(c1,c2) ((c1)|(c2<<8))\r
+\r
+// shape table provides a way for the presenter to access and\r
+// display any shape.\r
+//\r
+pi_shape_info far pi_shape_table[] = {\r
+\r
+ {BOLTOBJPIC,pis_pic2x}, // 0\r
+ {NUKEOBJPIC,pis_pic2x},\r
+ {SKELETON_1PIC,pis_pic2x},\r
+ {EYE_WALK1PIC,pis_pic2x},\r
+ {ZOMB_WALK1PIC,pis_pic2x},\r
+\r
+ {TIMEOBJ1PIC,pis_pic2x}, // 5\r
+ {POTIONOBJPIC,pis_pic2x},\r
+ {RKEYOBJPIC,pis_pic2x},\r
+ {YKEYOBJPIC,pis_pic2x},\r
+ {GKEYOBJPIC,pis_pic2x},\r
+\r
+ {BKEYOBJPIC,pis_pic2x}, // 10\r
+ {RGEM1PIC,pis_pic2x},\r
+ {GGEM1PIC,pis_pic2x},\r
+ {BGEM1PIC,pis_pic2x},\r
+ {YGEM1PIC,pis_pic2x},\r
+\r
+ {PGEM1PIC,pis_pic2x}, // 15\r
+ {CHESTOBJPIC,pis_pic2x},\r
+ {PSHOT1PIC,pis_pic2x},\r
+ {RED_DEMON1PIC,pis_pic2x},\r
+ {MAGE1PIC,pis_pic2x},\r
+\r
+ {BAT1PIC,pis_pic2x}, // 20\r
+ {GREL1PIC,pis_pic2x},\r
+ {GODESS_WALK1PIC,pis_pic2x},\r
+ {ANT_WALK1PIC,pis_pic2x},\r
+ {FATDEMON_WALK1PIC,pis_pic2x},\r
+\r
+ {SUCCUBUS_WALK1PIC,pis_pic2x}, //25\r
+ {TREE_WALK1PIC,pis_pic2x},\r
+ {DRAGON_WALK1PIC,pis_pic2x},\r
+ {BUNNY_LEFT1PIC,pis_pic2x},\r
+\r
+};\r
+\r
+// anim table holds info about each different animation.\r
+//\r
+pi_anim_info far pi_anim_table[] = {{0,0,3,0,10}, // 0 BOLT\r
+ {1,0,3,0,10}, // NUKE\r
+ {2,0,4,0,10}, // SKELETON\r
+ {3,0,3,0,10}, // EYE\r
+ {4,0,3,0,10}, // ZOMBIE\r
+\r
+ {5,0,2,0,10}, // 5 FREEZE TIME\r
+ {11,0,2,0,10}, // RED GEM\r
+ {12,0,2,0,10}, // GREEN GEM\r
+ {13,0,2,0,10}, // BLUE GEM\r
+ {14,0,2,0,10}, // YELLOW GEM\r
+\r
+ {15,0,2,0,10}, // 10 PURPLE GEM\r
+ {17,0,2,0,10}, // PLAYER'S SHOT\r
+ {18,0,3,0,10}, // RED DEMON\r
+ {19,0,2,0,10}, // MAGE\r
+ {20,0,4,0,10}, // BAT\r
+\r
+ {21,0,2,0,10}, // 15 GRELMINAR\r
+ {22,0,3,0,10}, // GODESS\r
+ {23,0,3,0,10}, // ANT\r
+ {24,0,4,0,10}, // FAT DEMON\r
+ {25,0,4,0,10}, // SUCCUBUS\r
+\r
+ {26,0,2,0,10}, // 20 TREE\r
+ {27,0,3,0,10}, // DRAGON\r
+ {28,0,2,0,10}, // BUNNY\r
+};\r
+\r
+// anim list is created on the fly from the anim table...\r
+// this allows a single animation to be display in more than\r
+// one place...\r
+//\r
+pi_anim_info far pi_anim_list[PI_MAX_ANIMS];\r
+boolean pi_recursing=false;\r
+\r
+//--------------------------------------------------------------------------\r
+// Presenter() - DANGEROUS DAVE "Presenter()" is more up-to-date than this.\r
+//\r
+//\r
+// Active control codes:\r
+//\r
+// ^CE - center text between 'left margin' and 'right margin'\r
+// ^FCn - set font color\r
+// ^LMnnn - set left margin (if 'nnn' == "fff" uses current x)\r
+// ^RMnnn - set right margin (if 'nnn' == "fff" uses current x)\r
+// ^EP - end of page (waits for up/down arrow)\r
+// ^PXnnn - move x to coordinate 'n'\r
+// ^PYnnn - move y to coordinate 'n'\r
+// ^XX - exit presenter\r
+// ^LJ - left justify --\ ^RJ doesn't handle imbedded control\r
+// ^RJ - right justify --/ codes properly. Use with caution.\r
+// ^BGn - set background color\r
+// ^ANnn - define animation\r
+// ^SHnnn - display shape 'n' at current x,y\r
+//\r
+//\r
+// Future control codes:\r
+//\r
+// ^OBnnn - activate object 'n'\r
+// ^FL - flush to edges (whatever it's called)\r
+//\r
+//\r
+// Other info:\r
+//\r
+// All 'n' values are hex numbers (0 - f), case insensitive.\r
+// The number of N's listed is the number of digits REQUIRED by that control\r
+// code. (IE: ^LMnnn MUST have 3 values! --> 003, 1a2, 01f, etc...)\r
+//\r
+// If a line consists only of control codes, the cursor is NOT advanced\r
+// to the next line (the ending <CR><LF> is skipped). If just ONE non-control\r
+// code is added, the number "8" for example, then the "8" is displayed\r
+// and the cursor is advanced to the next line.\r
+//\r
+// ^CE must be on the same line as the text it should center!\r
+//\r
+//--------------------------------------------------------------------------\r
+void Presenter(PresenterInfo *pi)\r
+{\r
+#ifdef AMIGA\r
+ XBitMap **font = pi->font;\r
+\r
+ #define ch_width(ch) font[ch]->pad\r
+ char font_height = font[0]->Rows;\r
+#else\r
+ fontstruct _seg *font = (fontstruct _seg *)grsegs[STARTFONT];\r
+\r
+ #define MAX_PB 150\r
+ #define ch_width(ch) font->width[ch]\r
+ char font_height = font->height-1; // "-1" squeezes font vertically\r
+ char pb[MAX_PB];\r
+ short length;\r
+#endif\r
+\r
+ enum {jm_left,jm_right,jm_flush};\r
+ char justify_mode = jm_left;\r
+ boolean centering=false;\r
+\r
+ short bgcolor = pi->bgcolor;\r
+ short xl=pi->xl,yl=pi->yl,xh=pi->xh,yh=pi->yh;\r
+ short cur_x = xl, cur_y = yl;\r
+ char far *first_ch = pi->script[0];\r
+\r
+ char far *scan_ch,temp;\r
+ short scan_x,PageNum=0,numanims=0;\r
+ boolean up_released=true,dn_released=true;\r
+ boolean presenting=true,start_of_line=true;\r
+\r
+// if set background is first thing in file, make sure initial screen\r
+// clear uses this color.\r
+//\r
+ if (!_fstrncmp(first_ch,"^BG",3))\r
+ bgcolor = PI_VALUE(first_ch+3,1);\r
+\r
+ if (!pi_recursing)\r
+ {\r
+ PurgeAllGfx();\r
+ CachePage(first_ch);\r
+ }\r
+ VW_Bar(xl,yl,xh-xl+1,yh-yl+1,bgcolor);\r
+\r
+ while (presenting)\r
+ {\r
+//\r
+// HANDLE WORD-WRAPPING TEXT\r
+//\r
+ if (*first_ch != PI_CONTROL_CHAR)\r
+ {\r
+ start_of_line = false;\r
+\r
+ // Parse script until one of the following:\r
+ //\r
+ // 1) text extends beyond right margin\r
+ // 2) NULL termination is reached\r
+ // 3) PI_RETURN_CHAR is reached\r
+ // 4) PI_CONTROL_CHAR is reached\r
+ //\r
+ scan_x = cur_x;\r
+ scan_ch = first_ch;\r
+ while ((scan_x+ch_width(*scan_ch) <= xh) && (*scan_ch) &&\r
+ (*scan_ch != PI_RETURN_CHAR) && (*scan_ch != PI_CONTROL_CHAR))\r
+ scan_x += ch_width(*scan_ch++);\r
+\r
+ // if 'text extends beyond right margin', scan backwards for\r
+ // a SPACE\r
+ //\r
+ if (scan_x+ch_width(*scan_ch) > xh)\r
+ {\r
+ short last_x = scan_x;\r
+ char far *last_ch = scan_ch;\r
+\r
+ while ((scan_ch != first_ch) && (*scan_ch != ' ') && (*scan_ch != PI_RETURN_CHAR))\r
+ scan_x -= ch_width(*scan_ch--);\r
+\r
+ if (scan_ch == first_ch)\r
+ scan_ch = last_ch, scan_x = last_x;\r
+ }\r
+\r
+ // print current line\r
+ //\r
+#ifdef AMIGA\r
+ while (first_ch < scan_ch)\r
+ {\r
+ qBlit(font[*first_ch++],pi->dst,cur_x,cur_y);\r
+// qBlit(font[*first_ch],pi->dst,cur_x,cur_y);\r
+// cur_x += ch_width(*first_ch++);\r
+ }\r
+#else\r
+ temp = *scan_ch;\r
+ *scan_ch = 0;\r
+\r
+ if ((justify_mode == jm_right)&&(!centering))\r
+ {\r
+ unsigned width,height;\r
+\r
+ VWL_MeasureString(first_ch,&width,&height,font);\r
+ cur_x = xh-width;\r
+ if (cur_x < xl)\r
+ cur_x = xl;\r
+ }\r
+\r
+ px = cur_x;\r
+ py = cur_y;\r
+\r
+ length = scan_ch-first_ch+1; // USL_DrawString only works with\r
+ if (length > MAX_PB)\r
+ Quit("Presenter(): Print buffer string too long!");\r
+ _fmemcpy(pb,first_ch,length); // near pointers...\r
+\r
+ if (*first_ch != '\n')\r
+ USL_DrawString(pb);\r
+\r
+ *scan_ch = temp;\r
+ first_ch = scan_ch;\r
+#endif\r
+ cur_x = scan_x;\r
+ centering = false;\r
+\r
+ // skip SPACES / RETURNS at end of line\r
+ //\r
+ if ((*first_ch==' ') || (*first_ch==PI_RETURN_CHAR))\r
+ first_ch++;\r
+\r
+ // PI_CONTROL_CHARs don't advance to next character line\r
+ //\r
+ if (*scan_ch != PI_CONTROL_CHAR)\r
+ {\r
+ cur_x = xl;\r
+ cur_y += font_height;\r
+ }\r
+ }\r
+ else\r
+//\r
+// HANDLE CONTROL CODES\r
+//\r
+ {\r
+ PresenterInfo endmsg;\r
+ pi_anim_info far *anim;\r
+ pi_shape_info far *shape_info;\r
+ unsigned shapenum;\r
+ short length;\r
+ char far *s;\r
+\r
+ if (first_ch[-1] == '\n')\r
+ start_of_line = true;\r
+\r
+ first_ch++;\r
+#ifndef PI_CASE_SENSITIVE\r
+ *first_ch=toupper(*first_ch);\r
+ *(first_ch+1)=toupper(*(first_ch+1));\r
+#endif\r
+ switch (*((unsigned far *)first_ch)++)\r
+ {\r
+ // CENTER TEXT ------------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('C','E'):\r
+ length = 0;\r
+ s = first_ch;\r
+ while (*s && (*s != PI_RETURN_CHAR))\r
+ {\r
+ switch (*s)\r
+ {\r
+ case PI_CONTROL_CHAR:\r
+ s++;\r
+ switch (*((unsigned *)s)++)\r
+ {\r
+#ifndef AMIGA\r
+ case PI_CNVT_CODE('F','C'):\r
+ case PI_CNVT_CODE('B','G'):\r
+ s++;\r
+ break;\r
+#endif\r
+\r
+ case PI_CNVT_CODE('L','M'):\r
+ case PI_CNVT_CODE('R','M'):\r
+ case PI_CNVT_CODE('S','H'):\r
+ case PI_CNVT_CODE('P','X'):\r
+ case PI_CNVT_CODE('P','Y'):\r
+ s += 3;\r
+ break;\r
+\r
+ case PI_CNVT_CODE('L','J'):\r
+ case PI_CNVT_CODE('R','J'):\r
+ // No parameters to pass over!\r
+ break;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ length += ch_width(*s++);\r
+ break;\r
+ }\r
+ }\r
+ cur_x = ((xh-xl+1)-length)/2;\r
+ centering = true;\r
+ break;\r
+\r
+ // DRAW SHAPE -------------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('S','H'):\r
+ shapenum = PI_VALUE(first_ch,3);\r
+ first_ch += 3;\r
+ shape_info = &pi_shape_table[shapenum];\r
+ switch (shape_info->shape_type)\r
+ {\r
+ short width;\r
+\r
+ case pis_pic2x:\r
+ cur_x = ((cur_x+2) + 7) & 0xFFF8;\r
+ width=BoxAroundPic(cur_x-2,cur_y-1,shape_info->shapenum,pi);\r
+ VW_DrawPic2x(cur_x>>3,cur_y,shape_info->shapenum);\r
+ cur_x += width;\r
+ break;\r
+ }\r
+ break;\r
+\r
+ // INIT ANIMATION ---------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('A','N'):\r
+ shapenum = PI_VALUE(first_ch,2);\r
+ first_ch += 2;\r
+ _fmemcpy(&pi_anim_list[numanims],&pi_anim_table[shapenum],sizeof(pi_anim_info));\r
+ anim = &pi_anim_list[numanims++];\r
+ shape_info = &pi_shape_table[anim->baseshape+anim->frame];\r
+ switch (shape_info->shape_type)\r
+ {\r
+ short width;\r
+\r
+ case pis_pic2x:\r
+ cur_x = ((cur_x+2) + 7) & 0xFFF8;\r
+ width=BoxAroundPic(cur_x-2,cur_y-1,shape_info->shapenum,pi);\r
+ VW_DrawPic2x(cur_x>>3,cur_y,shape_info->shapenum);\r
+ anim->x = cur_x>>3;\r
+ anim->y = cur_y;\r
+ cur_x += width;\r
+ break;\r
+ }\r
+ break;\r
+\r
+#ifndef AMIGA\r
+ // FONT COLOR -------------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('F','C'):\r
+ fontcolor = bgcolor ^ PI_VALUE(first_ch++,1);\r
+ break;\r
+#endif\r
+\r
+ // BACKGROUND COLOR -------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('B','G'):\r
+ bgcolor = PI_VALUE(first_ch++,1);\r
+ break;\r
+\r
+ // LEFT MARGIN ------------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('L','M'):\r
+ shapenum = PI_VALUE(first_ch,3);\r
+ first_ch += 3;\r
+ if (shapenum == 0xfff)\r
+ xl = cur_x;\r
+ else\r
+ xl = shapenum;\r
+ break;\r
+\r
+ // RIGHT MARGIN -----------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('R','M'):\r
+ shapenum = PI_VALUE(first_ch,3);\r
+ first_ch += 3;\r
+ if (shapenum == 0xfff)\r
+ xh = cur_x;\r
+ else\r
+ xh = shapenum;\r
+ break;\r
+\r
+ // SET X COORDINATE -------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('P','X'):\r
+ cur_x = PI_VALUE(first_ch,3);\r
+ first_ch += 3;\r
+ break;\r
+\r
+ // SET Y COORDINATE -------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('P','Y'):\r
+ cur_y = PI_VALUE(first_ch,3);\r
+ first_ch += 3;\r
+ break;\r
+\r
+ // LEFT JUSTIFY -----------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('L','J'):\r
+ justify_mode = jm_left;\r
+ break;\r
+\r
+ // RIGHT JUSTIFY ----------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('R','J'):\r
+ justify_mode = jm_right;\r
+ break;\r
+\r
+ // END OF PAGE ------------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('E','P'):\r
+ if (pi_recursing)\r
+ Quit("Presenter(): Can't use ^EP when recursing!");\r
+\r
+ endmsg.xl = cur_x;\r
+ endmsg.yl = yh-(font_height+2);\r
+ endmsg.xh = xh;\r
+ endmsg.yh = yh;\r
+ endmsg.bgcolor = bgcolor;\r
+ endmsg.ltcolor = pi->ltcolor;\r
+ endmsg.dkcolor = pi->dkcolor;\r
+ endmsg.script[0] = (char far *)"^CE^FC8- ^FC0ESC ^FC8to return to play, or ^FC0ARROW KEYS ^FC8to page through more Help -^XX";\r
+\r
+ pi_recursing = true;\r
+ Presenter(&endmsg);\r
+ pi_recursing = false;\r
+\r
+#ifndef AMIGA\r
+ if (screenfaded)\r
+ VW_FadeIn();\r
+ VW_ColorBorder(8 | 56);\r
+#endif\r
+\r
+ while (1)\r
+ {\r
+#ifndef AMIGA\r
+ long newtime;\r
+\r
+ VW_WaitVBL(1);\r
+ newtime = TimeCount;\r
+ realtics = tics = newtime-lasttimecount;\r
+ lasttimecount = newtime;\r
+#else\r
+ WaitVBL(1);\r
+ CALC_TICS;\r
+#endif\r
+ AnimatePage(numanims);\r
+ IN_ReadControl(0,&control);\r
+\r
+ if (control.button1 || Keyboard[1])\r
+ {\r
+ presenting=false;\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ if (ControlTypeUsed != ctrl_Keyboard)\r
+ control.dir = dir_None;\r
+\r
+ if (((control.dir == dir_North) || (control.dir == dir_West)) && (PageNum))\r
+ {\r
+ if (up_released)\r
+ {\r
+ PageNum--;\r
+ up_released = false;\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ up_released = true;\r
+ if (((control.dir == dir_South) || (control.dir == dir_East)) && (PageNum < pi->numpages-1))\r
+ {\r
+ if (dn_released)\r
+ {\r
+ PageNum++;\r
+ dn_released = false;\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ dn_released = true;\r
+ }\r
+ }\r
+ }\r
+\r
+ cur_x = xl;\r
+ cur_y = yl;\r
+ if (cur_y+font_height > yh)\r
+ cur_y = yh-font_height;\r
+ first_ch = pi->script[PageNum];\r
+\r
+ numanims = 0;\r
+ PurgeAllGfx();\r
+ CachePage(first_ch);\r
+\r
+ VW_Bar(xl,yl,xh-xl+1,yh-yl+1,bgcolor);\r
+ break;\r
+\r
+ // EXIT PRESENTER ---------------------------------------------------\r
+ //\r
+ case PI_CNVT_CODE('X','X'):\r
+ presenting=false;\r
+ break;\r
+ }\r
+\r
+ if ((first_ch[0] == ' ') && (first_ch[1] == '\n') && start_of_line)\r
+ first_ch += 2;\r
+ }\r
+ }\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// ResetAnims()\r
+//--------------------------------------------------------------------------\r
+void ResetAnims()\r
+{\r
+ pi_anim_list[0].baseshape = -1;\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// AnimatePage()\r
+//--------------------------------------------------------------------------\r
+void AnimatePage(short numanims)\r
+{\r
+ pi_anim_info far *anim=pi_anim_list;\r
+ pi_shape_info far *shape_info;\r
+\r
+ while (numanims--)\r
+ {\r
+ anim->delay += tics;\r
+ if (anim->delay >= anim->maxdelay)\r
+ {\r
+ anim->delay = 0;\r
+ anim->frame++;\r
+ if (anim->frame == anim->maxframes)\r
+ anim->frame = 0;\r
+\r
+#if ANIM_USES_SHAPETABLE\r
+ shape_info = &pi_shape_table[anim->baseshape+anim->frame];\r
+#else\r
+ shape_info = &pi_shape_table[anim->baseshape];\r
+#endif\r
+ switch (shape_info->shape_type)\r
+ {\r
+ case pis_pic2x:\r
+#if ANIM_USES_SHAPETABLE\r
+ VW_DrawPic2x(anim->x,anim->y,shape_info->shapenum);\r
+#else\r
+ VW_DrawPic2x(anim->x,anim->y,shape_info->shapenum+anim->frame);\r
+#endif\r
+ break;\r
+ }\r
+ }\r
+ anim++;\r
+ }\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// BoxAroundPic()\r
+//--------------------------------------------------------------------------\r
+short BoxAroundPic(short x1, short y1, unsigned picnum, PresenterInfo *pi)\r
+{\r
+ short x2,y2;\r
+\r
+ x2 = x1+(pictable[picnum-STARTPICS].width<<4)+2;\r
+ y2 = y1+(pictable[picnum-STARTPICS].height)+1;\r
+ VWB_Hlin(x1,x2,y1,pi->ltcolor);\r
+ VWB_Hlin(x1,x2,y2,pi->dkcolor);\r
+ VWB_Vlin(y1,y2,x1,pi->ltcolor);\r
+ VWB_Vlin(y1,y2,x1+1,pi->ltcolor);\r
+ VWB_Vlin(y1,y2,x2,pi->dkcolor);\r
+ VWB_Vlin(y1,y2,x2+1,pi->dkcolor);\r
+\r
+ return(x2-x1+1);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// PurgeAllGfx()\r
+//--------------------------------------------------------------------------\r
+void PurgeAllGfx()\r
+{\r
+ ResetAnims();\r
+ FreeUpMemory();\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// CachePage()\r
+//--------------------------------------------------------------------------\r
+void CachePage(char far *script)\r
+{\r
+ pi_anim_info far *anim;\r
+ short loop;\r
+ unsigned shapenum;\r
+ boolean end_of_page=false;\r
+ short numanims=0;\r
+\r
+ while (!end_of_page)\r
+ {\r
+ switch (*script++)\r
+ {\r
+ case PI_CONTROL_CHAR:\r
+#ifndef PI_CASE_SENSITIVE\r
+ *script=toupper(*script);\r
+ *(script+1)=toupper(*(script+1));\r
+#endif\r
+ switch (*((unsigned far *)script)++)\r
+ {\r
+ case PI_CNVT_CODE('S','H'):\r
+ shapenum = PI_VALUE(script,3);\r
+ script += 3;\r
+ CA_MarkGrChunk(pi_shape_table[shapenum].shapenum);\r
+ break;\r
+\r
+ case PI_CNVT_CODE('A','N'):\r
+ shapenum = PI_VALUE(script,2);\r
+ script += 2;\r
+\r
+ if (numanims++ == PI_MAX_ANIMS)\r
+ Quit("CachePage(): Too many anims on one page.");\r
+\r
+ anim = &pi_anim_table[shapenum];\r
+#if ANIM_USES_SHAPETABLE\r
+ for (loop=anim->baseshape;loop < anim->baseshape+anim->maxframes; loop++)\r
+ CA_MarkGrChunk(pi_shape_table[loop].shapenum);\r
+#else\r
+ shapenum = pi_shape_table[anim->baseshape].shapenum;\r
+ for (loop=0; loop<anim->maxframes; loop++)\r
+ CA_MarkGrChunk(shapenum+loop);\r
+#endif\r
+ break;\r
+\r
+ case PI_CNVT_CODE('X','X'):\r
+ case PI_CNVT_CODE('E','P'):\r
+ end_of_page = true;\r
+ break;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ CA_CacheMarks(NULL);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// PI_VALUE()\r
+//--------------------------------------------------------------------------\r
+unsigned PI_VALUE(char far *ptr,char num_nybbles)\r
+{\r
+ char ch,nybble,shift;\r
+ unsigned value=0;\r
+\r
+ for (nybble=0; nybble<num_nybbles; nybble++)\r
+ {\r
+ shift = 4*(num_nybbles-nybble-1);\r
+\r
+ ch = *ptr++;\r
+ if (isxdigit(ch))\r
+ if (isalpha(ch))\r
+ value |= (toupper(ch)-'A'+10)<<shift;\r
+ else\r
+ value |= (ch-'0')<<shift;\r
+ }\r
+\r
+ return(value);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// LoadPresenterScript()\r
+//--------------------------------------------------------------------------\r
+long LoadPresenterScript(char *filename,PresenterInfo *pi)\r
+{\r
+#pragma warn -pia\r
+ long size;\r
+\r
+ if (!(size=BLoad(filename,&pi->scriptstart)))\r
+ return(0);\r
+ pi->script[0] = MK_FP(pi->scriptstart,0);\r
+ pi->script[0][size-1] = 0; // Last byte is trashed!\r
+ InitPresenterScript(pi);\r
+\r
+ return(size);\r
+#pragma warn +pia\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// FreePresenterScript()\r
+//-------------------------------------------------------------------------\r
+void FreePresenterScript(PresenterInfo *pi)\r
+{\r
+ if (pi->script)\r
+ MM_FreePtr(&pi->scriptstart);\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// InitPresenterScript()\r
+//-------------------------------------------------------------------------\r
+void InitPresenterScript(PresenterInfo *pi)\r
+{\r
+ char far *script = pi->script[0];\r
+\r
+ pi->numpages = 1; // Assume at least 1 page\r
+ while (*script)\r
+ {\r
+ switch (*script++)\r
+ {\r
+ case PI_CONTROL_CHAR:\r
+#ifndef PI_CASE_SENSITIVE\r
+ *script=toupper(*script);\r
+ *(script+1)=toupper(*(script+1));\r
+#endif\r
+ switch (*((unsigned far *)script)++)\r
+ {\r
+ case PI_CNVT_CODE('E','P'):\r
+ if (pi->numpages < PI_MAX_PAGES)\r
+ pi->script[pi->numpages++] = script;\r
+ else\r
+ TrashProg("GE ERROR: Too many Presenter() pages. --> %d",pi->numpages);\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case '\r':\r
+ if (*script == '\n')\r
+ {\r
+ *(script-1) = ' ';\r
+ script++;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ pi->numpages--; // Last page defined is not a real page.\r
+}\r
+#endif\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// AnimateWallList()\r
+//-------------------------------------------------------------------------\r
+void AnimateWallList(void)\r
+{\r
+ walltype *wall, *check;\r
+ unsigned i;\r
+ int tile,org_tile;\r
+\r
+ if (wall_anim_delay>0)\r
+ {\r
+ wall_anim_delay-=realtics;\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Re-Init our counter...\r
+ //\r
+\r
+ wall_anim_delay = wall_anim_time;\r
+\r
+ //\r
+ // Clear all previous flags marking animation being DONE.\r
+ //\r
+\r
+ for (i=0;i<NUMFLOORS;i++)\r
+ TILE_FLAGS(i) &= ~tf_MARKED;\r
+\r
+\r
+ //\r
+ // Run though wall list updating only then needed animations\r
+ //\r
+\r
+ for (wall=&walls[1];wall<rightwall;wall++)\r
+ {\r
+ org_tile = tile = wall->color + wall_anim_pos[wall->color];\r
+\r
+ if (ANIM_FLAGS(tile))\r
+ {\r
+ do\r
+ {\r
+ if (!(TILE_FLAGS(tile) & tf_MARKED))\r
+ {\r
+ //\r
+ // update our offset table (0-NUMANIMS)\r
+ //\r
+\r
+ wall_anim_pos[tile] += (char signed)ANIM_FLAGS(tile+(char signed)wall_anim_pos[tile]);\r
+\r
+ //\r
+ // Mark tile as being already updated..\r
+ //\r
+\r
+ TILE_FLAGS(tile) |= tf_MARKED;\r
+ }\r
+\r
+ //\r
+ // Check rest of tiles in this animation string...\r
+ //\r
+\r
+ tile += (char signed)ANIM_FLAGS(tile);\r
+\r
+ } while (tile != org_tile);\r
+ }\r
+ }\r
+}\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "SL_FILE.h"\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// Defines\r
+//\r
+\r
+#define ANIM_USES_SHAPE_TABLE false\r
+#define PI_MAX_ANIMS 10\r
+#define PI_MAX_PAGES 40\r
+\r
+#define SAVEVER_DATA "0.01"\r
+#define FILENAME_LEN 15\r
+\r
+#define GAMENAME "CATACOMB ARMAGEDDON 3-D"\r
+#define VERSION "V1.02"\r
+#define REVISION " rev 1 "\r
+\r
+//#define BOBLIST 1 //SP - Undefine if not using BOBList\r
+\r
+#define AUDIO_DISK (2)\r
+#define VIDEO_DISK (1)\r
+#define LEVEL_DISK (2)\r
+\r
+#define BIO_BUFFER_LEN (512)\r
+\r
+#define TrashProg Quit\r
+\r
+// #define AMIGA\r
+\r
+\r
+typedef struct Sample {\r
+ char *filename;\r
+ memptr *data;\r
+} Sample;\r
+\r
+typedef enum {ged_none, ged_SoundSource,ged_SoundBlaster} AudioDeviceType;\r
+\r
+//typedef struct {\r
+// memptr textptr;\r
+// char far *pages[MAX_TEXT_PAGES];\r
+// short totalpages;\r
+//} textinfo;\r
+\r
+typedef struct {\r
+ int handle; // handle of file\r
+ memptr buffer; // pointer to buffer\r
+ word offset; // offset into buffer\r
+ word status; // read/write status\r
+} BufferedIO;\r
+\r
+typedef enum ANIMINFO {at_NONE,at_INIT,at_WAIT,at_ONCE,at_CYCLE,\r
+ at_REBOUND,at_EXTRA,\r
+ at_FWD,at_REV\r
+} ANIMINFO;\r
+\r
+struct BitMapHeader {\r
+ unsigned int w,h,x,y;\r
+ unsigned char d,trans,comp,pad;\r
+};\r
+\r
+struct BitMap {\r
+ unsigned int Width;\r
+ unsigned int Height;\r
+ unsigned int Depth;\r
+ unsigned int BytesPerRow;\r
+ char far *Planes[8];\r
+};\r
+\r
+struct Shape {\r
+ memptr Data;\r
+ long size;\r
+ unsigned int BPR;\r
+ struct BitMapHeader bmHdr;\r
+};\r
+\r
+#ifdef AMIGA\r
+typedef struct {\r
+ char *script[PI_MAX_PAGES];\r
+ XBitMap **shapes;\r
+ XBitMap **font;\r
+ short xl,yl,xh,yh;\r
+ struct BitMap *dst;\r
+ char numpages,bgcolor;\r
+} PresenterInfo;\r
+#else\r
+typedef struct {\r
+ char far *script[PI_MAX_PAGES];\r
+ memptr scriptstart;\r
+ short xl,yl,xh,yh;\r
+ char numpages,bgcolor,ltcolor,dkcolor;\r
+} PresenterInfo;\r
+#endif\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// Externs\r
+//\r
+\r
+extern char Filename[], ID[], VER[];\r
+extern boolean ge_textmode;\r
+extern short PPT_LeftEdge,PPT_RightEdge;\r
+//extern boolean ConserveMemory;\r
+extern BufferedIO lzwBIO;\r
+extern short wall_anim_delay,wall_anim_time;\r
+\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// Function prototypes\r
+//\r
+void WaitKeyVBL(short key, short vbls);\r
+void CalibrateJoystick(short joynum);\r
+void MoveScreen(short x, short y);\r
+void MoveGfxDst(short x, short y);\r
+void DoPiracy(void);\r
+void PrintPropText(char far *text);\r
+//void DisplayText(textinfo *textinfo);\r
+//long LoadTextFile(char *filename,textinfo *textinfo);\r
+//void FreeTextFile(textinfo *textinfo);\r
+//void InitTextFile(textinfo *textinfo);\r
+long Verify(char *filename);\r
+void GE_SaveGame(void);\r
+boolean GE_LoadGame(void);\r
+int GE_HardError(word errval,int ax,int bp,int si);\r
+\r
+#ifdef BOBLIST\r
+\r
+\r
+boolean UpdateBOBList(objtype *obj,struct Simple_Shape *Shape,shapeclass Class, short priority, spriteflags sprflags);\r
+boolean RemoveBOBShape(objtype *obj, shapeclass Class);\r
+void RemoveBOBList(objtype *obj);\r
+void InitBOBList(objtype *obj, struct BOB_Shape *BOB_Shape, short NumElements);\r
+void RefreshBOBList(objtype *obj);\r
+#endif\r
+\r
+\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr);\r
+void lzwDecompressFromRAM(byte far *SrcPtr, byte far *DstPtr, longword SrcLen);\r
+void lzwDecompressFromFile(BufferedIO *SrcPtr, byte far *DstPtr, longword SrcLen);\r
+byte readch(int handle);\r
+\r
+memptr InitBufferedIO(int handle, BufferedIO *bio);\r
+void FreeBufferedIO(BufferedIO *bio);\r
+byte bio_readch(BufferedIO *bio);\r
+void bio_fillbuffer(BufferedIO *bio);\r
+\r
+\r
+void SwapLong(long far *Var);\r
+void SwapWord(unsigned int far *Var);\r
+int LoadShape(char *Filename,struct Shape *SHP);\r
+void FreeShape(struct Shape *shape);\r
+int UnpackEGAShapeToScreen(struct Shape *SHP,int startx,int starty);\r
+char GetKeyChoice(char *choices,boolean clear);\r
+boolean AnimateObj(objtype *obj);\r
+void AdvanceAnimFWD(objtype *obj);\r
+void AdvanceAnimREV(objtype *obj);\r
+\r
+void LoadASArray(struct Sample *ASArray);\r
+void FreeASArray(struct Sample *ASArray);\r
+//void SelectDigiAudio(AudioDeviceType Device);\r
+void PlaySample(unsigned SampleNum);\r
+void GE_FreeAllDigiSounds(void);\r
+void GE_LoadAllDigiSounds(void);\r
+void DisplayGameList(short winx, short winy, short list_width, short list_height);\r
+void ReadGameList(void);\r
+void CheckStack(void);\r
+void CenterObj(objtype *obj, unsigned x, unsigned y);\r
+void cachein(short s,short e);\r
+void cacheout(short s,short e);\r
+void FizzleFade (unsigned source, unsigned dest,unsigned width,unsigned height, boolean abortable);\r
+void mprintf(char *msg, ...);\r
+boolean FindFile(char *filename,char *disktext,char disknum);\r
+void CacheAV(char *title);\r
+void BlackPalette(void);\r
+void ColoredPalette(void);\r
+void Presenter(PresenterInfo *pi);\r
+unsigned PI_VALUE(char far *ptr,char num_nybbles);\r
+long LoadPresenterScript(char *filename,PresenterInfo *pi);\r
+void FreePresenterScript(PresenterInfo *pi);\r
+void InitPresenterScript(PresenterInfo *pi);\r
+\r
+void AnimatePage(short numanims);\r
+short BoxAroundPic(short x1, short y1, unsigned picnum, PresenterInfo *pi);\r
+void PurgeAllGfx(void);\r
+void CachePage(char far *script);\r
+\r
+\r
+void AnimateWallList(void);\r
--- /dev/null
+;=====================================\r
+;\r
+; Graphics .EQU file for .ARM\r
+; IGRAB-ed on Thu Dec 02 13:59:00 1993\r
+;\r
+;=====================================\r
+\r
+FINALEPIC = 4\r
+STATUSPIC = 5\r
+FACE5PIC = 6\r
+FIRSTLATCHPIC = 7\r
+FACE1PIC = 8\r
+FACE2PIC = 9\r
+FACE3PIC = 10\r
+FACE4PIC = 11\r
+RADAR_TOPPIC = 12\r
+RADAR_BOTTOMPIC = 13\r
+RADAR_RGEMPIC = 14\r
+RADAR_GGEMPIC = 15\r
+RADAR_BGEMPIC = 16\r
+RADAR_YGEMPIC = 17\r
+RADAR_PGEMPIC = 18\r
+FIRSTGROUNDPIC = 19\r
+FIRSTSTRIPPIC = 20\r
+FIRSTSCALEPIC = 21\r
+SKELETON_1PIC = 22\r
+SKELETON_2PIC = 23\r
+SKELETON_3PIC = 24\r
+SKELETON_4PIC = 25\r
+SKELETON_ATTACK_1PIC = 26\r
+SKELETON_ATTACK_2PIC = 27\r
+SKELETON_ATTACK_3PIC = 28\r
+SKELETON_OUCHPIC = 29\r
+SKELETON_DEATH_1PIC = 30\r
+SKELETON_DEATH_2PIC = 31\r
+TOMB1PIC = 32\r
+TOMB2PIC = 33\r
+TOMB3PIC = 34\r
+OBJ_WARP1PIC = 35\r
+OBJ_WARP2PIC = 36\r
+OBJ_WARP3PIC = 37\r
+OBJ_WARP4PIC = 38\r
+EYE_WALK1PIC = 39\r
+EYE_WALK2PIC = 40\r
+EYE_WALK3PIC = 41\r
+EYE_OUCH1PIC = 42\r
+EYE_OUCH2PIC = 43\r
+EYE_DEATH1PIC = 44\r
+EYE_DEATH2PIC = 45\r
+EYE_DEATH3PIC = 46\r
+EYE_SCOWLPIC = 47\r
+EYE_SHOT1PIC = 48\r
+EYE_SHOT2PIC = 49\r
+ZOMB_APPEAR1PIC = 50\r
+ZOMB_APPEAR2PIC = 51\r
+ZOMB_APPEAR3PIC = 52\r
+ZOMB_APPEAR4PIC = 53\r
+ZOMB_WALK1PIC = 54\r
+ZOMB_WALK2PIC = 55\r
+ZOMB_WALK3PIC = 56\r
+ZOMB_OUCHPIC = 57\r
+ZOMB_ATTACKPIC = 58\r
+ZOMB_DIE1PIC = 59\r
+ZOMB_DIE2PIC = 60\r
+ZOMB_DIE3PIC = 61\r
+BOLTOBJPIC = 62\r
+BOLT2OBJPIC = 63\r
+BOLT3OBJPIC = 64\r
+NUKEOBJPIC = 65\r
+NUKE2OBJPIC = 66\r
+NUKE3OBJPIC = 67\r
+TIMEOBJ1PIC = 68\r
+TIMEOBJ2PIC = 69\r
+O_WATER_CHEST1PIC = 70\r
+O_WATER_CHEST2PIC = 71\r
+POTIONOBJPIC = 72\r
+RKEYOBJPIC = 73\r
+YKEYOBJPIC = 74\r
+GKEYOBJPIC = 75\r
+BKEYOBJPIC = 76\r
+RGEM1PIC = 77\r
+RGEM2PIC = 78\r
+GGEM1PIC = 79\r
+GGEM2PIC = 80\r
+BGEM1PIC = 81\r
+BGEM2PIC = 82\r
+YGEM1PIC = 83\r
+YGEM2PIC = 84\r
+PGEM1PIC = 85\r
+PGEM2PIC = 86\r
+CHESTOBJPIC = 87\r
+PSHOT1PIC = 88\r
+PSHOT2PIC = 89\r
+PSHOT_EXP1PIC = 90\r
+PSHOT_EXP2PIC = 91\r
+PSHOT_EXP3PIC = 92\r
+RED_DEMON1PIC = 93\r
+RED_DEMON2PIC = 94\r
+RED_DEMON3PIC = 95\r
+RED_DEMON4PIC = 96\r
+RED_DEMONATTACK1PIC = 97\r
+RED_DEMONATTACK2PIC = 98\r
+RED_DEMONATTACK3PIC = 99\r
+RED_DEMONOUCHPIC = 100\r
+RED_DEMONDIE1PIC = 101\r
+RED_DEMONDIE2PIC = 102\r
+RED_DEMONDIE3PIC = 103\r
+MAGE1PIC = 104\r
+MAGE2PIC = 105\r
+MAGEOUCHPIC = 106\r
+MAGEATTACKPIC = 107\r
+MAGEDIE1PIC = 108\r
+MAGEDIE2PIC = 109\r
+BAT1PIC = 110\r
+BAT2PIC = 111\r
+BAT3PIC = 112\r
+BAT4PIC = 113\r
+BATDIE1PIC = 114\r
+BATDIE2PIC = 115\r
+GREL1PIC = 116\r
+GREL2PIC = 117\r
+GRELATTACKPIC = 118\r
+GRELHITPIC = 119\r
+GRELDIE1PIC = 120\r
+GRELDIE2PIC = 121\r
+GRELDIE3PIC = 122\r
+GRELDIE4PIC = 123\r
+GRELDIE5PIC = 124\r
+GRELDIE6PIC = 125\r
+SKULL_SHOTPIC = 126\r
+GODESS_WALK1PIC = 127\r
+GODESS_WALK2PIC = 128\r
+GODESS_WALK3PIC = 129\r
+GODESS_ATTACK1PIC = 130\r
+GODESS_ATTACK2PIC = 131\r
+GODESS_ATTACK3PIC = 132\r
+GODESS_STATUEPIC = 133\r
+GODESS_OUCHPIC = 134\r
+GODESS_DEATH1PIC = 135\r
+GODESS_DEATH2PIC = 136\r
+ANT_EGG1PIC = 137\r
+ANT_EGG2PIC = 138\r
+ANT_WALK1PIC = 139\r
+ANT_WALK2PIC = 140\r
+ANT_WALK3PIC = 141\r
+ANT_ATTACKPIC = 142\r
+ANT_DEATH1PIC = 143\r
+ANT_DEATH2PIC = 144\r
+ANT_DEATH3PIC = 145\r
+FATDEMON_WALK1PIC = 146\r
+FATDEMON_WALK2PIC = 147\r
+FATDEMON_WALK3PIC = 148\r
+FATDEMON_WALK4PIC = 149\r
+FATDEMON_ATTACK1PIC = 150\r
+FATDEMON_ATTACK2PIC = 151\r
+FATDEMON_OUCHPIC = 152\r
+FATDEMON_BLOWUP1PIC = 153\r
+FATDEMON_BLOWUP2PIC = 154\r
+FATDEMON_BLOWUP3PIC = 155\r
+FATDEMON_EXPLODEPIC = 156\r
+FATDEMON_FEETPIC = 157\r
+SUCCUBUS_WALK1PIC = 158\r
+SUCCUBUS_WALK2PIC = 159\r
+SUCCUBUS_WALK3PIC = 160\r
+SUCCUBUS_WALK4PIC = 161\r
+SUCCUBUS_ATTACK1PIC = 162\r
+SUCCUBUS_ATTACK2PIC = 163\r
+SUCCUBUS_OUCHPIC = 164\r
+SUCCUBUS_DEATH1PIC = 165\r
+SUCCUBUS_DEATH2PIC = 166\r
+SUCCUBUS_SHOT1PIC = 167\r
+TREE_IDLEPIC = 168\r
+TREE_AWAKENINGPIC = 169\r
+TREE_WALK1PIC = 170\r
+TREE_WALK2PIC = 171\r
+TREE_ATTACK1PIC = 172\r
+TREE_ATTACK2PIC = 173\r
+TREE_ATTACK3PIC = 174\r
+TREE_DEATH1PIC = 175\r
+TREE_DEATH2PIC = 176\r
+TREE_DEATH3PIC = 177\r
+DRAGON_BUBBLES1PIC = 178\r
+DRAGON_BUBBLES2PIC = 179\r
+DRAGON_EYESPIC = 180\r
+DRAGON_RISE1PIC = 181\r
+DRAGON_RISE2PIC = 182\r
+DRAGON_WALK1PIC = 183\r
+DRAGON_WALK2PIC = 184\r
+DRAGON_WALK3PIC = 185\r
+DRAGON_WALK4PIC = 186\r
+DRAGON_ATTACK1PIC = 187\r
+DRAGON_ATTACK2PIC = 188\r
+DRAGON_ATTACK3PIC = 189\r
+DRAGON_OUCHPIC = 190\r
+DRAGON_DEATH1PIC = 191\r
+DRAGON_DEATH2PIC = 192\r
+DRAGON_DEATH3PIC = 193\r
+BUNNY_LEFT1PIC = 194\r
+BUNNY_LEFT2PIC = 195\r
+BUNNY_RIGHT1PIC = 196\r
+BUNNY_RIGHT2PIC = 197\r
+BUNNY_META1PIC = 198\r
+BUNNY_META2PIC = 199\r
+BUNNY_WALK1PIC = 200\r
+BUNNY_WALK2PIC = 201\r
+BUNNY_OUCHPIC = 202\r
+BUNNY_DEATH1PIC = 203\r
+BUNNY_DEATH2PIC = 204\r
+ARCH1PIC = 205\r
+ARCH2PIC = 206\r
+ARCH3PIC = 207\r
+ARCH4PIC = 208\r
+ARCH5PIC = 209\r
+ARCH6PIC = 210\r
+ARCH7PIC = 211\r
+ARCH8PIC = 212\r
+ARCH9PIC = 213\r
+ARCH10PIC = 214\r
+ARCH11PIC = 215\r
+ARCH12PIC = 216\r
+ARCH13PIC = 217\r
+ANT_HILLPIC = 218\r
+COLUMNPIC = 219\r
+SULPHUR_GAS_1PIC = 220\r
+SULPHUR_GAS_2PIC = 221\r
+SULPHUR_GAS_3PIC = 222\r
+FIRE_POT_1PIC = 223\r
+FIRE_POT_2PIC = 224\r
+SKEL_HANGPIC = 225\r
+FORCE_FIELD_1PIC = 226\r
+FORCE_FIELD_2PIC = 227\r
+FORCE_FIELD_3PIC = 228\r
+FORCE_FIELD_4PIC = 229\r
+WFOUNTAINPIC = 230\r
+FIRSTWALLPIC = 231\r
+CRYSTAL_LIGHT_1PIC = 232\r
+CRYSTAL_LIGHT_2PIC = 233\r
+CRYSTAL_LIGHT_3PIC = 234\r
+CRYSTAL_LIGHT_4PIC = 235\r
+CRYSTAL_DARK_1PIC = 236\r
+CRYSTAL_DARK_2PIC = 237\r
+CRYSTAL_DARK_3PIC = 238\r
+CRYSTAL_DARK_4PIC = 239\r
+FIRE_WALL_1PIC = 240\r
+FIRE_WALL_2PIC = 241\r
+FIRE_WALL_3PIC = 242\r
+FIRE_WALL_4PIC = 243\r
+BRN_STONE_GATEPIC = 244\r
+BRN_STONE_WALL_1PIC = 245\r
+BRN_STONE_WALL_2PIC = 246\r
+KUDZU_LIGHT_WALLPIC = 247\r
+KUDZU_DARK_WALLPIC = 248\r
+HEDGE_WALLPIC = 249\r
+HEDGE_EYESPIC = 250\r
+BRN_WINDOW_LIGHTPIC = 251\r
+ALTAR_LEFTPIC = 252\r
+ALTAR_RIGHTPIC = 253\r
+GRAY_LIGHT_WALLPIC = 254\r
+GRAY_DARK_WALLPIC = 255\r
+GRAY_LIGHT_SIGNPIC = 256\r
+GRAY_DARK_SIGNPIC = 257\r
+MANICLE_LIGHT_BLOODYPIC = 258\r
+MANICLE_DARK_BLOODYPIC = 259\r
+LIGHT_CURTAIN_WINDOWPIC = 260\r
+LIGHT_CURTAIN_WALLPIC = 261\r
+DARK_CURTAIN_WINDOWPIC = 262\r
+DARK_CURTAIN_WALLPIC = 263\r
+BRN_LIGHT_SIGNPIC = 264\r
+BRN_DARK_SIGNPIC = 265\r
+LIGHT_STONE_WALLPIC = 266\r
+DARK_STONE_WALLPIC = 267\r
+BRN_FLAGSTONE_LIGHT_2PIC = 268\r
+BRN_FLAGSTONE_DARK_2PIC = 269\r
+RUST_METAL_LIGHTPIC = 270\r
+RUST_METAL_DARKPIC = 271\r
+GRAY_METAL_LIGHTPIC = 272\r
+GRAY_METAL_DARKPIC = 273\r
+WEAK_STONE_LIGHTPIC = 274\r
+WEAK_STONE_DARKPIC = 275\r
+WEAK_GRAY_RFGSTN_LIGHTPIC = 276\r
+WEAK_GRAY_RFGSTN_DARKPIC = 277\r
+WEAK_CRYSTAL_LIGHTPIC = 278\r
+WEAK_CRYSTAL_DARKPIC = 279\r
+RED_MUD_LIGHTPIC = 280\r
+BRN_MUD_DARKPIC = 281\r
+RED_MUD_WEAK_LIGHTPIC = 282\r
+BRN_MUD_WEAK_DARKPIC = 283\r
+HORN_DOORPIC = 284\r
+CLOSED_DOOR_1PIC = 285\r
+DOOR_2PIC = 286\r
+WATER_LIGHT_WEAK_1PIC = 287\r
+WATER_LIGHT_WEAK_2PIC = 288\r
+WATER_LIGHT_WEAK_3PIC = 289\r
+WATER_DARK_WEAK_1PIC = 290\r
+WATER_DARK_WEAK_2PIC = 291\r
+WATER_DARK_WEAK_3PIC = 292\r
+WATER_LIGHT_1PIC = 293\r
+WATER_LIGHT_2PIC = 294\r
+WATER_LIGHT_3PIC = 295\r
+WATER_DARK_1PIC = 296\r
+WATER_DARK_2PIC = 297\r
+WATER_DARK_3PIC = 298\r
+TROLL_LIGHT_STONEPIC = 299\r
+TROLL_DARK_STONEPIC = 300\r
+TROLL_BLOODY_LT_STONEPIC = 301\r
+TROLL_BLOODY_DK_STONEPIC = 302\r
+LIGHT_BREATH_1PIC = 303\r
+LIGHT_BREATH_2PIC = 304\r
+LIGHT_BREATH_3PIC = 305\r
+DARK_BREATH_1PIC = 306\r
+DARK_BREATH_2PIC = 307\r
+DARK_BREATH_3PIC = 308\r
+EXP_WALL_1PIC = 309\r
+EXP_WALL_2PIC = 310\r
+EXP_WALL_3PIC = 311\r
+WATER_EXP_WALL_1PIC = 312\r
+WATER_EXP_WALL_2PIC = 313\r
+WATER_EXP_WALL_3PIC = 314\r
+W_GEN_DOOR1PIC = 315\r
+W_GEN_DOOR2PIC = 316\r
+W_CRYSTAL_DOORPIC = 317\r
+DMG_BRN_FSTN_LTPIC = 318\r
+DMG_BRN_FSTN_DKPIC = 319\r
+DMG_FIN_FSTN_LTPIC = 320\r
+DMG_FIN_FSTN_DKPIC = 321\r
+STEEL_DOOR1PIC = 322\r
+STEEL_DOOR2PIC = 323\r
+BRN_WINDOW_DARKPIC = 324\r
+GRY_DOOR_LTPIC = 325\r
+GRY_DOOR_DKPIC = 326\r
+BRN_DOOR_LTPIC = 327\r
+BRN_DOOR_DKPIC = 328\r
+GRY_FGSTN_LTPIC = 329\r
+GRY_FGSTN_DKPIC = 330\r
+KUDZU_WEAK_LIGHTPIC = 331\r
+KUDZU_WEAK_DARKPIC = 332\r
+LT_SKEL1PIC = 333\r
+DK_SKEL1PIC = 334\r
+LT_SKEL2PIC = 335\r
+DK_SKEL2PIC = 336\r
+MANICLE_LIGHT_WALLPIC = 337\r
+MANICLE_DARK_WALLPIC = 338\r
+TAP_1PIC = 339\r
+TAP_2PIC = 340\r
+TAP_3PIC = 341\r
+TAP_4PIC = 342\r
+TAP_5PIC = 343\r
+FINALWALLPIC = 344\r
+WATER_DOOR1_PIC = 345\r
+WATER_DOOR2_PIC = 346\r
+LASTWALLPIC = 347\r
+\r
+HAND1PICM = 348\r
+\r
+NORTHICONSPR = 349\r
+\r
+LEVEL1TEXT = 640\r
+LEVEL2TEXT = 641\r
+LEVEL3TEXT = 642\r
+LEVEL4TEXT = 643\r
+LEVEL5TEXT = 644\r
+LEVEL6TEXT = 645\r
+LEVEL7TEXT = 646\r
+LEVEL8TEXT = 647\r
+LEVEL9TEXT = 648\r
+LEVEL10TEXT = 649\r
+LEVEL11TEXT = 650\r
+LEVEL12TEXT = 651\r
+LEVEL13TEXT = 652\r
+LEVEL14TEXT = 653\r
+LEVEL15TEXT = 654\r
+LEVEL16TEXT = 655\r
+LEVEL17TEXT = 656\r
+PIRACY = 657\r
+\r
+SKELDUDE_LUMP_START = 22\r
+SKELDUDE_LUMP_END = 31\r
+\r
+TOMBSTONES_LUMP_START = 32\r
+TOMBSTONES_LUMP_END = 34\r
+\r
+OBJ_WARP_LUMP_START = 35\r
+OBJ_WARP_LUMP_END = 38\r
+\r
+EYE_LUMP_START = 39\r
+EYE_LUMP_END = 49\r
+\r
+ZOMBIE_LUMP_START = 50\r
+ZOMBIE_LUMP_END = 61\r
+\r
+BOLT_LUMP_START = 62\r
+BOLT_LUMP_END = 64\r
+\r
+NUKE_LUMP_START = 65\r
+NUKE_LUMP_END = 67\r
+\r
+TIME_LUMP_START = 68\r
+TIME_LUMP_END = 69\r
+\r
+O_WATER_CHEST_LUMP_START = 70\r
+O_WATER_CHEST_LUMP_END = 71\r
+\r
+POTION_LUMP_START = 72\r
+POTION_LUMP_END = 72\r
+\r
+RKEY_LUMP_START = 73\r
+RKEY_LUMP_END = 73\r
+\r
+YKEY_LUMP_START = 74\r
+YKEY_LUMP_END = 74\r
+\r
+GKEY_LUMP_START = 75\r
+GKEY_LUMP_END = 75\r
+\r
+BKEY_LUMP_START = 76\r
+BKEY_LUMP_END = 76\r
+\r
+RGEM_LUMP_START = 77\r
+RGEM_LUMP_END = 78\r
+\r
+GGEM_LUMP_START = 79\r
+GGEM_LUMP_END = 80\r
+\r
+BGEM_LUMP_START = 81\r
+BGEM_LUMP_END = 82\r
+\r
+YGEM_LUMP_START = 83\r
+YGEM_LUMP_END = 84\r
+\r
+PGEM_LUMP_START = 85\r
+PGEM_LUMP_END = 86\r
+\r
+CHEST_LUMP_START = 87\r
+CHEST_LUMP_END = 87\r
+\r
+PLAYER_LUMP_START = 88\r
+PLAYER_LUMP_END = 92\r
+\r
+REDDEMON_LUMP_START = 93\r
+REDDEMON_LUMP_END = 103\r
+\r
+MAGE_LUMP_START = 104\r
+MAGE_LUMP_END = 109\r
+\r
+BAT_LUMP_START = 110\r
+BAT_LUMP_END = 115\r
+\r
+GREL_LUMP_START = 116\r
+GREL_LUMP_END = 126\r
+\r
+GODESS_LUMP_START = 127\r
+GODESS_LUMP_END = 136\r
+\r
+ANT_LUMP_START = 137\r
+ANT_LUMP_END = 145\r
+\r
+FATDEMON_LUMP_START = 146\r
+FATDEMON_LUMP_END = 157\r
+\r
+SUCCUBUS_LUMP_START = 158\r
+SUCCUBUS_LUMP_END = 167\r
+\r
+TREE_LUMP_START = 168\r
+TREE_LUMP_END = 177\r
+\r
+DRAGON_LUMP_START = 178\r
+DRAGON_LUMP_END = 193\r
+\r
+BUNNY_LUMP_START = 194\r
+BUNNY_LUMP_END = 204\r
+\r
+ARCH1_LUMP_START = 205\r
+ARCH1_LUMP_END = 205\r
+\r
+ARCH2_LUMP_START = 206\r
+ARCH2_LUMP_END = 206\r
+\r
+ARCH3_LUMP_START = 207\r
+ARCH3_LUMP_END = 207\r
+\r
+ARCH4_LUMP_START = 208\r
+ARCH4_LUMP_END = 208\r
+\r
+ARCH5_LUMP_START = 209\r
+ARCH5_LUMP_END = 209\r
+\r
+ARCH6_LUMP_START = 210\r
+ARCH6_LUMP_END = 210\r
+\r
+ARCH7_LUMP_START = 211\r
+ARCH7_LUMP_END = 211\r
+\r
+ARCH8_LUMP_START = 212\r
+ARCH8_LUMP_END = 212\r
+\r
+ARCH9_LUMP_START = 213\r
+ARCH9_LUMP_END = 213\r
+\r
+ARCH10_LUMP_START = 214\r
+ARCH10_LUMP_END = 214\r
+\r
+ARCH11_LUMP_START = 215\r
+ARCH11_LUMP_END = 215\r
+\r
+ARCH12_LUMP_START = 216\r
+ARCH12_LUMP_END = 216\r
+\r
+ARCH13_LUMP_START = 217\r
+ARCH13_LUMP_END = 217\r
+\r
+ANTHILL_LUMP_START = 218\r
+ANTHILL_LUMP_END = 218\r
+\r
+COLUMN_LUMP_START = 219\r
+COLUMN_LUMP_END = 219\r
+\r
+SULPHURGAS_LUMP_START = 220\r
+SULPHURGAS_LUMP_END = 222\r
+\r
+FIREPOT_LUMP_START = 223\r
+FIREPOT_LUMP_END = 224\r
+\r
+SKELHANG_LUMP_START = 225\r
+SKELHANG_LUMP_END = 225\r
+\r
+FORCEFIELD_LUMP_START = 226\r
+FORCEFIELD_LUMP_END = 229\r
+\r
+FOUNTAIN_LUMP_START = 230\r
+FOUNTAIN_LUMP_END = 230\r
+\r
+\r
+;\r
+; Amount of each data item\r
+;\r
+NUMCHUNKS = 658\r
+NUMFONT = 1\r
+NUMFONTM = 0\r
+NUMPICS = 344\r
+NUMPICM = 1\r
+NUMSPRITES = 1\r
+NUMTILE8 = 108\r
+NUMTILE8M = 36\r
+NUMTILE16 = 216\r
+NUMTILE16M = 72\r
+NUMTILE32 = 0\r
+NUMTILE32M = 0\r
+NUMEXTERN = 18\r
+;\r
+; File offsets for data items\r
+;\r
+STRUCTPIC = 0\r
+STRUCTPICM = 1\r
+STRUCTSPRITE = 2\r
+\r
+STARTFONT = 3\r
+STARTFONTM = 4\r
+STARTPICS = 4\r
+STARTPICM = 348\r
+STARTSPRITES = 349\r
+STARTTILE8 = 350\r
+STARTTILE8M = 351\r
+STARTTILE16 = 352\r
+STARTTILE16M = 568\r
+STARTTILE32 = 640\r
+STARTTILE32M = 640\r
+STARTEXTERN = 640\r
+\r
+;\r
+; Thank you for using IGRAB!\r
+;\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//////////////////////////////////////\r
+//\r
+// Graphics .H file for .ARM\r
+// IGRAB-ed on Thu Dec 02 13:58:59 1993\r
+//\r
+//////////////////////////////////////\r
+\r
+typedef enum {\r
+ FINALEPIC=4,\r
+ STATUSPIC, // 5\r
+ FACE5PIC, // 6\r
+ FIRSTLATCHPIC, // 7\r
+ FACE1PIC, // 8\r
+ FACE2PIC, // 9\r
+ FACE3PIC, // 10\r
+ FACE4PIC, // 11\r
+ RADAR_TOPPIC, // 12\r
+ RADAR_BOTTOMPIC, // 13\r
+ RADAR_RGEMPIC, // 14\r
+ RADAR_GGEMPIC, // 15\r
+ RADAR_BGEMPIC, // 16\r
+ RADAR_YGEMPIC, // 17\r
+ RADAR_PGEMPIC, // 18\r
+ FIRSTGROUNDPIC, // 19\r
+ FIRSTSTRIPPIC, // 20\r
+ FIRSTSCALEPIC, // 21\r
+ // Lump Start\r
+ SKELETON_1PIC, // 22\r
+ SKELETON_2PIC, // 23\r
+ SKELETON_3PIC, // 24\r
+ SKELETON_4PIC, // 25\r
+ SKELETON_ATTACK_1PIC, // 26\r
+ SKELETON_ATTACK_2PIC, // 27\r
+ SKELETON_ATTACK_3PIC, // 28\r
+ SKELETON_OUCHPIC, // 29\r
+ SKELETON_DEATH_1PIC, // 30\r
+ SKELETON_DEATH_2PIC, // 31\r
+ // Lump Start\r
+ TOMB1PIC, // 32\r
+ TOMB2PIC, // 33\r
+ TOMB3PIC, // 34\r
+ // Lump Start\r
+ OBJ_WARP1PIC, // 35\r
+ OBJ_WARP2PIC, // 36\r
+ OBJ_WARP3PIC, // 37\r
+ OBJ_WARP4PIC, // 38\r
+ // Lump Start\r
+ EYE_WALK1PIC, // 39\r
+ EYE_WALK2PIC, // 40\r
+ EYE_WALK3PIC, // 41\r
+ EYE_OUCH1PIC, // 42\r
+ EYE_OUCH2PIC, // 43\r
+ EYE_DEATH1PIC, // 44\r
+ EYE_DEATH2PIC, // 45\r
+ EYE_DEATH3PIC, // 46\r
+ EYE_SCOWLPIC, // 47\r
+ EYE_SHOT1PIC, // 48\r
+ EYE_SHOT2PIC, // 49\r
+ // Lump Start\r
+ ZOMB_APPEAR1PIC, // 50\r
+ ZOMB_APPEAR2PIC, // 51\r
+ ZOMB_APPEAR3PIC, // 52\r
+ ZOMB_APPEAR4PIC, // 53\r
+ ZOMB_WALK1PIC, // 54\r
+ ZOMB_WALK2PIC, // 55\r
+ ZOMB_WALK3PIC, // 56\r
+ ZOMB_OUCHPIC, // 57\r
+ ZOMB_ATTACKPIC, // 58\r
+ ZOMB_DIE1PIC, // 59\r
+ ZOMB_DIE2PIC, // 60\r
+ ZOMB_DIE3PIC, // 61\r
+ // Lump Start\r
+ BOLTOBJPIC, // 62\r
+ BOLT2OBJPIC, // 63\r
+ BOLT3OBJPIC, // 64\r
+ // Lump Start\r
+ NUKEOBJPIC, // 65\r
+ NUKE2OBJPIC, // 66\r
+ NUKE3OBJPIC, // 67\r
+ // Lump Start\r
+ TIMEOBJ1PIC, // 68\r
+ TIMEOBJ2PIC, // 69\r
+ // Lump Start\r
+ O_WATER_CHEST1PIC, // 70\r
+ O_WATER_CHEST2PIC, // 71\r
+ // Lump Start\r
+ POTIONOBJPIC, // 72\r
+ // Lump Start\r
+ RKEYOBJPIC, // 73\r
+ // Lump Start\r
+ YKEYOBJPIC, // 74\r
+ // Lump Start\r
+ GKEYOBJPIC, // 75\r
+ // Lump Start\r
+ BKEYOBJPIC, // 76\r
+ // Lump Start\r
+ RGEM1PIC, // 77\r
+ RGEM2PIC, // 78\r
+ // Lump Start\r
+ GGEM1PIC, // 79\r
+ GGEM2PIC, // 80\r
+ // Lump Start\r
+ BGEM1PIC, // 81\r
+ BGEM2PIC, // 82\r
+ // Lump Start\r
+ YGEM1PIC, // 83\r
+ YGEM2PIC, // 84\r
+ // Lump Start\r
+ PGEM1PIC, // 85\r
+ PGEM2PIC, // 86\r
+ // Lump Start\r
+ CHESTOBJPIC, // 87\r
+ // Lump Start\r
+ PSHOT1PIC, // 88\r
+ PSHOT2PIC, // 89\r
+ PSHOT_EXP1PIC, // 90\r
+ PSHOT_EXP2PIC, // 91\r
+ PSHOT_EXP3PIC, // 92\r
+ // Lump Start\r
+ RED_DEMON1PIC, // 93\r
+ RED_DEMON2PIC, // 94\r
+ RED_DEMON3PIC, // 95\r
+ RED_DEMON4PIC, // 96\r
+ RED_DEMONATTACK1PIC, // 97\r
+ RED_DEMONATTACK2PIC, // 98\r
+ RED_DEMONATTACK3PIC, // 99\r
+ RED_DEMONOUCHPIC, // 100\r
+ RED_DEMONDIE1PIC, // 101\r
+ RED_DEMONDIE2PIC, // 102\r
+ RED_DEMONDIE3PIC, // 103\r
+ // Lump Start\r
+ MAGE1PIC, // 104\r
+ MAGE2PIC, // 105\r
+ MAGEOUCHPIC, // 106\r
+ MAGEATTACKPIC, // 107\r
+ MAGEDIE1PIC, // 108\r
+ MAGEDIE2PIC, // 109\r
+ // Lump Start\r
+ BAT1PIC, // 110\r
+ BAT2PIC, // 111\r
+ BAT3PIC, // 112\r
+ BAT4PIC, // 113\r
+ BATDIE1PIC, // 114\r
+ BATDIE2PIC, // 115\r
+ // Lump Start\r
+ GREL1PIC, // 116\r
+ GREL2PIC, // 117\r
+ GRELATTACKPIC, // 118\r
+ GRELHITPIC, // 119\r
+ GRELDIE1PIC, // 120\r
+ GRELDIE2PIC, // 121\r
+ GRELDIE3PIC, // 122\r
+ GRELDIE4PIC, // 123\r
+ GRELDIE5PIC, // 124\r
+ GRELDIE6PIC, // 125\r
+ SKULL_SHOTPIC, // 126\r
+ // Lump Start\r
+ GODESS_WALK1PIC, // 127\r
+ GODESS_WALK2PIC, // 128\r
+ GODESS_WALK3PIC, // 129\r
+ GODESS_ATTACK1PIC, // 130\r
+ GODESS_ATTACK2PIC, // 131\r
+ GODESS_ATTACK3PIC, // 132\r
+ GODESS_STATUEPIC, // 133\r
+ GODESS_OUCHPIC, // 134\r
+ GODESS_DEATH1PIC, // 135\r
+ GODESS_DEATH2PIC, // 136\r
+ // Lump Start\r
+ ANT_EGG1PIC, // 137\r
+ ANT_EGG2PIC, // 138\r
+ ANT_WALK1PIC, // 139\r
+ ANT_WALK2PIC, // 140\r
+ ANT_WALK3PIC, // 141\r
+ ANT_ATTACKPIC, // 142\r
+ ANT_DEATH1PIC, // 143\r
+ ANT_DEATH2PIC, // 144\r
+ ANT_DEATH3PIC, // 145\r
+ // Lump Start\r
+ FATDEMON_WALK1PIC, // 146\r
+ FATDEMON_WALK2PIC, // 147\r
+ FATDEMON_WALK3PIC, // 148\r
+ FATDEMON_WALK4PIC, // 149\r
+ FATDEMON_ATTACK1PIC, // 150\r
+ FATDEMON_ATTACK2PIC, // 151\r
+ FATDEMON_OUCHPIC, // 152\r
+ FATDEMON_BLOWUP1PIC, // 153\r
+ FATDEMON_BLOWUP2PIC, // 154\r
+ FATDEMON_BLOWUP3PIC, // 155\r
+ FATDEMON_EXPLODEPIC, // 156\r
+ FATDEMON_FEETPIC, // 157\r
+ // Lump Start\r
+ SUCCUBUS_WALK1PIC, // 158\r
+ SUCCUBUS_WALK2PIC, // 159\r
+ SUCCUBUS_WALK3PIC, // 160\r
+ SUCCUBUS_WALK4PIC, // 161\r
+ SUCCUBUS_ATTACK1PIC, // 162\r
+ SUCCUBUS_ATTACK2PIC, // 163\r
+ SUCCUBUS_OUCHPIC, // 164\r
+ SUCCUBUS_DEATH1PIC, // 165\r
+ SUCCUBUS_DEATH2PIC, // 166\r
+ SUCCUBUS_SHOT1PIC, // 167\r
+ // Lump Start\r
+ TREE_IDLEPIC, // 168\r
+ TREE_AWAKENINGPIC, // 169\r
+ TREE_WALK1PIC, // 170\r
+ TREE_WALK2PIC, // 171\r
+ TREE_ATTACK1PIC, // 172\r
+ TREE_ATTACK2PIC, // 173\r
+ TREE_ATTACK3PIC, // 174\r
+ TREE_DEATH1PIC, // 175\r
+ TREE_DEATH2PIC, // 176\r
+ TREE_DEATH3PIC, // 177\r
+ // Lump Start\r
+ DRAGON_BUBBLES1PIC, // 178\r
+ DRAGON_BUBBLES2PIC, // 179\r
+ DRAGON_EYESPIC, // 180\r
+ DRAGON_RISE1PIC, // 181\r
+ DRAGON_RISE2PIC, // 182\r
+ DRAGON_WALK1PIC, // 183\r
+ DRAGON_WALK2PIC, // 184\r
+ DRAGON_WALK3PIC, // 185\r
+ DRAGON_WALK4PIC, // 186\r
+ DRAGON_ATTACK1PIC, // 187\r
+ DRAGON_ATTACK2PIC, // 188\r
+ DRAGON_ATTACK3PIC, // 189\r
+ DRAGON_OUCHPIC, // 190\r
+ DRAGON_DEATH1PIC, // 191\r
+ DRAGON_DEATH2PIC, // 192\r
+ DRAGON_DEATH3PIC, // 193\r
+ // Lump Start\r
+ BUNNY_LEFT1PIC, // 194\r
+ BUNNY_LEFT2PIC, // 195\r
+ BUNNY_RIGHT1PIC, // 196\r
+ BUNNY_RIGHT2PIC, // 197\r
+ BUNNY_META1PIC, // 198\r
+ BUNNY_META2PIC, // 199\r
+ BUNNY_WALK1PIC, // 200\r
+ BUNNY_WALK2PIC, // 201\r
+ BUNNY_OUCHPIC, // 202\r
+ BUNNY_DEATH1PIC, // 203\r
+ BUNNY_DEATH2PIC, // 204\r
+ // Lump Start\r
+ ARCH1PIC, // 205\r
+ // Lump Start\r
+ ARCH2PIC, // 206\r
+ // Lump Start\r
+ ARCH3PIC, // 207\r
+ // Lump Start\r
+ ARCH4PIC, // 208\r
+ // Lump Start\r
+ ARCH5PIC, // 209\r
+ // Lump Start\r
+ ARCH6PIC, // 210\r
+ // Lump Start\r
+ ARCH7PIC, // 211\r
+ // Lump Start\r
+ ARCH8PIC, // 212\r
+ // Lump Start\r
+ ARCH9PIC, // 213\r
+ // Lump Start\r
+ ARCH10PIC, // 214\r
+ // Lump Start\r
+ ARCH11PIC, // 215\r
+ // Lump Start\r
+ ARCH12PIC, // 216\r
+ // Lump Start\r
+ ARCH13PIC, // 217\r
+ // Lump Start\r
+ ANT_HILLPIC, // 218\r
+ // Lump Start\r
+ COLUMNPIC, // 219\r
+ // Lump Start\r
+ SULPHUR_GAS_1PIC, // 220\r
+ SULPHUR_GAS_2PIC, // 221\r
+ SULPHUR_GAS_3PIC, // 222\r
+ // Lump Start\r
+ FIRE_POT_1PIC, // 223\r
+ FIRE_POT_2PIC, // 224\r
+ // Lump Start\r
+ SKEL_HANGPIC, // 225\r
+ // Lump Start\r
+ FORCE_FIELD_1PIC, // 226\r
+ FORCE_FIELD_2PIC, // 227\r
+ FORCE_FIELD_3PIC, // 228\r
+ FORCE_FIELD_4PIC, // 229\r
+ // Lump Start\r
+ WFOUNTAINPIC, // 230\r
+ FIRSTWALLPIC, // 231\r
+ CRYSTAL_LIGHT_1PIC, // 232\r
+ CRYSTAL_LIGHT_2PIC, // 233\r
+ CRYSTAL_LIGHT_3PIC, // 234\r
+ CRYSTAL_LIGHT_4PIC, // 235\r
+ CRYSTAL_DARK_1PIC, // 236\r
+ CRYSTAL_DARK_2PIC, // 237\r
+ CRYSTAL_DARK_3PIC, // 238\r
+ CRYSTAL_DARK_4PIC, // 239\r
+ FIRE_WALL_1PIC, // 240\r
+ FIRE_WALL_2PIC, // 241\r
+ FIRE_WALL_3PIC, // 242\r
+ FIRE_WALL_4PIC, // 243\r
+ BRN_STONE_GATEPIC, // 244\r
+ BRN_STONE_WALL_1PIC, // 245\r
+ BRN_STONE_WALL_2PIC, // 246\r
+ KUDZU_LIGHT_WALLPIC, // 247\r
+ KUDZU_DARK_WALLPIC, // 248\r
+ HEDGE_WALLPIC, // 249\r
+ HEDGE_EYESPIC, // 250\r
+ BRN_WINDOW_LIGHTPIC, // 251\r
+ ALTAR_LEFTPIC, // 252\r
+ ALTAR_RIGHTPIC, // 253\r
+ GRAY_LIGHT_WALLPIC, // 254\r
+ GRAY_DARK_WALLPIC, // 255\r
+ GRAY_LIGHT_SIGNPIC, // 256\r
+ GRAY_DARK_SIGNPIC, // 257\r
+ MANICLE_LIGHT_BLOODYPIC, // 258\r
+ MANICLE_DARK_BLOODYPIC, // 259\r
+ LIGHT_CURTAIN_WINDOWPIC, // 260\r
+ LIGHT_CURTAIN_WALLPIC, // 261\r
+ DARK_CURTAIN_WINDOWPIC, // 262\r
+ DARK_CURTAIN_WALLPIC, // 263\r
+ BRN_LIGHT_SIGNPIC, // 264\r
+ BRN_DARK_SIGNPIC, // 265\r
+ LIGHT_STONE_WALLPIC, // 266\r
+ DARK_STONE_WALLPIC, // 267\r
+ BRN_FLAGSTONE_LIGHT_2PIC, // 268\r
+ BRN_FLAGSTONE_DARK_2PIC, // 269\r
+ RUST_METAL_LIGHTPIC, // 270\r
+ RUST_METAL_DARKPIC, // 271\r
+ GRAY_METAL_LIGHTPIC, // 272\r
+ GRAY_METAL_DARKPIC, // 273\r
+ WEAK_STONE_LIGHTPIC, // 274\r
+ WEAK_STONE_DARKPIC, // 275\r
+ WEAK_GRAY_RFGSTN_LIGHTPIC, // 276\r
+ WEAK_GRAY_RFGSTN_DARKPIC, // 277\r
+ WEAK_CRYSTAL_LIGHTPIC, // 278\r
+ WEAK_CRYSTAL_DARKPIC, // 279\r
+ RED_MUD_LIGHTPIC, // 280\r
+ BRN_MUD_DARKPIC, // 281\r
+ RED_MUD_WEAK_LIGHTPIC, // 282\r
+ BRN_MUD_WEAK_DARKPIC, // 283\r
+ HORN_DOORPIC, // 284\r
+ CLOSED_DOOR_1PIC, // 285\r
+ DOOR_2PIC, // 286\r
+ WATER_LIGHT_WEAK_1PIC, // 287\r
+ WATER_LIGHT_WEAK_2PIC, // 288\r
+ WATER_LIGHT_WEAK_3PIC, // 289\r
+ WATER_DARK_WEAK_1PIC, // 290\r
+ WATER_DARK_WEAK_2PIC, // 291\r
+ WATER_DARK_WEAK_3PIC, // 292\r
+ WATER_LIGHT_1PIC, // 293\r
+ WATER_LIGHT_2PIC, // 294\r
+ WATER_LIGHT_3PIC, // 295\r
+ WATER_DARK_1PIC, // 296\r
+ WATER_DARK_2PIC, // 297\r
+ WATER_DARK_3PIC, // 298\r
+ TROLL_LIGHT_STONEPIC, // 299\r
+ TROLL_DARK_STONEPIC, // 300\r
+ TROLL_BLOODY_LT_STONEPIC, // 301\r
+ TROLL_BLOODY_DK_STONEPIC, // 302\r
+ LIGHT_BREATH_1PIC, // 303\r
+ LIGHT_BREATH_2PIC, // 304\r
+ LIGHT_BREATH_3PIC, // 305\r
+ DARK_BREATH_1PIC, // 306\r
+ DARK_BREATH_2PIC, // 307\r
+ DARK_BREATH_3PIC, // 308\r
+ EXP_WALL_1PIC, // 309\r
+ EXP_WALL_2PIC, // 310\r
+ EXP_WALL_3PIC, // 311\r
+ WATER_EXP_WALL_1PIC, // 312\r
+ WATER_EXP_WALL_2PIC, // 313\r
+ WATER_EXP_WALL_3PIC, // 314\r
+ W_GEN_DOOR1PIC, // 315\r
+ W_GEN_DOOR2PIC, // 316\r
+ W_CRYSTAL_DOORPIC, // 317\r
+ DMG_BRN_FSTN_LTPIC, // 318\r
+ DMG_BRN_FSTN_DKPIC, // 319\r
+ DMG_FIN_FSTN_LTPIC, // 320\r
+ DMG_FIN_FSTN_DKPIC, // 321\r
+ STEEL_DOOR1PIC, // 322\r
+ STEEL_DOOR2PIC, // 323\r
+ BRN_WINDOW_DARKPIC, // 324\r
+ GRY_DOOR_LTPIC, // 325\r
+ GRY_DOOR_DKPIC, // 326\r
+ BRN_DOOR_LTPIC, // 327\r
+ BRN_DOOR_DKPIC, // 328\r
+ GRY_FGSTN_LTPIC, // 329\r
+ GRY_FGSTN_DKPIC, // 330\r
+ KUDZU_WEAK_LIGHTPIC, // 331\r
+ KUDZU_WEAK_DARKPIC, // 332\r
+ LT_SKEL1PIC, // 333\r
+ DK_SKEL1PIC, // 334\r
+ LT_SKEL2PIC, // 335\r
+ DK_SKEL2PIC, // 336\r
+ MANICLE_LIGHT_WALLPIC, // 337\r
+ MANICLE_DARK_WALLPIC, // 338\r
+ TAP_1PIC, // 339\r
+ TAP_2PIC, // 340\r
+ TAP_3PIC, // 341\r
+ TAP_4PIC, // 342\r
+ TAP_5PIC, // 343\r
+ FINALWALLPIC, // 344\r
+ WATER_DOOR1_PIC, // 345\r
+ WATER_DOOR2_PIC, // 346\r
+ LASTWALLPIC, // 347\r
+\r
+ HAND1PICM=348,\r
+\r
+ NORTHICONSPR=349,\r
+\r
+ LEVEL1TEXT=640,\r
+ LEVEL2TEXT, // 641\r
+ LEVEL3TEXT, // 642\r
+ LEVEL4TEXT, // 643\r
+ LEVEL5TEXT, // 644\r
+ LEVEL6TEXT, // 645\r
+ LEVEL7TEXT, // 646\r
+ LEVEL8TEXT, // 647\r
+ LEVEL9TEXT, // 648\r
+ LEVEL10TEXT, // 649\r
+ LEVEL11TEXT, // 650\r
+ LEVEL12TEXT, // 651\r
+ LEVEL13TEXT, // 652\r
+ LEVEL14TEXT, // 653\r
+ LEVEL15TEXT, // 654\r
+ LEVEL16TEXT, // 655\r
+ LEVEL17TEXT, // 656\r
+ PIRACY, // 657\r
+ ENUMEND\r
+ } graphicnums;\r
+\r
+//\r
+// Data LUMPs\r
+//\r
+#define SKELDUDE_LUMP_START 22\r
+#define SKELDUDE_LUMP_END 31\r
+\r
+#define TOMBSTONES_LUMP_START 32\r
+#define TOMBSTONES_LUMP_END 34\r
+\r
+#define OBJ_WARP_LUMP_START 35\r
+#define OBJ_WARP_LUMP_END 38\r
+\r
+#define EYE_LUMP_START 39\r
+#define EYE_LUMP_END 49\r
+\r
+#define ZOMBIE_LUMP_START 50\r
+#define ZOMBIE_LUMP_END 61\r
+\r
+#define BOLT_LUMP_START 62\r
+#define BOLT_LUMP_END 64\r
+\r
+#define NUKE_LUMP_START 65\r
+#define NUKE_LUMP_END 67\r
+\r
+#define TIME_LUMP_START 68\r
+#define TIME_LUMP_END 69\r
+\r
+#define O_WATER_CHEST_LUMP_START 70\r
+#define O_WATER_CHEST_LUMP_END 71\r
+\r
+#define POTION_LUMP_START 72\r
+#define POTION_LUMP_END 72\r
+\r
+#define RKEY_LUMP_START 73\r
+#define RKEY_LUMP_END 73\r
+\r
+#define YKEY_LUMP_START 74\r
+#define YKEY_LUMP_END 74\r
+\r
+#define GKEY_LUMP_START 75\r
+#define GKEY_LUMP_END 75\r
+\r
+#define BKEY_LUMP_START 76\r
+#define BKEY_LUMP_END 76\r
+\r
+#define RGEM_LUMP_START 77\r
+#define RGEM_LUMP_END 78\r
+\r
+#define GGEM_LUMP_START 79\r
+#define GGEM_LUMP_END 80\r
+\r
+#define BGEM_LUMP_START 81\r
+#define BGEM_LUMP_END 82\r
+\r
+#define YGEM_LUMP_START 83\r
+#define YGEM_LUMP_END 84\r
+\r
+#define PGEM_LUMP_START 85\r
+#define PGEM_LUMP_END 86\r
+\r
+#define CHEST_LUMP_START 87\r
+#define CHEST_LUMP_END 87\r
+\r
+#define PLAYER_LUMP_START 88\r
+#define PLAYER_LUMP_END 92\r
+\r
+#define REDDEMON_LUMP_START 93\r
+#define REDDEMON_LUMP_END 103\r
+\r
+#define MAGE_LUMP_START 104\r
+#define MAGE_LUMP_END 109\r
+\r
+#define BAT_LUMP_START 110\r
+#define BAT_LUMP_END 115\r
+\r
+#define GREL_LUMP_START 116\r
+#define GREL_LUMP_END 126\r
+\r
+#define GODESS_LUMP_START 127\r
+#define GODESS_LUMP_END 136\r
+\r
+#define ANT_LUMP_START 137\r
+#define ANT_LUMP_END 145\r
+\r
+#define FATDEMON_LUMP_START 146\r
+#define FATDEMON_LUMP_END 157\r
+\r
+#define SUCCUBUS_LUMP_START 158\r
+#define SUCCUBUS_LUMP_END 167\r
+\r
+#define TREE_LUMP_START 168\r
+#define TREE_LUMP_END 177\r
+\r
+#define DRAGON_LUMP_START 178\r
+#define DRAGON_LUMP_END 193\r
+\r
+#define BUNNY_LUMP_START 194\r
+#define BUNNY_LUMP_END 204\r
+\r
+#define ARCH1_LUMP_START 205\r
+#define ARCH1_LUMP_END 205\r
+\r
+#define ARCH2_LUMP_START 206\r
+#define ARCH2_LUMP_END 206\r
+\r
+#define ARCH3_LUMP_START 207\r
+#define ARCH3_LUMP_END 207\r
+\r
+#define ARCH4_LUMP_START 208\r
+#define ARCH4_LUMP_END 208\r
+\r
+#define ARCH5_LUMP_START 209\r
+#define ARCH5_LUMP_END 209\r
+\r
+#define ARCH6_LUMP_START 210\r
+#define ARCH6_LUMP_END 210\r
+\r
+#define ARCH7_LUMP_START 211\r
+#define ARCH7_LUMP_END 211\r
+\r
+#define ARCH8_LUMP_START 212\r
+#define ARCH8_LUMP_END 212\r
+\r
+#define ARCH9_LUMP_START 213\r
+#define ARCH9_LUMP_END 213\r
+\r
+#define ARCH10_LUMP_START 214\r
+#define ARCH10_LUMP_END 214\r
+\r
+#define ARCH11_LUMP_START 215\r
+#define ARCH11_LUMP_END 215\r
+\r
+#define ARCH12_LUMP_START 216\r
+#define ARCH12_LUMP_END 216\r
+\r
+#define ARCH13_LUMP_START 217\r
+#define ARCH13_LUMP_END 217\r
+\r
+#define ANTHILL_LUMP_START 218\r
+#define ANTHILL_LUMP_END 218\r
+\r
+#define COLUMN_LUMP_START 219\r
+#define COLUMN_LUMP_END 219\r
+\r
+#define SULPHURGAS_LUMP_START 220\r
+#define SULPHURGAS_LUMP_END 222\r
+\r
+#define FIREPOT_LUMP_START 223\r
+#define FIREPOT_LUMP_END 224\r
+\r
+#define SKELHANG_LUMP_START 225\r
+#define SKELHANG_LUMP_END 225\r
+\r
+#define FORCEFIELD_LUMP_START 226\r
+#define FORCEFIELD_LUMP_END 229\r
+\r
+#define FOUNTAIN_LUMP_START 230\r
+#define FOUNTAIN_LUMP_END 230\r
+\r
+\r
+//\r
+// Amount of each data item\r
+//\r
+#define NUMCHUNKS 658\r
+#define NUMFONT 1\r
+#define NUMFONTM 0\r
+#define NUMPICS 344\r
+#define NUMPICM 1\r
+#define NUMSPRITES 1\r
+#define NUMTILE8 108\r
+#define NUMTILE8M 36\r
+#define NUMTILE16 216\r
+#define NUMTILE16M 72\r
+#define NUMTILE32 0\r
+#define NUMTILE32M 0\r
+#define NUMEXTERNS 18\r
+//\r
+// File offsets for data items\r
+//\r
+#define STRUCTPIC 0\r
+#define STRUCTPICM 1\r
+#define STRUCTSPRITE 2\r
+\r
+#define STARTFONT 3\r
+#define STARTFONTM 4\r
+#define STARTPICS 4\r
+#define STARTPICM 348\r
+#define STARTSPRITES 349\r
+#define STARTTILE8 350\r
+#define STARTTILE8M 351\r
+#define STARTTILE16 352\r
+#define STARTTILE16M 568\r
+#define STARTTILE32 640\r
+#define STARTTILE32M 640\r
+#define STARTEXTERNS 640\r
+\r
+//\r
+// Thank you for using IGRAB!\r
+//\r
--- /dev/null
+;\r
+; Equates for all .ASM files\r
+;\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+INCLUDE "GFXE_ARM.EQU"\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+CGAGR = 1\r
+EGAGR = 2\r
+VGAGR = 3\r
+\r
+GRMODE = EGAGR\r
+PROFILE = 0 ; 1=keep stats on tile drawing\r
+\r
+SC_INDEX = 03C4h\r
+SC_RESET = 0\r
+SC_CLOCK = 1\r
+SC_MAPMASK = 2\r
+SC_CHARMAP = 3\r
+SC_MEMMODE = 4\r
+\r
+CRTC_INDEX = 03D4h\r
+CRTC_H_TOTAL = 0\r
+CRTC_H_DISPEND = 1\r
+CRTC_H_BLANK = 2\r
+CRTC_H_ENDBLANK = 3\r
+CRTC_H_RETRACE = 4\r
+CRTC_H_ENDRETRACE = 5\r
+CRTC_V_TOTAL = 6\r
+CRTC_OVERFLOW = 7\r
+CRTC_ROWSCAN = 8\r
+CRTC_MAXSCANLINE = 9\r
+CRTC_CURSORSTART = 10\r
+CRTC_CURSOREND = 11\r
+CRTC_STARTHIGH = 12\r
+CRTC_STARTLOW = 13\r
+CRTC_CURSORHIGH = 14\r
+CRTC_CURSORLOW = 15\r
+CRTC_V_RETRACE = 16\r
+CRTC_V_ENDRETRACE = 17\r
+CRTC_V_DISPEND = 18\r
+CRTC_OFFSET = 19\r
+CRTC_UNDERLINE = 20\r
+CRTC_V_BLANK = 21\r
+CRTC_V_ENDBLANK = 22\r
+CRTC_MODE = 23\r
+CRTC_LINECOMPARE = 24\r
+\r
+\r
+GC_INDEX = 03CEh\r
+GC_SETRESET = 0\r
+GC_ENABLESETRESET = 1\r
+GC_COLORCOMPARE = 2\r
+GC_DATAROTATE = 3\r
+GC_READMAP = 4\r
+GC_MODE = 5\r
+GC_MISCELLANEOUS = 6\r
+GC_COLORDONTCARE = 7\r
+GC_BITMASK = 8\r
+\r
+ATR_INDEX = 03c0h\r
+ATR_MODE = 16\r
+ATR_OVERSCAN = 17\r
+ATR_COLORPLANEENABLE = 18\r
+ATR_PELPAN = 19\r
+ATR_COLORSELECT = 20\r
+\r
+STATUS_REGISTER_1 = 03dah\r
+\r
+\r
+MACRO WORDOUT\r
+ out dx,ax\r
+ENDM\r
+\r
+if 0\r
+\r
+MACRO WORDOUT\r
+ out dx,al\r
+ inc dx\r
+ xchg al,ah\r
+ out dx,al\r
+ dec dx\r
+ xchg al,ah\r
+ENDM\r
+\r
+endif\r
+\r
+UPDATEWIDE = 22\r
+UPDATEHIGH = 13 ; hack for catacombs\r
+\r
+;\r
+; tile info offsets from segment tinf\r
+;\r
+\r
+SPEED = 402\r
+ANIM = (SPEED+NUMTILE16)\r
+\r
+NORTHWALL = (ANIM+NUMTILE16)\r
+EASTWALL = (NORTHWALL+NUMTILE16M)\r
+SOUTHWALL = (EASTWALL+NUMTILE16M)\r
+WESTWALL = (SOUTHWALL+NUMTILE16M)\r
+MANIM = (WESTWALL+NUMTILE16M)\r
+INTILE = (MANIM+NUMTILE16M)\r
+MSPEED = (INTILE+NUMTILE16M)\r
+\r
+IFE GRMODE-EGAGR\r
+SCREENWIDTH = 40\r
+ENDIF\r
+IFE GRMODE-CGAGR\r
+SCREENWIDTH = 128\r
+ENDIF\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_CA.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+Id Software Caching Manager\r
+---------------------------\r
+\r
+Must be started BEFORE the memory manager, because it needs to get the headers\r
+loaded into the data segment\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "LIB_HEAD.H"\r
+#pragma hdrstop\r
+//#include "ID_STRS.H"\r
+\r
+#pragma warn -pro\r
+#pragma warn -use\r
+\r
+#define THREEBYTEGRSTARTS\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+typedef struct\r
+{\r
+ unsigned bit0,bit1; // 0-255 is a character, > is a pointer to a node\r
+} huffnode;\r
+\r
+\r
+typedef struct\r
+{\r
+ unsigned RLEWtag;\r
+ long headeroffsets[100];\r
+ byte tileinfo[];\r
+} mapfiletype;\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+byte _seg *tinf;\r
+int mapon;\r
+\r
+unsigned _seg *mapsegs[3];\r
+maptype _seg *mapheaderseg[NUMMAPS];\r
+byte _seg *audiosegs[NUMSNDCHUNKS];\r
+void _seg *grsegs[NUMCHUNKS];\r
+\r
+byte far grneeded[NUMCHUNKS];\r
+byte ca_levelbit,ca_levelnum;\r
+\r
+int profilehandle,debughandle;\r
+\r
+void (*drawcachebox) (char *title, unsigned numcache);\r
+void (*updatecachebox) (void);\r
+void (*finishcachebox) (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern long far CGAhead;\r
+extern long far EGAhead;\r
+extern byte CGAdict;\r
+extern byte EGAdict;\r
+extern byte far maphead;\r
+extern byte mapdict;\r
+extern byte far audiohead;\r
+extern byte audiodict;\r
+\r
+\r
+long _seg *grstarts; // array of offsets in egagraph, -1 for sparse\r
+long _seg *audiostarts; // array of offsets in audio / audiot\r
+\r
+#ifdef GRHEADERLINKED\r
+huffnode *grhuffman;\r
+#else\r
+huffnode grhuffman[255];\r
+#endif\r
+\r
+#ifdef AUDIOHEADERLINKED\r
+huffnode *audiohuffman;\r
+#else\r
+huffnode audiohuffman[255];\r
+#endif\r
+\r
+\r
+int grhandle; // handle to EGAGRAPH\r
+int maphandle; // handle to MAPTEMP / GAMEMAPS\r
+int audiohandle; // handle to AUDIOT / AUDIO\r
+\r
+long chunkcomplen,chunkexplen;\r
+\r
+SDMode oldsoundmode;\r
+\r
+\r
+\r
+void CAL_DialogDraw (char *title,unsigned numcache);\r
+void CAL_DialogUpdate (void);\r
+void CAL_DialogFinish (void);\r
+void CAL_CarmackExpand (unsigned far *source, unsigned far *dest,\r
+ unsigned length);\r
+\r
+\r
+#ifdef THREEBYTEGRSTARTS\r
+#define FILEPOSSIZE 3\r
+//#define GRFILEPOS(c) (*(long far *)(((byte far *)grstarts)+(c)*3)&0xffffff)\r
+long GRFILEPOS(int c)\r
+{\r
+ long value;\r
+ int offset;\r
+\r
+ offset = c*3;\r
+\r
+ value = *(long far *)(((byte far *)grstarts)+offset);\r
+\r
+ value &= 0x00ffffffl;\r
+\r
+ if (value == 0xffffffl)\r
+ value = -1;\r
+\r
+ return value;\r
+};\r
+#else\r
+#define FILEPOSSIZE 4\r
+#define GRFILEPOS(c) (grstarts[c])\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOW LEVEL ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+============================\r
+=\r
+= CA_OpenDebug / CA_CloseDebug\r
+=\r
+= Opens a binary file with the handle "debughandle"\r
+=\r
+============================\r
+*/\r
+\r
+void CA_OpenDebug (void)\r
+{\r
+ unlink ("DEBUG.TXT");\r
+ debughandle = open("DEBUG.TXT", O_CREAT | O_WRONLY | O_TEXT);\r
+}\r
+\r
+void CA_CloseDebug (void)\r
+{\r
+ close (debughandle);\r
+}\r
+\r
+\r
+\r
+/*\r
+============================\r
+=\r
+= CAL_GetGrChunkLength\r
+=\r
+= Gets the length of an explicit length chunk (not tiles)\r
+= The file pointer is positioned so the compressed data can be read in next.\r
+=\r
+============================\r
+*/\r
+\r
+void CAL_GetGrChunkLength (int chunk)\r
+{\r
+ lseek(grhandle,GRFILEPOS(chunk),SEEK_SET);\r
+ read(grhandle,&chunkexplen,sizeof(chunkexplen));\r
+ chunkcomplen = GRFILEPOS(chunk+1)-GRFILEPOS(chunk)-4;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_FarRead\r
+=\r
+= Read from a file to a far pointer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_FarRead (int handle, byte far *dest, long length)\r
+{\r
+ if (length>0xffffl)\r
+ Quit ("CA_FarRead doesn't support 64K reads yet!");\r
+\r
+asm push ds\r
+asm mov bx,[handle]\r
+asm mov cx,[WORD PTR length]\r
+asm mov dx,[WORD PTR dest]\r
+asm mov ds,[WORD PTR dest+2]\r
+asm mov ah,0x3f // READ w/handle\r
+asm int 21h\r
+asm pop ds\r
+asm jnc good\r
+ errno = _AX;\r
+ return false;\r
+good:\r
+asm cmp ax,[WORD PTR length]\r
+asm je done\r
+ errno = EINVFMT; // user manager knows this is bad read\r
+ return false;\r
+done:\r
+ return true;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_SegWrite\r
+=\r
+= Write from a file to a far pointer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_FarWrite (int handle, byte far *source, long length)\r
+{\r
+ if (length>0xffffl)\r
+ Quit ("CA_FarWrite doesn't support 64K reads yet!");\r
+\r
+asm push ds\r
+asm mov bx,[handle]\r
+asm mov cx,[WORD PTR length]\r
+asm mov dx,[WORD PTR source]\r
+asm mov ds,[WORD PTR source+2]\r
+asm mov ah,0x40 // WRITE w/handle\r
+asm int 21h\r
+asm pop ds\r
+asm jnc good\r
+ errno = _AX;\r
+ return false;\r
+good:\r
+asm cmp ax,[WORD PTR length]\r
+asm je done\r
+ errno = ENOMEM; // user manager knows this is bad write\r
+ return false;\r
+\r
+done:\r
+ return true;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_ReadFile\r
+=\r
+= Reads a file into an allready allocated buffer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_ReadFile (char *filename, memptr *ptr)\r
+{\r
+ int handle;\r
+ long size;\r
+\r
+ if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ return false;\r
+\r
+ size = filelength (handle);\r
+ if (!CA_FarRead (handle,*ptr,size))\r
+ {\r
+ close (handle);\r
+ return false;\r
+ }\r
+ close (handle);\r
+ return true;\r
+}\r
+\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_LoadFile\r
+=\r
+= Allocate space for and load a file\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_LoadFile (char *filename, memptr *ptr)\r
+{\r
+ int handle;\r
+ long size;\r
+\r
+ if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ return false;\r
+\r
+ size = filelength (handle);\r
+ MM_GetPtr (ptr,size);\r
+ if (!CA_FarRead (handle,*ptr,size))\r
+ {\r
+ close (handle);\r
+ return false;\r
+ }\r
+ close (handle);\r
+ return true;\r
+}\r
+\r
+/*\r
+============================================================================\r
+\r
+ COMPRESSION routines, see JHUFF.C for more\r
+\r
+============================================================================\r
+*/\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= CAL_OptimizeNodes\r
+=\r
+= Goes through a huffman table and changes the 256-511 node numbers to the\r
+= actular address of the node. Must be called before CAL_HuffExpand\r
+=\r
+===============\r
+*/\r
+\r
+void CAL_OptimizeNodes (huffnode *table)\r
+{\r
+ huffnode *node;\r
+ int i;\r
+\r
+ node = table;\r
+\r
+ for (i=0;i<255;i++)\r
+ {\r
+ if (node->bit0 >= 256)\r
+ node->bit0 = (unsigned)(table+(node->bit0-256));\r
+ if (node->bit1 >= 256)\r
+ node->bit1 = (unsigned)(table+(node->bit1-256));\r
+ node++;\r
+ }\r
+}\r
+\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_HuffExpand\r
+=\r
+= Length is the length of the EXPANDED data\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_HuffExpand (byte huge *source, byte huge *dest,\r
+ long length,huffnode *hufftable)\r
+{\r
+// unsigned bit,byte,node,code;\r
+ unsigned sourceseg,sourceoff,destseg,destoff,endoff;\r
+ huffnode *headptr;\r
+// huffnode *nodeon;\r
+\r
+ headptr = hufftable+254; // head node is allways node 254\r
+\r
+ source++; // normalize\r
+ source--;\r
+ dest++;\r
+ dest--;\r
+\r
+ sourceseg = FP_SEG(source);\r
+ sourceoff = FP_OFF(source);\r
+ destseg = FP_SEG(dest);\r
+ destoff = FP_OFF(dest);\r
+ endoff = destoff+length;\r
+\r
+//\r
+// ds:si source\r
+// es:di dest\r
+// ss:bx node pointer\r
+//\r
+\r
+ if (length <0xfff0)\r
+ {\r
+\r
+//--------------------------\r
+// expand less than 64k of data\r
+//--------------------------\r
+\r
+asm mov bx,[headptr]\r
+\r
+asm mov si,[sourceoff]\r
+asm mov di,[destoff]\r
+asm mov es,[destseg]\r
+asm mov ds,[sourceseg]\r
+asm mov ax,[endoff]\r
+\r
+asm mov ch,[si] // load first byte\r
+asm inc si\r
+asm mov cl,1\r
+\r
+expandshort:\r
+asm test ch,cl // bit set?\r
+asm jnz bit1short\r
+asm mov dx,[ss:bx] // take bit0 path from node\r
+asm shl cl,1 // advance to next bit position\r
+asm jc newbyteshort\r
+asm jnc sourceupshort\r
+\r
+bit1short:\r
+asm mov dx,[ss:bx+2] // take bit1 path\r
+asm shl cl,1 // advance to next bit position\r
+asm jnc sourceupshort\r
+\r
+newbyteshort:\r
+asm mov ch,[si] // load next byte\r
+asm inc si\r
+asm mov cl,1 // back to first bit\r
+\r
+sourceupshort:\r
+asm or dh,dh // if dx<256 its a byte, else move node\r
+asm jz storebyteshort\r
+asm mov bx,dx // next node = (huffnode *)code\r
+asm jmp expandshort\r
+\r
+storebyteshort:\r
+asm mov [es:di],dl\r
+asm inc di // write a decopmpressed byte out\r
+asm mov bx,[headptr] // back to the head node for next bit\r
+\r
+asm cmp di,ax // done?\r
+asm jne expandshort\r
+ }\r
+ else\r
+ {\r
+\r
+//--------------------------\r
+// expand more than 64k of data\r
+//--------------------------\r
+\r
+ length--;\r
+\r
+asm mov bx,[headptr]\r
+asm mov cl,1\r
+\r
+asm mov si,[sourceoff]\r
+asm mov di,[destoff]\r
+asm mov es,[destseg]\r
+asm mov ds,[sourceseg]\r
+\r
+asm lodsb // load first byte\r
+\r
+expand:\r
+asm test al,cl // bit set?\r
+asm jnz bit1\r
+asm mov dx,[ss:bx] // take bit0 path from node\r
+asm jmp gotcode\r
+bit1:\r
+asm mov dx,[ss:bx+2] // take bit1 path\r
+\r
+gotcode:\r
+asm shl cl,1 // advance to next bit position\r
+asm jnc sourceup\r
+asm lodsb\r
+asm cmp si,0x10 // normalize ds:si\r
+asm jb sinorm\r
+asm mov cx,ds\r
+asm inc cx\r
+asm mov ds,cx\r
+asm xor si,si\r
+sinorm:\r
+asm mov cl,1 // back to first bit\r
+\r
+sourceup:\r
+asm or dh,dh // if dx<256 its a byte, else move node\r
+asm jz storebyte\r
+asm mov bx,dx // next node = (huffnode *)code\r
+asm jmp expand\r
+\r
+storebyte:\r
+asm mov [es:di],dl\r
+asm inc di // write a decopmpressed byte out\r
+asm mov bx,[headptr] // back to the head node for next bit\r
+\r
+asm cmp di,0x10 // normalize es:di\r
+asm jb dinorm\r
+asm mov dx,es\r
+asm inc dx\r
+asm mov es,dx\r
+asm xor di,di\r
+dinorm:\r
+\r
+asm sub [WORD PTR ss:length],1\r
+asm jnc expand\r
+asm dec [WORD PTR ss:length+2]\r
+asm jns expand // when length = ffff ffff, done\r
+\r
+ }\r
+\r
+asm mov ax,ss\r
+asm mov ds,ax\r
+\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_CarmackExpand\r
+=\r
+= Length is the length of the EXPANDED data\r
+=\r
+======================\r
+*/\r
+\r
+#define NEARTAG 0xa7\r
+#define FARTAG 0xa8\r
+\r
+void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, unsigned length)\r
+{\r
+ unsigned ch,chhigh,count,offset;\r
+ unsigned far *copyptr, far *inptr, far *outptr;\r
+\r
+ length/=2;\r
+\r
+ inptr = source;\r
+ outptr = dest;\r
+\r
+ while (length)\r
+ {\r
+ ch = *inptr++;\r
+ chhigh = ch>>8;\r
+ if (chhigh == NEARTAG)\r
+ {\r
+ count = ch&0xff;\r
+ if (!count)\r
+ { // have to insert a word containing the tag byte\r
+ ch |= *((unsigned char far *)inptr)++;\r
+ *outptr++ = ch;\r
+ length--;\r
+ }\r
+ else\r
+ {\r
+ offset = *((unsigned char far *)inptr)++;\r
+ copyptr = outptr - offset;\r
+ length -= count;\r
+ while (count--)\r
+ *outptr++ = *copyptr++;\r
+ }\r
+ }\r
+ else if (chhigh == FARTAG)\r
+ {\r
+ count = ch&0xff;\r
+ if (!count)\r
+ { // have to insert a word containing the tag byte\r
+ ch |= *((unsigned char far *)inptr)++;\r
+ *outptr++ = ch;\r
+ length --;\r
+ }\r
+ else\r
+ {\r
+ offset = *inptr++;\r
+ copyptr = dest + offset;\r
+ length -= count;\r
+ while (count--)\r
+ *outptr++ = *copyptr++;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ *outptr++ = ch;\r
+ length --;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_RLEWcompress\r
+=\r
+======================\r
+*/\r
+\r
+long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest,\r
+ unsigned rlewtag)\r
+{\r
+ long complength;\r
+ unsigned value,count,i;\r
+ unsigned huge *start,huge *end;\r
+\r
+ start = dest;\r
+\r
+ end = source + (length+1)/2;\r
+\r
+//\r
+// compress it\r
+//\r
+ do\r
+ {\r
+ count = 1;\r
+ value = *source++;\r
+ while (*source == value && source<end)\r
+ {\r
+ count++;\r
+ source++;\r
+ }\r
+ if (count>3 || value == rlewtag)\r
+ {\r
+ //\r
+ // send a tag / count / value string\r
+ //\r
+ *dest++ = rlewtag;\r
+ *dest++ = count;\r
+ *dest++ = value;\r
+ }\r
+ else\r
+ {\r
+ //\r
+ // send word without compressing\r
+ //\r
+ for (i=1;i<=count;i++)\r
+ *dest++ = value;\r
+ }\r
+\r
+ } while (source<end);\r
+\r
+ complength = 2*(dest-start);\r
+ return complength;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_RLEWexpand\r
+= length is EXPANDED length\r
+=\r
+======================\r
+*/\r
+\r
+void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length,\r
+ unsigned rlewtag)\r
+{\r
+// unsigned value,count,i;\r
+ unsigned huge *end;\r
+ unsigned sourceseg,sourceoff,destseg,destoff,endseg,endoff;\r
+\r
+\r
+//\r
+// expand it\r
+//\r
+#if 0\r
+ do\r
+ {\r
+ value = *source++;\r
+ if (value != rlewtag)\r
+ //\r
+ // uncompressed\r
+ //\r
+ *dest++=value;\r
+ else\r
+ {\r
+ //\r
+ // compressed string\r
+ //\r
+ count = *source++;\r
+ value = *source++;\r
+ for (i=1;i<=count;i++)\r
+ *dest++ = value;\r
+ }\r
+ } while (dest<end);\r
+#endif\r
+\r
+ end = dest + (length)/2;\r
+ sourceseg = FP_SEG(source);\r
+ sourceoff = FP_OFF(source);\r
+ destseg = FP_SEG(dest);\r
+ destoff = FP_OFF(dest);\r
+ endseg = FP_SEG(end);\r
+ endoff = FP_OFF(end);\r
+\r
+\r
+//\r
+// ax = source value\r
+// bx = tag value\r
+// cx = repeat counts\r
+// dx = scratch\r
+//\r
+// NOTE: A repeat count that produces 0xfff0 bytes can blow this!\r
+//\r
+\r
+asm mov bx,rlewtag\r
+asm mov si,sourceoff\r
+asm mov di,destoff\r
+asm mov es,destseg\r
+asm mov ds,sourceseg\r
+\r
+expand:\r
+asm lodsw\r
+asm cmp ax,bx\r
+asm je repeat\r
+asm stosw\r
+asm jmp next\r
+\r
+repeat:\r
+asm lodsw\r
+asm mov cx,ax // repeat count\r
+asm lodsw // repeat value\r
+asm rep stosw\r
+\r
+next:\r
+\r
+asm cmp si,0x10 // normalize ds:si\r
+asm jb sinorm\r
+asm mov ax,si\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm mov dx,ds\r
+asm add dx,ax\r
+asm mov ds,dx\r
+asm and si,0xf\r
+sinorm:\r
+asm cmp di,0x10 // normalize es:di\r
+asm jb dinorm\r
+asm mov ax,di\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm shr ax,1\r
+asm mov dx,es\r
+asm add dx,ax\r
+asm mov es,dx\r
+asm and di,0xf\r
+dinorm:\r
+\r
+asm cmp di,ss:endoff\r
+asm jne expand\r
+asm mov ax,es\r
+asm cmp ax,ss:endseg\r
+asm jb expand\r
+\r
+asm mov ax,ss\r
+asm mov ds,ax\r
+\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ CACHE MANAGER ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupGrFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupGrFile (void)\r
+{\r
+ int handle;\r
+ memptr compseg;\r
+\r
+#ifdef GRHEADERLINKED\r
+\r
+#if GRMODE == EGAGR\r
+ grhuffman = (huffnode *)&EGAdict;\r
+ grstarts = (long _seg *)FP_SEG(&EGAhead);\r
+#endif\r
+#if GRMODE == CGAGR\r
+ grhuffman = (huffnode *)&CGAdict;\r
+ grstarts = (long _seg *)FP_SEG(&CGAhead);\r
+#endif\r
+\r
+ CAL_OptimizeNodes (grhuffman);\r
+\r
+#else\r
+\r
+//\r
+// load ???dict.ext (huffman dictionary for graphics files)\r
+//\r
+\r
+ if ((handle = open(GREXT"DICT."EXT,\r
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ Quit ("Can't open "GREXT"DICT."EXT"!");\r
+\r
+ read(handle, &grhuffman, sizeof(grhuffman));\r
+ close(handle);\r
+ CAL_OptimizeNodes (grhuffman);\r
+//\r
+// load the data offsets from ???head.ext\r
+//\r
+ MM_GetPtr (&(memptr)grstarts,(NUMCHUNKS+1)*FILEPOSSIZE);\r
+\r
+ if ((handle = open(GREXT"HEAD."EXT,\r
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ Quit ("Can't open "GREXT"HEAD."EXT"!");\r
+\r
+ CA_FarRead(handle, (memptr)grstarts, (NUMCHUNKS+1)*FILEPOSSIZE);\r
+\r
+ close(handle);\r
+\r
+\r
+#endif\r
+\r
+//\r
+// Open the graphics file, leaving it open until the game is finished\r
+//\r
+ grhandle = open(GREXT"GRAPH."EXT, O_RDONLY | O_BINARY);\r
+ if (grhandle == -1)\r
+ Quit ("Cannot open "GREXT"GRAPH."EXT"!");\r
+\r
+\r
+//\r
+// load the pic and sprite headers into the arrays in the data segment\r
+//\r
+#if NUMPICS>0\r
+ MM_GetPtr(&(memptr)pictable,NUMPICS*sizeof(pictabletype));\r
+ CAL_GetGrChunkLength(STRUCTPIC); // position file pointer\r
+ MM_GetPtr(&compseg,chunkcomplen);\r
+ CA_FarRead (grhandle,compseg,chunkcomplen);\r
+ CAL_HuffExpand (compseg, (byte huge *)pictable,NUMPICS*sizeof(pictabletype),grhuffman);\r
+ MM_FreePtr(&compseg);\r
+#endif\r
+\r
+#if NUMPICM>0\r
+ MM_GetPtr(&(memptr)picmtable,NUMPICM*sizeof(pictabletype));\r
+ CAL_GetGrChunkLength(STRUCTPICM); // position file pointer\r
+ MM_GetPtr(&compseg,chunkcomplen);\r
+ CA_FarRead (grhandle,compseg,chunkcomplen);\r
+ CAL_HuffExpand (compseg, (byte huge *)picmtable,NUMPICS*sizeof(pictabletype),grhuffman);\r
+ MM_FreePtr(&compseg);\r
+#endif\r
+\r
+#if NUMSPRITES>0\r
+ MM_GetPtr(&(memptr)spritetable,NUMSPRITES*sizeof(spritetabletype));\r
+ CAL_GetGrChunkLength(STRUCTSPRITE); // position file pointer\r
+ MM_GetPtr(&compseg,chunkcomplen);\r
+ CA_FarRead (grhandle,compseg,chunkcomplen);\r
+ CAL_HuffExpand (compseg, (byte huge *)spritetable,NUMSPRITES*sizeof(spritetabletype),grhuffman);\r
+ MM_FreePtr(&compseg);\r
+#endif\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupMapFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupMapFile (void)\r
+{\r
+ int handle;\r
+ long length;\r
+\r
+//\r
+// load maphead.ext (offsets and tileinfo for map file)\r
+//\r
+#ifndef MAPHEADERLINKED\r
+ if ((handle = open("MAPHEAD."EXT,\r
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ Quit ("Can't open MAPHEAD."EXT"!");\r
+ length = filelength(handle);\r
+ MM_GetPtr (&(memptr)tinf,length);\r
+ CA_FarRead(handle, tinf, length);\r
+ close(handle);\r
+#else\r
+\r
+ tinf = (byte _seg *)FP_SEG(&maphead);\r
+\r
+#endif\r
+\r
+//\r
+// open the data file\r
+//\r
+#ifdef MAPHEADERLINKED\r
+ if ((maphandle = open("GAMEMAPS."EXT,\r
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ Quit ("Can't open GAMEMAPS."EXT"!");\r
+#else\r
+ if ((maphandle = open("MAPTEMP."EXT,\r
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ Quit ("Can't open MAPTEMP."EXT"!");\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupAudioFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupAudioFile (void)\r
+{\r
+ int handle;\r
+ long length;\r
+\r
+//\r
+// load maphead.ext (offsets and tileinfo for map file)\r
+//\r
+#ifndef AUDIOHEADERLINKED\r
+ if ((handle = open("AUDIOHED."EXT,\r
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ Quit ("Can't open AUDIOHED."EXT"!");\r
+ length = filelength(handle);\r
+ MM_GetPtr (&(memptr)audiostarts,length);\r
+ CA_FarRead(handle, (byte far *)audiostarts, length);\r
+ close(handle);\r
+#else\r
+ audiohuffman = (huffnode *)&audiodict;\r
+ CAL_OptimizeNodes (audiohuffman);\r
+ audiostarts = (long _seg *)FP_SEG(&audiohead);\r
+#endif\r
+\r
+//\r
+// open the data file\r
+//\r
+#ifndef AUDIOHEADERLINKED\r
+ if ((audiohandle = open("AUDIOT."EXT,\r
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ Quit ("Can't open AUDIOT."EXT"!");\r
+#else\r
+ if ((audiohandle = open("AUDIO."EXT,\r
+ O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ Quit ("Can't open AUDIO."EXT"!");\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_Startup\r
+=\r
+= Open all files and load in headers\r
+=\r
+======================\r
+*/\r
+\r
+void CA_Startup (void)\r
+{\r
+#ifdef PROFILE\r
+ unlink ("PROFILE.TXT");\r
+ profilehandle = open("PROFILE.TXT", O_CREAT | O_WRONLY | O_TEXT);\r
+#endif\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+ if (!FindFile("AUDIO."EXT,NULL,2))\r
+ Quit("CA_Startup(): Can't find audio files.");\r
+//\r
+// MDM end\r
+\r
+#ifndef NOAUDIO\r
+ CAL_SetupAudioFile ();\r
+#endif\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+ if (!FindFile("GAMEMAPS."EXT,NULL,1))\r
+ Quit("CA_Startup(): Can't find level files.");\r
+//\r
+// MDM end\r
+\r
+#ifndef NOMAPS\r
+ CAL_SetupMapFile ();\r
+#endif\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+ if (!FindFile("EGAGRAPH."EXT,NULL,2))\r
+ Quit("CA_Startup(): Can't find graphics files.");\r
+//\r
+// MDM end\r
+\r
+#ifndef NOGRAPHICS\r
+ CAL_SetupGrFile ();\r
+#endif\r
+\r
+ mapon = -1;\r
+ ca_levelbit = 1;\r
+ ca_levelnum = 0;\r
+\r
+ drawcachebox = CAL_DialogDraw;\r
+ updatecachebox = CAL_DialogUpdate;\r
+ finishcachebox = CAL_DialogFinish;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_Shutdown\r
+=\r
+= Closes all files\r
+=\r
+======================\r
+*/\r
+\r
+void CA_Shutdown (void)\r
+{\r
+#ifdef PROFILE\r
+ close (profilehandle);\r
+#endif\r
+\r
+ close (maphandle);\r
+ close (grhandle);\r
+ close (audiohandle);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheAudioChunk\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheAudioChunk (int chunk)\r
+{\r
+ long pos,compressed;\r
+#ifdef AUDIOHEADERLINKED\r
+ long expanded;\r
+ memptr bigbufferseg;\r
+ byte far *source;\r
+#endif\r
+\r
+ if (audiosegs[chunk])\r
+ {\r
+ MM_SetPurge (&(memptr)audiosegs[chunk],0);\r
+ return; // allready in memory\r
+ }\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+ if (!FindFile("AUDIO."EXT,NULL,2))\r
+ Quit("CA_CacheAudioChunk(): Can't find audio files.");\r
+//\r
+// MDM end\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+ pos = audiostarts[chunk];\r
+ compressed = audiostarts[chunk+1]-pos;\r
+\r
+ lseek(audiohandle,pos,SEEK_SET);\r
+\r
+#ifndef AUDIOHEADERLINKED\r
+\r
+ MM_GetPtr (&(memptr)audiosegs[chunk],compressed);\r
+ if (mmerror)\r
+ return;\r
+\r
+ CA_FarRead(audiohandle,audiosegs[chunk],compressed);\r
+\r
+#else\r
+\r
+ if (compressed<=BUFFERSIZE)\r
+ {\r
+ CA_FarRead(audiohandle,bufferseg,compressed);\r
+ source = bufferseg;\r
+ }\r
+ else\r
+ {\r
+ MM_GetPtr(&bigbufferseg,compressed);\r
+ if (mmerror)\r
+ return;\r
+ MM_SetLock (&bigbufferseg,true);\r
+ CA_FarRead(audiohandle,bigbufferseg,compressed);\r
+ source = bigbufferseg;\r
+ }\r
+\r
+ expanded = *(long far *)source;\r
+ source += 4; // skip over length\r
+ MM_GetPtr (&(memptr)audiosegs[chunk],expanded);\r
+ if (mmerror)\r
+ goto done;\r
+ CAL_HuffExpand (source,audiosegs[chunk],expanded,audiohuffman);\r
+\r
+done:\r
+ if (compressed>BUFFERSIZE)\r
+ MM_FreePtr(&bigbufferseg);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_LoadAllSounds\r
+=\r
+= Purges all sounds, then loads all new ones (mode switch)\r
+=\r
+======================\r
+*/\r
+\r
+void CA_LoadAllSounds (void)\r
+{\r
+ unsigned start,i;\r
+\r
+ switch (oldsoundmode)\r
+ {\r
+ case sdm_Off:\r
+ goto cachein;\r
+ case sdm_PC:\r
+ start = STARTPCSOUNDS;\r
+ break;\r
+ case sdm_AdLib:\r
+ start = STARTADLIBSOUNDS;\r
+ break;\r
+ }\r
+\r
+ for (i=0;i<NUMSOUNDS;i++,start++)\r
+ if (audiosegs[start])\r
+ MM_SetPurge (&(memptr)audiosegs[start],3); // make purgable\r
+\r
+cachein:\r
+\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_Off:\r
+ return;\r
+ case sdm_PC:\r
+ start = STARTPCSOUNDS;\r
+ break;\r
+ case sdm_AdLib:\r
+ start = STARTADLIBSOUNDS;\r
+ break;\r
+ }\r
+\r
+ for (i=0;i<NUMSOUNDS;i++,start++)\r
+ CA_CacheAudioChunk (start);\r
+\r
+ oldsoundmode = SoundMode;\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if GRMODE == EGAGR\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ShiftSprite\r
+=\r
+= Make a shifted (one byte wider) copy of a sprite into another area\r
+=\r
+======================\r
+*/\r
+\r
+unsigned static sheight,swidth;\r
+boolean static dothemask;\r
+\r
+void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest,\r
+ unsigned width, unsigned height, unsigned pixshift, boolean domask)\r
+{\r
+\r
+ sheight = height; // because we are going to reassign bp\r
+ swidth = width;\r
+ dothemask = domask;\r
+\r
+asm mov ax,[segment]\r
+asm mov ds,ax // source and dest are in same segment, and all local\r
+\r
+asm mov bx,[source]\r
+asm mov di,[dest]\r
+\r
+asm mov bp,[pixshift]\r
+asm shl bp,1\r
+asm mov bp,WORD PTR [shifttabletable+bp] // bp holds pointer to shift table\r
+\r
+asm cmp [ss:dothemask],0\r
+asm je skipmask\r
+\r
+//\r
+// table shift the mask\r
+//\r
+asm mov dx,[ss:sheight]\r
+\r
+domaskrow:\r
+\r
+asm mov BYTE PTR [di],255 // 0xff first byte\r
+asm mov cx,ss:[swidth]\r
+\r
+domaskbyte:\r
+\r
+asm mov al,[bx] // source\r
+asm not al\r
+asm inc bx // next source byte\r
+asm xor ah,ah\r
+asm shl ax,1\r
+asm mov si,ax\r
+asm mov ax,[bp+si] // table shift into two bytes\r
+asm not ax\r
+asm and [di],al // and with first byte\r
+asm inc di\r
+asm mov [di],ah // replace next byte\r
+\r
+asm loop domaskbyte\r
+\r
+asm inc di // the last shifted byte has 1s in it\r
+asm dec dx\r
+asm jnz domaskrow\r
+\r
+skipmask:\r
+\r
+//\r
+// table shift the data\r
+//\r
+asm mov dx,ss:[sheight]\r
+asm shl dx,1\r
+asm shl dx,1 // four planes of data\r
+\r
+dodatarow:\r
+\r
+asm mov BYTE PTR [di],0 // 0 first byte\r
+asm mov cx,ss:[swidth]\r
+\r
+dodatabyte:\r
+\r
+asm mov al,[bx] // source\r
+asm inc bx // next source byte\r
+asm xor ah,ah\r
+asm shl ax,1\r
+asm mov si,ax\r
+asm mov ax,[bp+si] // table shift into two bytes\r
+asm or [di],al // or with first byte\r
+asm inc di\r
+asm mov [di],ah // replace next byte\r
+\r
+asm loop dodatabyte\r
+\r
+asm inc di // the last shifted byte has 0s in it\r
+asm dec dx\r
+asm jnz dodatarow\r
+\r
+//\r
+// done\r
+//\r
+\r
+asm mov ax,ss // restore data segment\r
+asm mov ds,ax\r
+\r
+}\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_CacheSprite\r
+=\r
+= Generate shifts and set up sprite structure for a given sprite\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_CacheSprite (int chunk, byte far *compressed)\r
+{\r
+ int i;\r
+ unsigned shiftstarts[5];\r
+ unsigned smallplane,bigplane,expanded;\r
+ spritetabletype far *spr;\r
+ spritetype _seg *dest;\r
+\r
+#if GRMODE == CGAGR\r
+//\r
+// CGA has no pel panning, so shifts are never needed\r
+//\r
+ spr = &spritetable[chunk-STARTSPRITES];\r
+ smallplane = spr->width*spr->height;\r
+ MM_GetPtr (&grsegs[chunk],smallplane*2+MAXSHIFTS*6);\r
+ if (mmerror)\r
+ return;\r
+ dest = (spritetype _seg *)grsegs[chunk];\r
+ dest->sourceoffset[0] = MAXSHIFTS*6; // start data after 3 unsigned tables\r
+ dest->planesize[0] = smallplane;\r
+ dest->width[0] = spr->width;\r
+\r
+//\r
+// expand the unshifted shape\r
+//\r
+ CAL_HuffExpand (compressed, &dest->data[0],smallplane*2,grhuffman);\r
+\r
+#endif\r
+\r
+\r
+#if GRMODE == EGAGR\r
+\r
+//\r
+// calculate sizes\r
+//\r
+ spr = &spritetable[chunk-STARTSPRITES];\r
+ smallplane = spr->width*spr->height;\r
+ bigplane = (spr->width+1)*spr->height;\r
+\r
+ shiftstarts[0] = MAXSHIFTS*6; // start data after 3 unsigned tables\r
+ shiftstarts[1] = shiftstarts[0] + smallplane*5; // 5 planes in a sprite\r
+ shiftstarts[2] = shiftstarts[1] + bigplane*5;\r
+ shiftstarts[3] = shiftstarts[2] + bigplane*5;\r
+ shiftstarts[4] = shiftstarts[3] + bigplane*5; // nothing ever put here\r
+\r
+ expanded = shiftstarts[spr->shifts];\r
+ MM_GetPtr (&grsegs[chunk],expanded);\r
+ if (mmerror)\r
+ return;\r
+ dest = (spritetype _seg *)grsegs[chunk];\r
+\r
+//\r
+// expand the unshifted shape\r
+//\r
+ CAL_HuffExpand (compressed, &dest->data[0],smallplane*5,grhuffman);\r
+\r
+//\r
+// make the shifts!\r
+//\r
+ switch (spr->shifts)\r
+ {\r
+ case 1:\r
+ for (i=0;i<4;i++)\r
+ {\r
+ dest->sourceoffset[i] = shiftstarts[0];\r
+ dest->planesize[i] = smallplane;\r
+ dest->width[i] = spr->width;\r
+ }\r
+ break;\r
+\r
+ case 2:\r
+ for (i=0;i<2;i++)\r
+ {\r
+ dest->sourceoffset[i] = shiftstarts[0];\r
+ dest->planesize[i] = smallplane;\r
+ dest->width[i] = spr->width;\r
+ }\r
+ for (i=2;i<4;i++)\r
+ {\r
+ dest->sourceoffset[i] = shiftstarts[1];\r
+ dest->planesize[i] = bigplane;\r
+ dest->width[i] = spr->width+1;\r
+ }\r
+ CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+ dest->sourceoffset[2],spr->width,spr->height,4,true);\r
+ break;\r
+\r
+ case 4:\r
+ dest->sourceoffset[0] = shiftstarts[0];\r
+ dest->planesize[0] = smallplane;\r
+ dest->width[0] = spr->width;\r
+\r
+ dest->sourceoffset[1] = shiftstarts[1];\r
+ dest->planesize[1] = bigplane;\r
+ dest->width[1] = spr->width+1;\r
+ CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+ dest->sourceoffset[1],spr->width,spr->height,2,true);\r
+\r
+ dest->sourceoffset[2] = shiftstarts[2];\r
+ dest->planesize[2] = bigplane;\r
+ dest->width[2] = spr->width+1;\r
+ CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+ dest->sourceoffset[2],spr->width,spr->height,4,true);\r
+\r
+ dest->sourceoffset[3] = shiftstarts[3];\r
+ dest->planesize[3] = bigplane;\r
+ dest->width[3] = spr->width+1;\r
+ CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+ dest->sourceoffset[3],spr->width,spr->height,6,true);\r
+\r
+ break;\r
+\r
+ default:\r
+ Quit ("CAL_CacheSprite: Bad shifts number!");\r
+ }\r
+\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ExpandGrChunk\r
+=\r
+= Does whatever is needed with a pointer to a compressed chunk\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_ExpandGrChunk (int chunk, byte far *source)\r
+{\r
+ long expanded;\r
+\r
+\r
+ if (chunk >= STARTTILE8 && chunk < STARTEXTERNS)\r
+ {\r
+ //\r
+ // expanded sizes of tile8/16/32 are implicit\r
+ //\r
+\r
+#if GRMODE == EGAGR\r
+#define BLOCK 32\r
+#define MASKBLOCK 40\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define BLOCK 16\r
+#define MASKBLOCK 32\r
+#endif\r
+\r
+ if (chunk<STARTTILE8M) // tile 8s are all in one chunk!\r
+ expanded = BLOCK*NUMTILE8;\r
+ else if (chunk<STARTTILE16)\r
+ expanded = MASKBLOCK*NUMTILE8M;\r
+ else if (chunk<STARTTILE16M) // all other tiles are one/chunk\r
+ expanded = BLOCK*4;\r
+ else if (chunk<STARTTILE32)\r
+ expanded = MASKBLOCK*4;\r
+ else if (chunk<STARTTILE32M)\r
+ expanded = BLOCK*16;\r
+ else\r
+ expanded = MASKBLOCK*16;\r
+ }\r
+ else\r
+ {\r
+ //\r
+ // everything else has an explicit size longword\r
+ //\r
+ expanded = *(long far *)source;\r
+ source += 4; // skip over length\r
+ }\r
+\r
+//\r
+// allocate final space, decompress it, and free bigbuffer\r
+// Sprites need to have shifts made and various other junk\r
+//\r
+ if (chunk>=STARTSPRITES && chunk< STARTTILE8)\r
+ CAL_CacheSprite(chunk,source);\r
+ else\r
+ {\r
+ MM_GetPtr (&grsegs[chunk],expanded);\r
+ if (mmerror)\r
+ return;\r
+ CAL_HuffExpand (source,grsegs[chunk],expanded,grhuffman);\r
+ }\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ReadGrChunk\r
+=\r
+= Gets a chunk off disk, optimizing reads to general buffer\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_ReadGrChunk (int chunk)\r
+{\r
+ long pos,compressed;\r
+ memptr bigbufferseg;\r
+ byte far *source;\r
+ int next;\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+ pos = GRFILEPOS(chunk);\r
+ if (pos<0) // $FFFFFFFF start is a sparse tile\r
+ return;\r
+\r
+ next = chunk +1;\r
+ while (GRFILEPOS(next) == -1) // skip past any sparse tiles\r
+ next++;\r
+\r
+ compressed = GRFILEPOS(next)-pos;\r
+\r
+ lseek(grhandle,pos,SEEK_SET);\r
+\r
+ if (compressed<=BUFFERSIZE)\r
+ {\r
+ CA_FarRead(grhandle,bufferseg,compressed);\r
+ source = bufferseg;\r
+ }\r
+ else\r
+ {\r
+ MM_GetPtr(&bigbufferseg,compressed);\r
+ if (mmerror)\r
+ return;\r
+ MM_SetLock (&bigbufferseg,true);\r
+ CA_FarRead(grhandle,bigbufferseg,compressed);\r
+ source = bigbufferseg;\r
+ }\r
+\r
+ CAL_ExpandGrChunk (chunk,source);\r
+\r
+ if (compressed>BUFFERSIZE)\r
+ MM_FreePtr(&bigbufferseg);\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheGrChunk\r
+=\r
+= Makes sure a given chunk is in memory, loadiing it if needed\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheGrChunk (int chunk)\r
+{\r
+ long pos,compressed;\r
+ memptr bigbufferseg;\r
+ byte far *source;\r
+ int next;\r
+\r
+ grneeded[chunk] |= ca_levelbit; // make sure it doesn't get removed\r
+ if (grsegs[chunk])\r
+ {\r
+ MM_SetPurge (&grsegs[chunk],0);\r
+ return; // allready in memory\r
+ }\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+ if (!FindFile("EGAGRAPH."EXT,NULL,2))\r
+ Quit("CA_CacheGrChunk(): Can't find graphics files.");\r
+//\r
+// MDM end\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+ pos = GRFILEPOS(chunk);\r
+ if (pos<0) // $FFFFFFFF start is a sparse tile\r
+ return;\r
+\r
+ next = chunk +1;\r
+ while (GRFILEPOS(next) == -1) // skip past any sparse tiles\r
+ next++;\r
+\r
+ compressed = GRFILEPOS(next)-pos;\r
+\r
+ lseek(grhandle,pos,SEEK_SET);\r
+\r
+ if (compressed<=BUFFERSIZE)\r
+ {\r
+ CA_FarRead(grhandle,bufferseg,compressed);\r
+ source = bufferseg;\r
+ }\r
+ else\r
+ {\r
+ MM_GetPtr(&bigbufferseg,compressed);\r
+ MM_SetLock (&bigbufferseg,true);\r
+ CA_FarRead(grhandle,bigbufferseg,compressed);\r
+ source = bigbufferseg;\r
+ }\r
+\r
+ CAL_ExpandGrChunk (chunk,source);\r
+\r
+ if (compressed>BUFFERSIZE)\r
+ MM_FreePtr(&bigbufferseg);\r
+}\r
+\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheMap\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheMap (int mapnum)\r
+{\r
+ long pos,compressed;\r
+ int plane;\r
+ memptr *dest,bigbufferseg;\r
+ unsigned size;\r
+ unsigned far *source;\r
+#ifdef MAPHEADERLINKED\r
+ memptr buffer2seg;\r
+ long expanded;\r
+#endif\r
+\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+ if (!FindFile("GAMEMAPS."EXT,NULL,1))\r
+ Quit("CA_CacheMap(): Can't find level files.");\r
+//\r
+// MDM end\r
+\r
+\r
+//\r
+// free up memory from last map\r
+//\r
+ if (mapon>-1 && mapheaderseg[mapon])\r
+ MM_SetPurge (&(memptr)mapheaderseg[mapon],3);\r
+ for (plane=0;plane<MAPPLANES;plane++)\r
+ if (mapsegs[plane])\r
+ MM_FreePtr (&(memptr)mapsegs[plane]);\r
+\r
+ mapon = mapnum;\r
+\r
+\r
+//\r
+// load map header\r
+// The header will be cached if it is still around\r
+//\r
+ if (!mapheaderseg[mapnum])\r
+ {\r
+ pos = ((mapfiletype _seg *)tinf)->headeroffsets[mapnum];\r
+ if (pos<0) // $FFFFFFFF start is a sparse map\r
+ Quit ("CA_CacheMap: Tried to load a non existent map!");\r
+\r
+ MM_GetPtr(&(memptr)mapheaderseg[mapnum],sizeof(maptype));\r
+ lseek(maphandle,pos,SEEK_SET);\r
+ CA_FarRead (maphandle,(memptr)mapheaderseg[mapnum],sizeof(maptype));\r
+ }\r
+ else\r
+ MM_SetPurge (&(memptr)mapheaderseg[mapnum],0);\r
+\r
+//\r
+// load the planes in\r
+// If a plane's pointer still exists it will be overwritten (levels are\r
+// allways reloaded, never cached)\r
+//\r
+\r
+ size = mapheaderseg[mapnum]->width * mapheaderseg[mapnum]->height * 2;\r
+\r
+ for (plane = 0; plane<MAPPLANES; plane++)\r
+ {\r
+ pos = mapheaderseg[mapnum]->planestart[plane];\r
+ compressed = mapheaderseg[mapnum]->planelength[plane];\r
+\r
+ if (!compressed)\r
+ continue; // the plane is not used in this game\r
+\r
+ dest = &(memptr)mapsegs[plane];\r
+ MM_GetPtr(dest,size);\r
+\r
+ lseek(maphandle,pos,SEEK_SET);\r
+ if (compressed<=BUFFERSIZE)\r
+ source = bufferseg;\r
+ else\r
+ {\r
+ MM_GetPtr(&bigbufferseg,compressed);\r
+ MM_SetLock (&bigbufferseg,true);\r
+ source = bigbufferseg;\r
+ }\r
+\r
+ CA_FarRead(maphandle,(byte far *)source,compressed);\r
+#ifdef MAPHEADERLINKED\r
+ //\r
+ // unhuffman, then unRLEW\r
+ // The huffman'd chunk has a two byte expanded length first\r
+ // The resulting RLEW chunk also does, even though it's not really\r
+ // needed\r
+ //\r
+ expanded = *source;\r
+ source++;\r
+ MM_GetPtr (&buffer2seg,expanded);\r
+ CAL_CarmackExpand (source, (unsigned far *)buffer2seg,expanded);\r
+ CA_RLEWexpand (((unsigned far *)buffer2seg)+1,*dest,size,\r
+ ((mapfiletype _seg *)tinf)->RLEWtag);\r
+ MM_FreePtr (&buffer2seg);\r
+\r
+#else\r
+ //\r
+ // unRLEW, skipping expanded length\r
+ //\r
+ CA_RLEWexpand (source+1, *dest,size,\r
+ ((mapfiletype _seg *)tinf)->RLEWtag);\r
+#endif\r
+\r
+ if (compressed>BUFFERSIZE)\r
+ MM_FreePtr(&bigbufferseg);\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_UpLevel\r
+=\r
+= Goes up a bit level in the needed lists and clears it out.\r
+= Everything is made purgable\r
+=\r
+======================\r
+*/\r
+\r
+void CA_UpLevel (void)\r
+{\r
+ if (ca_levelnum==7)\r
+ Quit ("CA_UpLevel: Up past level 7!");\r
+\r
+ ca_levelbit<<=1;\r
+ ca_levelnum++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_DownLevel\r
+=\r
+= Goes down a bit level in the needed lists and recaches\r
+= everything from the lower level\r
+=\r
+======================\r
+*/\r
+\r
+void CA_DownLevel (void)\r
+{\r
+ if (!ca_levelnum)\r
+ Quit ("CA_DownLevel: Down past level 0!");\r
+ ca_levelbit>>=1;\r
+ ca_levelnum--;\r
+ CA_CacheMarks(NULL);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_ClearMarks\r
+=\r
+= Clears out all the marks at the current level\r
+=\r
+======================\r
+*/\r
+\r
+void CA_ClearMarks (void)\r
+{\r
+ int i;\r
+\r
+ for (i=0;i<NUMCHUNKS;i++)\r
+ grneeded[i]&=~ca_levelbit;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_ClearAllMarks\r
+=\r
+= Clears out all the marks on all the levels\r
+=\r
+======================\r
+*/\r
+\r
+void CA_ClearAllMarks (void)\r
+{\r
+ _fmemset (grneeded,0,sizeof(grneeded));\r
+ ca_levelbit = 1;\r
+ ca_levelnum = 0;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_FreeGraphics\r
+=\r
+======================\r
+*/\r
+\r
+void CA_FreeGraphics (void)\r
+{\r
+ int i;\r
+\r
+ for (i=0;i<NUMCHUNKS;i++)\r
+ if (grsegs[i])\r
+ MM_SetPurge (&(memptr)grsegs[i],3);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_SetAllPurge\r
+=\r
+= Make everything possible purgable\r
+=\r
+======================\r
+*/\r
+\r
+void CA_SetAllPurge (void)\r
+{\r
+ int i;\r
+\r
+ CA_ClearMarks ();\r
+\r
+//\r
+// free cursor sprite and background save\r
+//\r
+ VW_FreeCursor ();\r
+\r
+//\r
+// free map headers and map planes\r
+//\r
+ for (i=0;i<NUMMAPS;i++)\r
+ if (mapheaderseg[i])\r
+ MM_SetPurge (&(memptr)mapheaderseg[i],3);\r
+\r
+ for (i=0;i<3;i++)\r
+ if (mapsegs[i])\r
+ MM_FreePtr (&(memptr)mapsegs[i]);\r
+\r
+//\r
+// free sounds\r
+//\r
+ for (i=0;i<NUMSNDCHUNKS;i++)\r
+ if (audiosegs[i])\r
+ MM_SetPurge (&(memptr)audiosegs[i],3);\r
+\r
+//\r
+// free graphics\r
+//\r
+ CA_FreeGraphics ();\r
+}\r
+\r
+\r
+void CA_SetGrPurge (void)\r
+{\r
+ int i;\r
+\r
+//\r
+// free graphics\r
+//\r
+ for (i=0;i<NUMCHUNKS;i++)\r
+ if (grsegs[i])\r
+ MM_SetPurge (&(memptr)grsegs[i],3);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogDraw\r
+=\r
+======================\r
+*/\r
+\r
+#define NUMBARS (17l*8)\r
+#define BARSTEP 8\r
+\r
+unsigned thx,thy,lastx;\r
+long barx,barstep;\r
+\r
+void CAL_DialogDraw (char *title,unsigned numcache)\r
+{\r
+ unsigned homex,homey,x;\r
+\r
+ barstep = (NUMBARS<<16)/numcache;\r
+\r
+//\r
+// draw dialog window (masked tiles 12 - 20 are window borders)\r
+//\r
+ US_CenterWindow (20,8);\r
+ homex = PrintX;\r
+ homey = PrintY;\r
+\r
+ US_CPrint ("Loading");\r
+ fontcolor = F_SECONDCOLOR;\r
+ US_CPrint (title);\r
+ fontcolor = F_BLACK;\r
+\r
+//\r
+// draw thermometer bar\r
+//\r
+ thx = homex + 8;\r
+ thy = homey + 32;\r
+ VWB_DrawTile8(thx,thy,0); // CAT3D numbers\r
+ VWB_DrawTile8(thx,thy+8,3);\r
+ VWB_DrawTile8(thx,thy+16,6);\r
+ VWB_DrawTile8(thx+17*8,thy,2);\r
+ VWB_DrawTile8(thx+17*8,thy+8,5);\r
+ VWB_DrawTile8(thx+17*8,thy+16,8);\r
+ for (x=thx+8;x<thx+17*8;x+=8)\r
+ {\r
+ VWB_DrawTile8(x,thy,1);\r
+ VWB_DrawTile8(x,thy+8,4);\r
+ VWB_DrawTile8(x,thy+16,7);\r
+ }\r
+\r
+ thx += 4; // first line location\r
+ thy += 5;\r
+ barx = (long)thx<<16;\r
+ lastx = thx;\r
+\r
+ VW_UpdateScreen();\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogUpdate\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_DialogUpdate (void)\r
+{\r
+ unsigned x,xh;\r
+\r
+ barx+=barstep;\r
+ xh = barx>>16;\r
+ if (xh - lastx > BARSTEP)\r
+ {\r
+ for (x=lastx;x<=xh;x++)\r
+#if GRMODE == EGAGR\r
+ VWB_Vlin (thy,thy+13,x,14);\r
+#endif\r
+#if GRMODE == CGAGR\r
+ VWB_Vlin (thy,thy+13,x,SECONDCOLOR);\r
+#endif\r
+ lastx = xh;\r
+ VW_UpdateScreen();\r
+ }\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogFinish\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_DialogFinish (void)\r
+{\r
+ unsigned x,xh;\r
+\r
+ xh = thx + NUMBARS;\r
+ for (x=lastx;x<=xh;x++)\r
+#if GRMODE == EGAGR\r
+ VWB_Vlin (thy,thy+13,x,14);\r
+#endif\r
+#if GRMODE == CGAGR\r
+ VWB_Vlin (thy,thy+13,x,SECONDCOLOR);\r
+#endif\r
+ VW_UpdateScreen();\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheMarks\r
+=\r
+======================\r
+*/\r
+#define MAXEMPTYREAD 1024\r
+\r
+void CA_CacheMarks (char *title)\r
+{\r
+ boolean dialog;\r
+ int i,next,numcache;\r
+ long pos,endpos,nextpos,nextendpos,compressed;\r
+ long bufferstart,bufferend; // file position of general buffer\r
+ byte far *source;\r
+ memptr bigbufferseg;\r
+\r
+ dialog = (title!=NULL);\r
+\r
+ numcache = 0;\r
+//\r
+// go through and make everything not needed purgable\r
+//\r
+ for (i=0;i<NUMCHUNKS;i++)\r
+ if (grneeded[i]&ca_levelbit)\r
+ {\r
+ if (grsegs[i]) // its allready in memory, make\r
+ MM_SetPurge(&grsegs[i],0); // sure it stays there!\r
+ else\r
+ numcache++;\r
+ }\r
+ else\r
+ {\r
+ if (grsegs[i]) // not needed, so make it purgeable\r
+ MM_SetPurge(&grsegs[i],3);\r
+ }\r
+\r
+ if (!numcache) // nothing to cache!\r
+ return;\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+ if (!FindFile("EGAGRAPH."EXT,NULL,2))\r
+ Quit("CA_CacheMarks(): Can't find graphics files.");\r
+//\r
+// MDM end\r
+\r
+ if (dialog)\r
+ {\r
+#ifdef PROFILE\r
+ write(profilehandle,title,strlen(title));\r
+ write(profilehandle,"\n",1);\r
+#endif\r
+ if (drawcachebox)\r
+ drawcachebox(title,numcache);\r
+ }\r
+\r
+//\r
+// go through and load in anything still needed\r
+//\r
+ bufferstart = bufferend = 0; // nothing good in buffer now\r
+\r
+ for (i=0;i<NUMCHUNKS;i++)\r
+ if ( (grneeded[i]&ca_levelbit) && !grsegs[i])\r
+ {\r
+//\r
+// update thermometer\r
+//\r
+ if (dialog && updatecachebox)\r
+ updatecachebox ();\r
+\r
+ pos = GRFILEPOS(i);\r
+ if (pos<0)\r
+ continue;\r
+\r
+ next = i +1;\r
+ while (GRFILEPOS(next) == -1) // skip past any sparse tiles\r
+ next++;\r
+\r
+ compressed = GRFILEPOS(next)-pos;\r
+ endpos = pos+compressed;\r
+\r
+ if (compressed<=BUFFERSIZE)\r
+ {\r
+ if (bufferstart<=pos\r
+ && bufferend>= endpos)\r
+ {\r
+ // data is allready in buffer\r
+ source = (byte _seg *)bufferseg+(pos-bufferstart);\r
+ }\r
+ else\r
+ {\r
+ // load buffer with a new block from disk\r
+ // try to get as many of the needed blocks in as possible\r
+ while ( next < NUMCHUNKS )\r
+ {\r
+ while (next < NUMCHUNKS &&\r
+ !(grneeded[next]&ca_levelbit && !grsegs[next]))\r
+ next++;\r
+ if (next == NUMCHUNKS)\r
+ continue;\r
+\r
+ nextpos = GRFILEPOS(next);\r
+ while (GRFILEPOS(++next) == -1) // skip past any sparse tiles\r
+ ;\r
+ nextendpos = GRFILEPOS(next);\r
+ if (nextpos - endpos <= MAXEMPTYREAD\r
+ && nextendpos-pos <= BUFFERSIZE)\r
+ endpos = nextendpos;\r
+ else\r
+ next = NUMCHUNKS; // read pos to posend\r
+ }\r
+\r
+ lseek(grhandle,pos,SEEK_SET);\r
+ CA_FarRead(grhandle,bufferseg,endpos-pos);\r
+ bufferstart = pos;\r
+ bufferend = endpos;\r
+ source = bufferseg;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // big chunk, allocate temporary buffer\r
+ MM_GetPtr(&bigbufferseg,compressed);\r
+ if (mmerror)\r
+ return;\r
+ MM_SetLock (&bigbufferseg,true);\r
+ lseek(grhandle,pos,SEEK_SET);\r
+ CA_FarRead(grhandle,bigbufferseg,compressed);\r
+ source = bigbufferseg;\r
+ }\r
+\r
+ CAL_ExpandGrChunk (i,source);\r
+ if (mmerror)\r
+ return;\r
+\r
+ if (compressed>BUFFERSIZE)\r
+ MM_FreePtr(&bigbufferseg);\r
+\r
+ }\r
+\r
+//\r
+// finish up any thermometer remnants\r
+//\r
+ if (dialog && finishcachebox)\r
+ finishcachebox();\r
+}\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_CA.H
+
+/*#ifndef __TYPES__\r
+#include "ID_TYPES.H"\r
+#endif\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+#ifndef __ID_GLOB__\r
+#include "ID_GLOB.H"\r
+#endif*/\r
+\r
+#define __ID_CA__\r
+\r
+//===========================================================================\r
+\r
+//#define NOMAPS\r
+//#define NOGRAPHICS\r
+//#define NOAUDIO\r
+\r
+#define MAPHEADERLINKED\r
+#define GRHEADERLINKED\r
+#define AUDIOHEADERLINKED\r
+\r
+#define NUMMAPS 39\r
+#define MAPPLANES 3\r
+\r
+//===========================================================================
+\r
+typedef struct\r
+{\r
+ long planestart[3];\r
+ unsigned planelength[3];\r
+ unsigned width,height;\r
+ char name[16];\r
+} maptype;\r
+\r
+//===========================================================================\r
+\r
+extern byte /*_1seg*/ *tinf;\r
+extern int mapon;\r
+\r
+extern unsigned /*_1seg*/ *mapsegs[3];\r
+extern maptype /*_1seg*/ *mapheaderseg[NUMMAPS];\r
+extern byte /*_1seg*/ *audiosegs[NUMSNDCHUNKS];\r
+extern void /*_1seg*/ *grsegs[NUMCHUNKS];\r
+\r
+extern byte far grneeded[NUMCHUNKS];\r
+extern byte ca_levelbit,ca_levelnum;\r
+\r
+extern char *titleptr[8];\r
+\r
+extern int profilehandle,debughandle;\r
+\r
+//\r
+// hooks for custom cache dialogs\r
+//\r
+extern void (*drawcachebox) (char *title, unsigned numcache);\r
+extern void (*updatecachebox) (void);\r
+extern void (*finishcachebox) (void);\r
+\r
+//===========================================================================\r
+\r
+// just for the score box reshifting\r
+\r
+void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest,\r
+ unsigned width, unsigned height, unsigned pixshift, boolean domask);\r
+\r
+//===========================================================================\r
+\r
+void CA_OpenDebug (void);\r
+void CA_CloseDebug (void);\r
+boolean CA_FarRead (int handle, byte far *dest, long length);\r
+boolean CA_FarWrite (int handle, byte far *source, long length);\r
+boolean CA_ReadFile (char *filename, memptr *ptr);\r
+boolean CA_LoadFile (char *filename, memptr *ptr);\r
+\r
+long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest,\r
+ unsigned rlewtag);\r
+\r
+void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length,\r
+ unsigned rlewtag);\r
+\r
+void CA_Startup (void);\r
+void CA_Shutdown (void);\r
+\r
+void CA_CacheAudioChunk (int chunk);\r
+void CA_LoadAllSounds (void);\r
+\r
+void CA_UpLevel (void);\r
+void CA_DownLevel (void);\r
+\r
+void CA_SetAllPurge (void);\r
+\r
+void CA_ClearMarks (void);\r
+void CA_ClearAllMarks (void);\r
+\r
+#define CA_MarkGrChunk(chunk) grneeded[chunk]|=ca_levelbit\r
+\r
+void CA_CacheGrChunk (int chunk);\r
+void CA_CacheMap (int mapnum);\r
+\r
+void CA_CacheMarks (char *title);\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_GLOB.H\r
+\r
+\r
+#include <ALLOC.H>\r
+#include <CTYPE.H>\r
+#include <DOS.H>\r
+#include <ERRNO.H>\r
+#include <FCNTL.H>\r
+#include <IO.H>\r
+#include <MEM.H>\r
+#include <PROCESS.H>\r
+#include <STDIO.H>\r
+#include <STDLIB.H>\r
+#include <STRING.H>\r
+#include <SYS\STAT.H>\r
+\r
+#define __ID_GLOB__\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define EXT "ARM"\r
+\r
+extern char far introscn;\r
+\r
+#include "GFXE_ARM.H"\r
+#include "AUDIOARM.H"\r
+#include "MAPSARM.H"\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+//\r
+// DEFINES THE TILE ATTRIBUTE CHECKING CONVENTION (macros).\r
+//\r
+// DEFINE CUSTOM BIT-FLAG NAMES...\r
+//\r
+\r
+\r
+#define tf_SOLID 0x01\r
+#define tf_SPECIAL 0x02\r
+#define tf_EMBEDDED_KEY_COLOR 0x04\r
+#define tf_INVISIBLE_WALL 0x09\r
+#define tf_MARKED 0x80\r
+\r
+#define ANIM_FLAGS(tile) (tinf[ANIM+(tile)])\r
+#define TILE_FLAGS(tile) (tinf[FLAGS+(tile)])\r
+\r
+#define GATE_KEY_COLOR(tile) ((unsigned char)(TILE_FLAGS(tile)>>4))\r
+\r
+#define CAT3D\r
+\r
+#define TEXTGR 0\r
+#define CGAGR 1\r
+#define EGAGR 2\r
+#define VGAGR 3\r
+\r
+#define EGA320GR 10 // MDM (GAMERS EDGE)\r
+#define EGA640GR 11 // MDM (GAMERS EDGE)\r
+\r
+#define GRMODE EGAGR\r
+\r
+#if GRMODE == EGAGR\r
+#define GREXT "EGA"\r
+#endif\r
+#if GRMODE == CGAGR\r
+#define GREXT "CGA"\r
+#endif\r
+\r
+//#define PROFILE\r
+\r
+//\r
+// ID Engine\r
+// Types.h - Generic types, #defines, etc.\r
+// v1.0d1\r
+//\r
+\r
+#ifndef __TYPES__\r
+#define __TYPES__\r
+\r
+typedef enum {false,true} boolean;\r
+typedef unsigned char byte;\r
+typedef unsigned int word;\r
+typedef unsigned long longword;\r
+typedef byte * Ptr;\r
+\r
+typedef struct\r
+ {\r
+ int x,y;\r
+ } Point;\r
+typedef struct\r
+ {\r
+ Point ul,lr;\r
+ } Rect;\r
+\r
+#define nil ((void *)0)\r
+\r
+#endif\r
+\r
+#include "ID_MM.H"\r
+#include "ID_CA.H"\r
+#include "ID_VW.H"\r
+#include "ID_IN.H"\r
+#include "ID_SD.H"\r
+#include "ID_US.H"\r
+\r
+\r
+void Quit (char *error, ...); // defined in user program\r
+\r
+//\r
+// replacing refresh manager with custom routines\r
+//\r
+\r
+#define PORTTILESWIDE 21 // all drawing takes place inside a\r
+#define PORTTILESHIGH 14 // non displayed port of this size\r
+\r
+#define UPDATEWIDE (PORTTILESWIDE+1)\r
+#define UPDATEHIGH PORTTILESHIGH\r
+\r
+#define MAXTICS 6\r
+#define DEMOTICS 3\r
+\r
+#define UPDATETERMINATE 0x0301\r
+\r
+extern unsigned mapwidth,mapheight,tics,realtics;\r
+extern boolean compatability;\r
+\r
+extern byte *updateptr;\r
+extern unsigned uwidthtable[UPDATEHIGH];\r
+extern unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_IN.c - Input Manager\r
+// v1.0d1\r
+// By Jason Blochowiak\r
+//\r
+\r
+//\r
+// This module handles dealing with the various input devices\r
+//\r
+// Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff),\r
+// User Mgr (for command line parms)\r
+//\r
+// Globals:\r
+// LastScan - The keyboard scan code of the last key pressed\r
+// LastASCII - The ASCII value of the last key pressed\r
+// DEBUG - there are more globals\r
+//\r
+\r
+//#include "ID_HEADS.H"
+#include "id_in.h"\r
+//#pragma hdrstop\r
+\r
+#define KeyInt 9 // The keyboard ISR number\r
+\r
+// Stuff for the joystick\r
+#define JoyScaleMax 32768\r
+#define JoyScaleShift 8\r
+#define MaxJoyValue 5000\r
+\r
+// Global variables\r
+ boolean JoystickCalibrated=false; // MDM (GAMERS EDGE) - added\r
+ ControlType ControlTypeUsed; // MDM (GAMERS EDGE) - added\r
+\r
+ boolean Keyboard[NumCodes],\r
+ JoysPresent[MaxJoys],\r
+ MousePresent;\r
+ boolean Paused;\r
+ char LastASCII;\r
+ ScanCode LastScan;\r
+ KeyboardDef KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}};\r
+ JoystickDef JoyDefs[MaxJoys];\r
+ ControlType Controls[MaxPlayers];\r
+\r
+// Demo DemoMode = demo_Off;\r
+// byte /*_1seg*/ *DemoBuffer;\r
+// word DemoOffset,DemoSize;\r
+\r
+// Internal variables\r
+static boolean IN_Started;\r
+static boolean CapsLock;\r
+static ScanCode CurCode,LastCode;\r
+static byte far ASCIINames[] = // Unshifted ASCII for scan codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8 ,9 , // 0\r
+ 'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0 ,'a','s', // 1\r
+ 'd','f','g','h','j','k','l',';',39 ,'`',0 ,92 ,'z','x','c','v', // 2\r
+ 'b','n','m',',','.','/',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4\r
+ '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+ far ShiftNames[] = // Shifted ASCII for scan codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8 ,9 , // 0\r
+ 'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0 ,'A','S', // 1\r
+ 'D','F','G','H','J','K','L',':',34 ,'~',0 ,'|','Z','X','C','V', // 2\r
+ 'B','N','M','<','>','?',0 ,'*',0 ,' ',0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,'7','8','9','-','4','5','6','+','1', // 4\r
+ '2','3','0',127,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+ far SpecialNames[] = // ASCII for 0xe0 prefixed codes\r
+ {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 0\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,13 ,0 ,0 ,0 , // 1\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 2\r
+ 0 ,0 ,0 ,0 ,0 ,'/',0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 3\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 4\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 5\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , // 6\r
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 // 7\r
+ },\r
+\r
+#if 0\r
+ *ScanNames[] = // Scan code names with single chars\r
+ {\r
+ "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?",\r
+ "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S",\r
+ "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V",\r
+ "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?",\r
+ "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+ "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?"\r
+ }, // DEBUG - consolidate these\r
+#endif\r
+\r
+ far ExtScanCodes[] = // Scan codes with >1 char names\r
+ {\r
+ 1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,\r
+ 0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36,\r
+ 0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48,\r
+ 0x50,0x4b,0x4d,0x00\r
+ };\r
+#if 0\r
+ *ExtScanNames[] = // Names corresponding to ExtScanCodes\r
+ {\r
+ "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4",\r
+ "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft",\r
+ "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up",\r
+ "Down","Left","Right",""\r
+ };\r
+#endif\r
+static Direction DirTable[] = // Quick lookup for total direction\r
+ {\r
+ dir_NorthWest, dir_North, dir_NorthEast,\r
+ dir_West, dir_None, dir_East,\r
+ dir_SouthWest, dir_South, dir_SouthEast\r
+ };\r
+\r
+static void (*INL_KeyHook)(void);\r
+static void interrupt (*OldKeyVect)(void);\r
+\r
+static char *ParmStrings[] = {"nojoys","nomouse",nil};\r
+\r
+// Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_KeyService() - Handles a keyboard interrupt (key up/down)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+INL_KeyService(void)\r
+{\r
+static boolean special;\r
+ byte k,c,\r
+ temp;\r
+\r
+ k = inportb(0x60); // Get the scan code\r
+\r
+ // Tell the XT keyboard controller to clear the key\r
+ outportb(0x61,(temp = inportb(0x61)) | 0x80);\r
+ outportb(0x61,temp);\r
+\r
+ if (k == 0xe0) // Special key prefix\r
+ special = true;\r
+ else if (k == 0xe1) // Handle Pause key\r
+ Paused = true;\r
+ else\r
+ {\r
+ if (k & 0x80) // Break code\r
+ {\r
+ k &= 0x7f;\r
+\r
+// DEBUG - handle special keys: ctl-alt-delete, print scrn\r
+\r
+ Keyboard[k] = false;\r
+ }\r
+ else // Make code\r
+ {\r
+ LastCode = CurCode;\r
+ CurCode = LastScan = k;\r
+ Keyboard[k] = true;\r
+\r
+ if (special)\r
+ c = SpecialNames[k];\r
+ else\r
+ {\r
+ if (k == sc_CapsLock)\r
+ {\r
+ CapsLock ^= true;\r
+ // DEBUG - make caps lock light work\r
+ }\r
+\r
+ if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted\r
+ {\r
+ c = ShiftNames[k];\r
+ if ((c >= 'A') && (c <= 'Z') && CapsLock)\r
+ c += 'a' - 'A';\r
+ }\r
+ else\r
+ {\r
+ c = ASCIINames[k];\r
+ if ((c >= 'a') && (c <= 'z') && CapsLock)\r
+ c -= 'a' - 'A';\r
+ }\r
+ }\r
+ if (c)\r
+ LastASCII = c;\r
+ }\r
+\r
+ special = false;\r
+ }\r
+\r
+ if (INL_KeyHook && !special)\r
+ INL_KeyHook();\r
+ outportb(0x20,0x20);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetMouseDelta() - Gets the amount that the mouse has moved from the\r
+// mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetMouseDelta(int *x,int *y)\r
+{\r
+ Mouse(MDelta);\r
+ *x = _CX;\r
+ *y = _DX;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetMouseButtons() - Gets the status of the mouse buttons from the\r
+// mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetMouseButtons(void)\r
+{\r
+ word buttons;\r
+\r
+ Mouse(MButtons);\r
+ buttons = _BX;\r
+ return(buttons);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetJoyAbs() - Reads the absolute position of the specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_GetJoyAbs(word joy,word *xp,word *yp)\r
+{\r
+ byte xb,yb,\r
+ xs,ys;\r
+ word x,y;\r
+\r
+ x = y = 0;\r
+ xs = joy? 2 : 0; // Find shift value for x axis\r
+ xb = 1 << xs; // Use shift value to get x bit mask\r
+ ys = joy? 3 : 1; // Do the same for y axis\r
+ yb = 1 << ys;\r
+\r
+// Read the absolute joystick values\r
+asm pushf // Save some registers\r
+asm push si\r
+asm push di\r
+asm cli // Make sure an interrupt doesn't screw the timings\r
+\r
+\r
+asm mov dx,0x201\r
+asm in al,dx\r
+asm out dx,al // Clear the resistors\r
+\r
+asm mov ah,[xb] // Get masks into registers\r
+asm mov ch,[yb]\r
+\r
+asm xor si,si // Clear count registers\r
+asm xor di,di\r
+asm xor bh,bh // Clear high byte of bx for later\r
+\r
+asm push bp // Don't mess up stack frame\r
+asm mov bp,MaxJoyValue\r
+\r
+loop:\r
+asm in al,dx // Get bits indicating whether all are finished\r
+\r
+asm dec bp // Check bounding register\r
+asm jz done // We have a silly value - abort\r
+\r
+asm mov bl,al // Duplicate the bits\r
+asm and bl,ah // Mask off useless bits (in [xb])\r
+asm add si,bx // Possibly increment count register\r
+asm mov cl,bl // Save for testing later\r
+\r
+asm mov bl,al\r
+asm and bl,ch // [yb]\r
+asm add di,bx\r
+\r
+asm add cl,bl\r
+asm jnz loop // If both bits were 0, drop out\r
+\r
+done:\r
+asm pop bp\r
+\r
+asm mov cl,[xs] // Get the number of bits to shift\r
+asm shr si,cl // and shift the count that many times\r
+\r
+asm mov cl,[ys]\r
+asm shr di,cl\r
+\r
+asm mov [x],si // Store the values into the variables\r
+asm mov [y],di\r
+\r
+asm pop di\r
+asm pop si\r
+asm popf // Restore the registers\r
+\r
+ *xp = x;\r
+ *yp = y;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetJoyDelta() - Returns the relative movement of the specified\r
+// joystick (from +/-127, scaled adaptively)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive)\r
+{\r
+ word x,y;\r
+ longword time;\r
+ JoystickDef *def;\r
+static longword lasttime;\r
+\r
+ IN_GetJoyAbs(joy,&x,&y);\r
+ def = JoyDefs + joy;\r
+\r
+ if (x < def->threshMinX)\r
+ {\r
+ if (x < def->joyMinX)\r
+ x = def->joyMinX;\r
+\r
+ x = -(x - def->threshMinX);\r
+ x *= def->joyMultXL;\r
+ x >>= JoyScaleShift;\r
+ *dx = (x > 127)? -127 : -x;\r
+ }\r
+ else if (x > def->threshMaxX)\r
+ {\r
+ if (x > def->joyMaxX)\r
+ x = def->joyMaxX;\r
+\r
+ x = x - def->threshMaxX;\r
+ x *= def->joyMultXH;\r
+ x >>= JoyScaleShift;\r
+ *dx = (x > 127)? 127 : x;\r
+ }\r
+ else\r
+ *dx = 0;\r
+\r
+ if (y < def->threshMinY)\r
+ {\r
+ if (y < def->joyMinY)\r
+ y = def->joyMinY;\r
+\r
+ y = -(y - def->threshMinY);\r
+ y *= def->joyMultYL;\r
+ y >>= JoyScaleShift;\r
+ *dy = (y > 127)? -127 : -y;\r
+ }\r
+ else if (y > def->threshMaxY)\r
+ {\r
+ if (y > def->joyMaxY)\r
+ y = def->joyMaxY;\r
+\r
+ y = y - def->threshMaxY;\r
+ y *= def->joyMultYH;\r
+ y >>= JoyScaleShift;\r
+ *dy = (y > 127)? 127 : y;\r
+ }\r
+ else\r
+ *dy = 0;\r
+\r
+ if (adaptive)\r
+ {\r
+ time = (TimeCount - lasttime) / 2;\r
+ if (time)\r
+ {\r
+ if (time > 8)\r
+ time = 8;\r
+ *dx *= time;\r
+ *dy *= time;\r
+ }\r
+ }\r
+ lasttime = TimeCount;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_GetJoyButtons() - Returns the button status of the specified\r
+// joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetJoyButtons(word joy)\r
+{\r
+register word result;\r
+\r
+ result = inportb(0x201); // Get all the joystick buttons\r
+ result >>= joy? 6 : 4; // Shift into bits 0-1\r
+ result &= 3; // Mask off the useless bits\r
+ result ^= 3;\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetJoyButtonsDB() - Returns the de-bounced button status of the\r
+// specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+IN_GetJoyButtonsDB(word joy)\r
+{\r
+ longword lasttime;\r
+ word result1,result2;\r
+\r
+ do\r
+ {\r
+ result1 = INL_GetJoyButtons(joy);\r
+ lasttime = TimeCount;\r
+ while (TimeCount == lasttime)\r
+ ;\r
+ result2 = INL_GetJoyButtons(joy);\r
+ } while (result1 != result2);\r
+ return(result1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartKbd() - Sets up my keyboard stuff for use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_StartKbd(void)\r
+{\r
+ INL_KeyHook = 0; // Clear key hook\r
+\r
+ IN_ClearKeysDown();\r
+\r
+ OldKeyVect = getvect(KeyInt);\r
+ setvect(KeyInt,INL_KeyService);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutKbd() - Restores keyboard control to the BIOS\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutKbd(void)\r
+{\r
+ poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0); // Clear ctrl/alt/shift flags\r
+\r
+ setvect(KeyInt,OldKeyVect);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartMouse() - Detects and sets up the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartMouse(void)\r
+{\r
+ if (getvect(MouseInt))\r
+ {\r
+ Mouse(MReset);\r
+ if (_AX == 0xffff)\r
+ return(true);\r
+ }\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutMouse() - Cleans up after the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutMouse(void)\r
+{\r
+}\r
+\r
+//\r
+// INL_SetJoyScale() - Sets up scaling values for the specified joystick\r
+//\r
+static void\r
+INL_SetJoyScale(word joy)\r
+{\r
+ JoystickDef *def;\r
+\r
+ def = &JoyDefs[joy];\r
+ def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX);\r
+ def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX);\r
+ def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY);\r
+ def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale()\r
+// to set up scaling values\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy)\r
+{\r
+ word d,r;\r
+ JoystickDef *def;\r
+\r
+ def = &JoyDefs[joy];\r
+\r
+ def->joyMinX = minx;\r
+ def->joyMaxX = maxx;\r
+ r = maxx - minx;\r
+ d = r / 5;\r
+ def->threshMinX = ((r / 2) - d) + minx;\r
+ def->threshMaxX = ((r / 2) + d) + minx;\r
+\r
+ def->joyMinY = miny;\r
+ def->joyMaxY = maxy;\r
+ r = maxy - miny;\r
+ d = r / 5;\r
+ def->threshMinY = ((r / 2) - d) + miny;\r
+ def->threshMaxY = ((r / 2) + d) + miny;\r
+\r
+ INL_SetJoyScale(joy);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_StartJoy() - Detects & auto-configures the specified joystick\r
+// The auto-config assumes the joystick is centered\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartJoy(word joy)\r
+{\r
+ word x,y;\r
+\r
+ IN_GetJoyAbs(joy,&x,&y);\r
+\r
+ if\r
+ (\r
+ ((x == 0) || (x > MaxJoyValue - 10))\r
+ || ((y == 0) || (y > MaxJoyValue - 10))\r
+ )\r
+ return(false);\r
+ else\r
+ {\r
+ IN_SetupJoy(joy,0,x * 2,0,y * 2);\r
+ return(true);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_ShutJoy() - Cleans up the joystick stuff\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutJoy(word joy)\r
+{\r
+ JoysPresent[joy] = false;\r
+}\r
+\r
+// Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Startup() - Starts up the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Startup(void)\r
+{\r
+ boolean checkjoys,checkmouse;\r
+ word i;\r
+\r
+ if (IN_Started)\r
+ return;\r
+\r
+ checkjoys = true;\r
+ checkmouse = true;\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ switch (US_CheckParm(_argv[i],ParmStrings))\r
+ {\r
+ case 0:\r
+ checkjoys = false;\r
+ break;\r
+ case 1:\r
+ checkmouse = false;\r
+ break;\r
+ }\r
+ }\r
+\r
+ INL_StartKbd();\r
+ MousePresent = checkmouse? INL_StartMouse() : false;\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ JoysPresent[i] = checkjoys? INL_StartJoy(i) : false;\r
+\r
+ IN_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Default() - Sets up default conditions for the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Default(boolean gotit,ControlType in)\r
+{\r
+ if\r
+ (\r
+ (!gotit)\r
+ || ((in == ctrl_Joystick1) && !JoysPresent[0])\r
+ || ((in == ctrl_Joystick2) && !JoysPresent[1])\r
+ || ((in == ctrl_Mouse) && !MousePresent)\r
+ )\r
+ in = ctrl_Keyboard1;\r
+ IN_SetControlType(0,in);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Shutdown() - Shuts down the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Shutdown(void)\r
+{\r
+ word i;\r
+\r
+ if (!IN_Started)\r
+ return;\r
+\r
+ INL_ShutMouse();\r
+ for (i = 0;i < MaxJoys;i++)\r
+ INL_ShutJoy(i);\r
+ INL_ShutKbd();\r
+\r
+ IN_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService()\r
+// everytime a real make/break code gets hit\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetKeyHook(void (*hook)())\r
+{\r
+ INL_KeyHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ClearKeyDown() - Clears the keyboard array\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ClearKeysDown(void)\r
+{\r
+ int i;\r
+\r
+ LastScan = sc_None;\r
+ LastASCII = key_None;\r
+ for (i = 0;i < NumCodes;i++)\r
+ Keyboard[i] = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy)\r
+{\r
+ if (buttons & (1 << 0))\r
+ info->button0 = true;\r
+ if (buttons & (1 << 1))\r
+ info->button1 = true;\r
+\r
+ info->x += dx;\r
+ info->y += dy;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ReadCursor() - Reads the input devices and fills in the cursor info\r
+// struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadCursor(CursorInfo *info)\r
+{\r
+ word i,\r
+ buttons;\r
+ int dx,dy;\r
+\r
+ info->x = info->y = 0;\r
+ info->button0 = info->button1 = false;\r
+\r
+ if (MousePresent)\r
+ {\r
+ buttons = INL_GetMouseButtons();\r
+ INL_GetMouseDelta(&dx,&dy);\r
+ INL_AdjustCursor(info,buttons,dx,dy);\r
+ }\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ {\r
+ if (!JoysPresent[i])\r
+ continue;\r
+\r
+ buttons = INL_GetJoyButtons(i);\r
+ INL_GetJoyDelta(i,&dx,&dy,true);\r
+ dx /= 64;\r
+ dy /= 64;\r
+ INL_AdjustCursor(info,buttons,dx,dy);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ReadControl() - Reads the device associated with the specified\r
+// player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+ boolean realdelta=false; // MDM (GAMERS EDGE)\r
+ byte dbyte;\r
+ word buttons;\r
+ int dx,dy;\r
+ Motion mx,my;\r
+ ControlType type;\r
+register KeyboardDef *def;\r
+\r
+ dx = dy = 0;\r
+ mx = my = motion_None;\r
+ buttons = 0;\r
+\r
+#if 0\r
+ if (DemoMode == demo_Playback)\r
+ {\r
+ dbyte = DemoBuffer[DemoOffset + 1];\r
+ my = (dbyte & 3) - 1;\r
+ mx = ((dbyte >> 2) & 3) - 1;\r
+ buttons = (dbyte >> 4) & 3;\r
+\r
+ if (!(--DemoBuffer[DemoOffset]))\r
+ {\r
+ DemoOffset += 2;\r
+ if (DemoOffset >= DemoSize)\r
+ DemoMode = demo_PlayDone;\r
+ }\r
+\r
+ realdelta = false;\r
+ }\r
+ else if (DemoMode == demo_PlayDone)\r
+ Quit("Demo playback exceeded");\r
+ else\r
+#endif\r
+ {\r
+ // MDM begin (GAMERS EDGE) - added this block\r
+ ControlTypeUsed = ctrl_None;\r
+\r
+ // Handle mouse input...\r
+ //\r
+ if ((MousePresent) && (ControlTypeUsed == ctrl_None))\r
+ {\r
+ INL_GetMouseDelta(&dx,&dy);\r
+ buttons = INL_GetMouseButtons();\r
+ realdelta = true;\r
+ if (dx || dy || buttons)\r
+ ControlTypeUsed = ctrl_Mouse;\r
+ }\r
+\r
+ // Handle joystick input...\r
+ //\r
+ if ((JoystickCalibrated) && (ControlTypeUsed == ctrl_None))\r
+ {\r
+ type = ctrl_Joystick1;\r
+ INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+ buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+ realdelta = true;\r
+ if (dx || dy || buttons)\r
+ ControlTypeUsed = ctrl_Joystick;\r
+ }\r
+\r
+ // Handle keyboard input...\r
+ //\r
+ if (ControlTypeUsed == ctrl_None)\r
+ {\r
+ type = ctrl_Keyboard1;\r
+ def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+ if (Keyboard[def->upleft])\r
+ mx = motion_Left,my = motion_Up;\r
+ else if (Keyboard[def->upright])\r
+ mx = motion_Right,my = motion_Up;\r
+ else if (Keyboard[def->downleft])\r
+ mx = motion_Left,my = motion_Down;\r
+ else if (Keyboard[def->downright])\r
+ mx = motion_Right,my = motion_Down;\r
+\r
+ if (Keyboard[def->up])\r
+ my = motion_Up;\r
+ else if (Keyboard[def->down])\r
+ my = motion_Down;\r
+\r
+ if (Keyboard[def->left])\r
+ mx = motion_Left;\r
+ else if (Keyboard[def->right])\r
+ mx = motion_Right;\r
+\r
+ if (Keyboard[def->button0])\r
+ buttons += 1 << 0;\r
+ if (Keyboard[def->button1])\r
+ buttons += 1 << 1;\r
+ realdelta = false;\r
+ if (mx || my || buttons)\r
+ ControlTypeUsed = ctrl_Keyboard;\r
+ } // MDM end (GAMERS EDGE)\r
+ }\r
+\r
+ if (realdelta)\r
+ {\r
+ mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+ my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+ }\r
+ else\r
+ {\r
+ dx = mx * 127;\r
+ dy = my * 127;\r
+ }\r
+\r
+ info->x = dx;\r
+ info->xaxis = mx;\r
+ info->y = dy;\r
+ info->yaxis = my;\r
+ info->button0 = buttons & (1 << 0);\r
+ info->button1 = buttons & (1 << 1);\r
+ info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+#if 0\r
+ if (DemoMode == demo_Record)\r
+ {\r
+ // Pack the control info into a byte\r
+ dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+ if\r
+ (\r
+ (DemoBuffer[DemoOffset + 1] == dbyte)\r
+ && (DemoBuffer[DemoOffset] < 255)\r
+ )\r
+ (DemoBuffer[DemoOffset])++;\r
+ else\r
+ {\r
+ if (DemoOffset || DemoBuffer[DemoOffset])\r
+ DemoOffset += 2;\r
+\r
+ if (DemoOffset >= DemoSize)\r
+ Quit("Demo buffer overflow");\r
+\r
+ DemoBuffer[DemoOffset] = 1;\r
+ DemoBuffer[DemoOffset + 1] = dbyte;\r
+ }\r
+ }\r
+#endif\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_ReadControl() - Reads the device associated with the specified\r
+// player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+ boolean realdelta;\r
+ byte dbyte;\r
+ word buttons;\r
+ int dx,dy;\r
+ Motion mx,my;\r
+ ControlType type;\r
+register KeyboardDef *def;\r
+\r
+ dx = dy = 0;\r
+ mx = my = motion_None;\r
+ buttons = 0;\r
+\r
+#if 0\r
+ if (DemoMode == demo_Playback)\r
+ {\r
+ dbyte = DemoBuffer[DemoOffset + 1];\r
+ my = (dbyte & 3) - 1;\r
+ mx = ((dbyte >> 2) & 3) - 1;\r
+ buttons = (dbyte >> 4) & 3;\r
+\r
+ if (!(--DemoBuffer[DemoOffset]))\r
+ {\r
+ DemoOffset += 2;\r
+ if (DemoOffset >= DemoSize)\r
+ DemoMode = demo_PlayDone;\r
+ }\r
+\r
+ realdelta = false;\r
+ }\r
+ else if (DemoMode == demo_PlayDone)\r
+ Quit("Demo playback exceeded");\r
+ else\r
+#endif\r
+ {\r
+ switch (type = Controls[player])\r
+ {\r
+ case ctrl_Keyboard1:\r
+ case ctrl_Keyboard2:\r
+ def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+ if (Keyboard[def->upleft])\r
+ mx = motion_Left,my = motion_Up;\r
+ else if (Keyboard[def->upright])\r
+ mx = motion_Right,my = motion_Up;\r
+ else if (Keyboard[def->downleft])\r
+ mx = motion_Left,my = motion_Down;\r
+ else if (Keyboard[def->downright])\r
+ mx = motion_Right,my = motion_Down;\r
+\r
+ if (Keyboard[def->up])\r
+ my = motion_Up;\r
+ else if (Keyboard[def->down])\r
+ my = motion_Down;\r
+\r
+ if (Keyboard[def->left])\r
+ mx = motion_Left;\r
+ else if (Keyboard[def->right])\r
+ mx = motion_Right;\r
+\r
+ if (Keyboard[def->button0])\r
+ buttons += 1 << 0;\r
+ if (Keyboard[def->button1])\r
+ buttons += 1 << 1;\r
+ realdelta = false;\r
+ break;\r
+ case ctrl_Joystick1:\r
+ case ctrl_Joystick2:\r
+ INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+ buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+ realdelta = true;\r
+ break;\r
+ case ctrl_Mouse:\r
+ INL_GetMouseDelta(&dx,&dy);\r
+ buttons = INL_GetMouseButtons();\r
+ realdelta = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (realdelta)\r
+ {\r
+ mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+ my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+ }\r
+ else\r
+ {\r
+ dx = mx * 127;\r
+ dy = my * 127;\r
+ }\r
+\r
+ info->x = dx;\r
+ info->xaxis = mx;\r
+ info->y = dy;\r
+ info->yaxis = my;\r
+ info->button0 = buttons & (1 << 0);\r
+ info->button1 = buttons & (1 << 1);\r
+ info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+#if 0\r
+ if (DemoMode == demo_Record)\r
+ {\r
+ // Pack the control info into a byte\r
+ dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+ if\r
+ (\r
+ (DemoBuffer[DemoOffset + 1] == dbyte)\r
+ && (DemoBuffer[DemoOffset] < 255)\r
+ )\r
+ (DemoBuffer[DemoOffset])++;\r
+ else\r
+ {\r
+ if (DemoOffset || DemoBuffer[DemoOffset])\r
+ DemoOffset += 2;\r
+\r
+ if (DemoOffset >= DemoSize)\r
+ Quit("Demo buffer overflow");\r
+\r
+ DemoBuffer[DemoOffset] = 1;\r
+ DemoBuffer[DemoOffset + 1] = dbyte;\r
+ }\r
+ }\r
+#endif\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_SetControlType() - Sets the control type to be used by the specified\r
+// player\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetControlType(int player,ControlType type)\r
+{\r
+ // DEBUG - check that requested type is present?\r
+ Controls[player] = type;\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StartDemoRecord() - Starts the demo recording, using a buffer the\r
+// size passed. Returns if the buffer allocation was successful\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_StartDemoRecord(word bufsize)\r
+{\r
+ if (!bufsize)\r
+ return(false);\r
+\r
+ MM_GetPtr((memptr *)&DemoBuffer,bufsize);\r
+ DemoMode = demo_Record;\r
+ DemoSize = bufsize & ~1;\r
+ DemoOffset = 0;\r
+ DemoBuffer[0] = DemoBuffer[1] = 0;\r
+\r
+ return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StartDemoPlayback() - Plays back the demo pointed to of the given size\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StartDemoPlayback(byte /*_1seg*/ *buffer,word bufsize)\r
+{\r
+ DemoBuffer = buffer;\r
+ DemoMode = demo_Playback;\r
+ DemoSize = bufsize & ~1;\r
+ DemoOffset = 0;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_StopDemo() - Turns off demo mode\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StopDemo(void)\r
+{\r
+ if ((DemoMode == demo_Record) && DemoOffset)\r
+ DemoOffset += 2;\r
+\r
+ DemoMode = demo_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_FreeDemoBuffer(void)\r
+{\r
+ if (DemoBuffer)\r
+ MM_FreePtr((memptr *)&DemoBuffer);\r
+}\r
+#endif\r
+\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_GetScanName() - Returns a string containing the name of the\r
+// specified scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+byte *\r
+IN_GetScanName(ScanCode scan)\r
+{\r
+ byte **p;\r
+ ScanCode far *s;\r
+\r
+ for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++)\r
+ if (*s == scan)\r
+ return(*p);\r
+\r
+ return(ScanNames[scan]);\r
+}\r
+#endif\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_WaitForKey() - Waits for a scan code, then clears LastScan and\r
+// returns the scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+ScanCode\r
+IN_WaitForKey(void)\r
+{\r
+ ScanCode result;\r
+\r
+ while (!(result = LastScan))\r
+ ;\r
+ LastScan = 0;\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and\r
+// returns the ASCII value\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char\r
+IN_WaitForASCII(void)\r
+{\r
+ char result;\r
+\r
+ while (!(result = LastASCII))\r
+ ;\r
+ LastASCII = '\0';\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_AckBack() - Waits for either an ASCII keypress or a button press\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_AckBack(void)\r
+{\r
+ word i;\r
+\r
+ while (!LastScan)\r
+ {\r
+ if (MousePresent)\r
+ {\r
+ if (INL_GetMouseButtons())\r
+ {\r
+ while (INL_GetMouseButtons())\r
+ ;\r
+ return;\r
+ }\r
+ }\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ {\r
+ if (JoysPresent[i])\r
+ {\r
+ if (IN_GetJoyButtonsDB(i))\r
+ {\r
+ while (IN_GetJoyButtonsDB(i))\r
+ ;\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ IN_ClearKey(LastScan);\r
+ LastScan = sc_None;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_Ack() - Clears user input & then calls IN_AckBack()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Ack(void)\r
+{\r
+ word i;\r
+\r
+ IN_ClearKey(LastScan);\r
+ LastScan = sc_None;\r
+\r
+ if (MousePresent)\r
+ while (INL_GetMouseButtons())\r
+ ;\r
+ for (i = 0;i < MaxJoys;i++)\r
+ if (JoysPresent[i])\r
+ while (IN_GetJoyButtonsDB(i))\r
+ ;\r
+\r
+ IN_AckBack();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_IsUserInput() - Returns true if a key has been pressed or a button\r
+// is down\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_IsUserInput(void)\r
+{\r
+ boolean result;\r
+ word i;\r
+\r
+ result = LastScan;\r
+\r
+ if (MousePresent)\r
+ if (INL_GetMouseButtons())\r
+ result = true;\r
+\r
+ for (i = 0;i < MaxJoys;i++)\r
+ if (JoysPresent[i])\r
+ if (INL_GetJoyButtons(i))\r
+ result = true;\r
+\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// IN_UserInput() - Waits for the specified delay time (in ticks) or the\r
+// user pressing a key or a mouse button. If the clear flag is set, it\r
+// then either clears the key or waits for the user to let the mouse\r
+// button up.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_UserInput(longword delay,boolean clear)\r
+{\r
+ longword lasttime;\r
+\r
+ lasttime = TimeCount;\r
+ do\r
+ {\r
+ if (IN_IsUserInput())\r
+ {\r
+ if (clear)\r
+ IN_AckBack();\r
+ return(true);\r
+ }\r
+ } while (TimeCount - lasttime < delay);\r
+ return(false);\r
+}\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_IN.h - Header file for Input Manager\r
+// v1.0d1\r
+// By Jason Blochowiak\r
+//\r
+
+#include "lib_head.h"
+\r
+#ifndef __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef __ID_IN__\r
+#define __ID_IN__\r
+\r
+#ifdef __DEBUG__\r
+#define __DEBUG_InputMgr__\r
+#endif\r
+\r
+#define MaxPlayers 4\r
+#define MaxKbds 2\r
+#define MaxJoys 2\r
+#define NumCodes 128\r
+\r
+typedef byte ScanCode;\r
+#define sc_None 0\r
+#define sc_Bad 0xff\r
+#define sc_Return 0x1c\r
+#define sc_Enter sc_Return\r
+#define sc_Escape 0x01\r
+#define sc_Space 0x39\r
+#define sc_BackSpace 0x0e\r
+#define sc_Tab 0x0f\r
+#define sc_Alt 0x38\r
+#define sc_Control 0x1d\r
+#define sc_CapsLock 0x3a\r
+#define sc_LShift 0x2a\r
+#define sc_RShift 0x36\r
+#define sc_UpArrow 0x48\r
+#define sc_DownArrow 0x50\r
+#define sc_LeftArrow 0x4b\r
+#define sc_RightArrow 0x4d\r
+#define sc_Insert 0x52\r
+#define sc_Delete 0x53\r
+#define sc_Home 0x47\r
+#define sc_End 0x4f\r
+#define sc_PgUp 0x49\r
+#define sc_PgDn 0x51\r
+#define sc_F1 0x3b\r
+#define sc_F2 0x3c\r
+#define sc_F3 0x3d\r
+#define sc_F4 0x3e\r
+#define sc_F5 0x3f\r
+#define sc_F6 0x40\r
+#define sc_F7 0x41\r
+#define sc_F8 0x42\r
+#define sc_F9 0x43\r
+#define sc_F10 0x44\r
+#define sc_F11 0x57\r
+#define sc_F12 0x59\r
+\r
+#define sc_A 0x1e\r
+#define sc_B 0x30\r
+#define sc_C 0x2e\r
+#define sc_D 0x20\r
+#define sc_E 0x12\r
+#define sc_F 0x21\r
+#define sc_G 0x22\r
+#define sc_H 0x23\r
+#define sc_I 0x17\r
+#define sc_J 0x24\r
+#define sc_K 0x25\r
+#define sc_L 0x26\r
+#define sc_M 0x32\r
+#define sc_N 0x31\r
+#define sc_O 0x18\r
+#define sc_P 0x19\r
+#define sc_Q 0x10\r
+#define sc_R 0x13\r
+#define sc_S 0x1f\r
+#define sc_T 0x14\r
+#define sc_U 0x16\r
+#define sc_V 0x2f\r
+#define sc_W 0x11\r
+#define sc_X 0x2d\r
+#define sc_Y 0x15\r
+#define sc_Z 0x2c\r
+\r
+#define key_None 0\r
+#define key_Return 0x0d\r
+#define key_Enter key_Return\r
+#define key_Escape 0x1b\r
+#define key_Space 0x20\r
+#define key_BackSpace 0x08\r
+#define key_Tab 0x09\r
+#define key_Delete 0x7f\r
+\r
+// Stuff for the mouse\r
+#define MReset 0\r
+#define MButtons 3\r
+#define MDelta 11\r
+\r
+#define MouseInt 0x33\r
+#define Mouse(x) _AX = x,geninterrupt(MouseInt)\r
+\r
+typedef enum {\r
+ demo_Off,demo_Record,demo_Playback,demo_PlayDone\r
+ } Demo;\r
+typedef enum {\r
+ ctrl_None, // MDM (GAMERS EDGE) - added\r
+ ctrl_Keyboard,\r
+ ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2,\r
+ ctrl_Joystick,\r
+ ctrl_Joystick1 = ctrl_Joystick,ctrl_Joystick2,\r
+ ctrl_Mouse\r
+ } ControlType;\r
+typedef enum {\r
+ motion_Left = -1,motion_Up = -1,\r
+ motion_None = 0,\r
+ motion_Right = 1,motion_Down = 1\r
+ } Motion;\r
+typedef enum {\r
+ dir_North,dir_NorthEast,\r
+ dir_East,dir_SouthEast,\r
+ dir_South,dir_SouthWest,\r
+ dir_West,dir_NorthWest,\r
+ dir_None\r
+ } Direction;\r
+typedef struct {\r
+ boolean button0,button1;\r
+ int x,y;\r
+ Motion xaxis,yaxis;\r
+ Direction dir;\r
+ } CursorInfo;\r
+typedef CursorInfo ControlInfo;\r
+typedef struct {\r
+ ScanCode button0,button1,\r
+ upleft, up, upright,\r
+ left, right,\r
+ downleft, down, downright;\r
+ } KeyboardDef;\r
+typedef struct {\r
+ word joyMinX,joyMinY,\r
+ threshMinX,threshMinY,\r
+ threshMaxX,threshMaxY,\r
+ joyMaxX,joyMaxY,\r
+ joyMultXL,joyMultYL,\r
+ joyMultXH,joyMultYH;\r
+ } JoystickDef;\r
+// Global variables\r
+extern boolean Keyboard[],\r
+ MousePresent,\r
+ JoysPresent[];\r
+extern boolean Paused;\r
+extern char LastASCII;\r
+extern ScanCode LastScan;\r
+extern KeyboardDef KbdDefs[];\r
+extern JoystickDef JoyDefs[];\r
+extern ControlType Controls[MaxPlayers];\r
+\r
+extern boolean JoystickCalibrated; // MDM (GAMERS EDGE) - added\r
+extern ControlType ControlTypeUsed; // MDM (GAMERS EDGE) - added\r
+\r
+extern Demo DemoMode;\r
+extern byte _seg *DemoBuffer;\r
+extern word DemoOffset,DemoSize;\r
+\r
+// Function prototypes\r
+#define IN_KeyDown(code) (Keyboard[(code)])\r
+#define IN_ClearKey(code) {Keyboard[code] = false;\\r
+ if (code == LastScan) LastScan = sc_None;}\r
+\r
+// DEBUG - put names in prototypes\r
+extern void IN_Startup(void),IN_Shutdown(void),\r
+ IN_Default(boolean gotit,ControlType in),\r
+ IN_SetKeyHook(void (*)()),\r
+ IN_ClearKeysDown(void),\r
+ IN_ReadCursor(CursorInfo *),\r
+ IN_ReadControl(int,ControlInfo *),\r
+ IN_SetControlType(int,ControlType),\r
+ IN_GetJoyAbs(word joy,word *xp,word *yp),\r
+ IN_SetupJoy(word joy,word minx,word maxx,\r
+ word miny,word maxy),\r
+ IN_StartDemoPlayback(byte _seg *buffer,word bufsize),\r
+ IN_StopDemo(void),IN_FreeDemoBuffer(void),\r
+ IN_Ack(void),IN_AckBack(void);\r
+extern boolean IN_UserInput(longword delay,boolean clear),\r
+ IN_IsUserInput(void),\r
+ IN_StartDemoRecord(word bufsize);\r
+extern byte *IN_GetScanName(ScanCode);\r
+extern char IN_WaitForASCII(void);\r
+extern ScanCode IN_WaitForKey(void);\r
+extern word IN_GetJoyButtonsDB(word joy);\r
+\r
+#endif\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// NEWMM.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+ ID software memory manager\r
+ --------------------------\r
+\r
+Primary coder: John Carmack\r
+\r
+RELIES ON\r
+---------\r
+Quit (char *error) function\r
+\r
+\r
+WORK TO DO\r
+----------\r
+MM_SizePtr to change the size of a given pointer\r
+\r
+Multiple purge levels utilized\r
+\r
+EMS / XMS unmanaged routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+//#include "LIB_HEAD.H"
+#include "ID_MM.H"
+//#pragma hdrstop\r
+\r
+//#pragma warn -pro\r
+//#pragma warn -use\r
+\r
+\r
+#if 1 // 1 == Debug/Dev ; 0 == Production/final\r
+\r
+#define OUT_OF_MEM_MSG "MM_GetPtr: Out of memory!\nYou were short :%ld bytes"\r
+\r
+#else\r
+\r
+\r
+#define OUT_OF_MEM_MSG "\npee\n"
+\r
+#endif\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL INFO\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define LOCKBIT 0x80 // if set in attributes, block cannot be moved\r
+#define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first\r
+#define PURGEMASK 0xfffc\r
+#define BASEATTRIBUTES 0 // unlocked, non purgable\r
+\r
+#define MAXUMBS 10\r
+\r
+typedef struct mmblockstruct\r
+{\r
+ unsigned start,length;\r
+ unsigned attributes;\r
+ memptr *useptr; // pointer to the segment start\r
+ struct mmblockstruct far *next;\r
+} mmblocktype;\r
+\r
+\r
+//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;}\r
+\r
+#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}\r
+\r
+#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+mminfotype mminfo;\r
+memptr bufferseg;\r
+boolean mmerror;\r
+\r
+void (* beforesort) (void);\r
+void (* aftersort) (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean mmstarted;\r
+
+void huge *hugeheap;\r
+void far *farheap;\r
+void *nearheap;\r
+\r
+mmblocktype far mmblocks[MAXBLOCKS]\r
+ ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;\r
+\r
+boolean bombonerror;\r
+\r
+unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;\r
+\r
+void (* XMSaddr) (void); // far pointer to XMS driver\r
+\r
+unsigned numUMBs,UMBbase[MAXUMBS];\r
+\r
+//==========================================================================\r
+\r
+//\r
+// local prototypes\r
+//\r
+\r
+boolean MML_CheckForEMS (void);\r
+void MML_ShutdownEMS (void);\r
+void MM_MapEMS (void);\r
+boolean MML_CheckForXMS (void);\r
+void MML_ShutdownXMS (void);\r
+void MML_UseSpace (unsigned segstart, unsigned seglength);\r
+void MML_ClearBlock (void);\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForEMS\r
+=\r
+= Routine from p36 of Extending DOS\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForEMS (void)\r
+{
+ boolean emmcfems;
+ char emmname[] = "EMMXXXX0";
+
+ __asm {\r
+ mov dx,OFFSET emmname[0]\r
+ mov ax,0x3d00\r
+ int 0x21 // try to open EMMXXXX0 device\r
+ jc error\r
+\r
+ mov bx,ax\r
+ mov ax,0x4400\r
+\r
+ int 0x21 // get device info\r
+ jc error\r
+\r
+ and dx,0x80\r
+ jz error\r
+\r
+ mov ax,0x4407\r
+\r
+ int 0x21 // get status\r
+ jc error\r
+ or al,al\r
+ jz error\r
+\r
+ mov ah,0x3e\r
+ int 0x21 // close handle\r
+ jc error
+ //\r
+ // EMS is good\r
+ //
+ mov emmcfems,1
+ jmp End
+ error:
+ //\r
+ // EMS is bad\r
+ //\r
+ mov emmcfems,0
+ End:
+ }
+ return(emmcfems);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupEMS (void)\r
+{\r
+ char str[80],str2[10];\r
+ unsigned error;\r
+\r
+ totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;\r
+\r
+ __asm {\r
+ mov ah,EMS_STATUS\r
+ int EMS_INT // make sure EMS hardware is present\r
+ or ah,ah\r
+ jnz error\r
+\r
+ mov ah,EMS_VERSION\r
+ int EMS_INT\r
+ or ah,ah\r
+ jnz error\r
+ cmp al,0x32 // only work on ems 3.2 or greater\r
+ jb error\r
+\r
+ mov ah,EMS_GETFRAME\r
+ int EMS_INT // find the page frame address\r
+ or ah,ah\r
+ jnz error\r
+ mov [EMSpageframe],bx\r
+\r
+ mov ah,EMS_GETPAGES\r
+ int EMS_INT // find out how much EMS is there\r
+ or ah,ah\r
+ jnz error\r
+ mov [totalEMSpages],dx\r
+ mov [freeEMSpages],bx\r
+ or bx,bx\r
+ jz noEMS // no EMS at all to allocate\r
+\r
+ cmp bx,4\r
+ jle getpages // there is only 1,2,3,or 4 pages\r
+ mov bx,4 // we can't use more than 4 pages\r
+ }\r
+\r
+getpages:\r
+asm {\r
+ mov [EMSpagesmapped],bx\r
+ mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS\r
+ int EMS_INT\r
+ or ah,ah\r
+ jnz error\r
+ mov [EMShandle],dx\r
+ }\r
+ return;\r
+\r
+error:\r
+ error = _AH;\r
+ strcpy (str,"MML_SetupEMS: EMS error 0x");\r
+ itoa(error,str2,16);\r
+ strcpy (str,str2);\r
+ Quit (str);\r
+\r
+noEMS:\r
+;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_ShutdownEMS (void)\r
+{\r
+ if (!EMShandle)\r
+ return;\r
+\r
+ {\r
+ mov ah,EMS_FREEPAGES\r
+ mov dx,[EMShandle]\r
+ int EMS_INT\r
+ or ah,ah\r
+ jz ok\r
+ }\r
+\r
+ Quit ("MML_ShutdownEMS: Error freeing EMS!");\r
+\r
+ok:\r
+;\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= MM_MapEMS\r
+=\r
+= Maps the 64k of EMS used by memory manager into the page frame\r
+= for general use. This only needs to be called if you are keeping\r
+= other things in EMS.\r
+=\r
+====================\r
+*/\r
+\r
+void MM_MapEMS (void)\r
+{\r
+ char str[80],str2[10];\r
+ unsigned error;\r
+ int i;\r
+\r
+ for (i=0;i<EMSpagesmapped;i++)\r
+ {\r
+ {\r
+ mov ah,EMS_MAPPAGE\r
+ mov bx,[i] // logical page\r
+ mov al,bl // physical page\r
+ mov dx,[EMShandle] // handle\r
+ int EMS_INT\r
+ or ah,ah\r
+ jnz error\r
+ }\r
+ }\r
+\r
+ return;\r
+\r
+error:\r
+ error = _AH;\r
+ strcpy (str,"MM_MapEMS: EMS error 0x");\r
+ itoa(error,str2,16);\r
+ strcpy (str,str2);\r
+ Quit (str);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForXMS\r
+=\r
+= Check for XMM driver\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForXMS (void)\r
+{\r
+ numUMBs = 0;\r
+\r
+asm {\r
+ mov ax,0x4300\r
+ int 0x2f // query status of installed diver\r
+ cmp al,0x80\r
+ je good\r
+ }\r
+ return false;\r
+good:\r
+ return true;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupXMS\r
+=\r
+= Try to allocate all upper memory block\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupXMS (void)\r
+{\r
+ unsigned base,size;\r
+\r
+ {\r
+ mov ax,0x4310\r
+ int 0x2f\r
+ mov [WORD PTR XMSaddr],bx\r
+ mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver\r
+ }\r
+\r
+getmemory:\r
+ {\r
+ mov ah,XMS_ALLOCUMB\r
+ mov dx,0xffff // try for largest block possible\r
+ call [DWORD PTR XMSaddr]\r
+ or ax,ax\r
+ jnz gotone\r
+\r
+ cmp bl,0xb0 // error: smaller UMB is available\r
+ jne done;\r
+\r
+ mov ah,XMS_ALLOCUMB\r
+ call [DWORD PTR XMSaddr] // DX holds largest available UMB\r
+ or ax,ax\r
+ jz done // another error...\r
+ }\r
+\r
+gotone:\r
+ {\r
+ mov [base],bx\r
+ mov [size],dx\r
+ }\r
+ MML_UseSpace (base,size);\r
+ mminfo.XMSmem += size*16;\r
+ UMBbase[numUMBs] = base;\r
+ numUMBs++;\r
+ if (numUMBs < MAXUMBS)\r
+ goto getmemory;\r
+\r
+done:;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownXMS\r
+=\r
+======================\r
+*/\r
+\r
+void MML_ShutdownXMS (void)\r
+{\r
+ int i;\r
+ unsigned base;\r
+\r
+ for (i=0;i<numUMBs;i++)\r
+ {\r
+ base = UMBbase[i];\r
+\r
+ mov ah,XMS_FREEUMB\r
+ mov dx,[base]\r
+ call [DWORD PTR XMSaddr]\r
+ }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_UseSpace\r
+=\r
+= Marks a range of paragraphs as usable by the memory manager\r
+= This is used to mark space for the near heap, far heap, ems page frame,\r
+= and upper memory blocks\r
+=\r
+======================\r
+*/\r
+\r
+void MML_UseSpace (unsigned segstart, unsigned seglength)\r
+{\r
+ mmblocktype far *scan,far *last;\r
+ unsigned oldend;\r
+ long extra;\r
+\r
+ scan = last = mmhead;\r
+ mmrover = mmhead; // reset rover to start of memory\r
+\r
+//\r
+// search for the block that contains the range of segments\r
+//\r
+ while (scan->start+scan->length < segstart)\r
+ {\r
+ last = scan;\r
+ scan = scan->next;\r
+ }\r
+\r
+//\r
+// take the given range out of the block\r
+//\r
+ oldend = scan->start + scan->length;\r
+ extra = oldend - (segstart+seglength);\r
+ if (extra < 0)\r
+ Quit ("MML_UseSpace: Segment spans two blocks!");\r
+\r
+ if (segstart == scan->start)\r
+ {\r
+ last->next = scan->next; // unlink block\r
+ FREEBLOCK(scan);\r
+ scan = last;\r
+ }\r
+ else\r
+ scan->length = segstart-scan->start; // shorten block\r
+\r
+ if (extra > 0)\r
+ {\r
+ GETNEWBLOCK;\r
+ mmnew->next = scan->next;\r
+ scan->next = mmnew;\r
+ mmnew->start = segstart+seglength;\r
+ mmnew->length = extra;\r
+ mmnew->attributes = LOCKBIT;\r
+ }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MML_ClearBlock\r
+=\r
+= We are out of blocks, so free a purgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MML_ClearBlock (void)\r
+{\r
+ mmblocktype far *scan,far *last;\r
+\r
+ scan = mmhead->next;\r
+\r
+ while (scan)\r
+ {\r
+ if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )\r
+ {\r
+ MM_FreePtr(scan->useptr);\r
+ return;\r
+ }\r
+ scan = scan->next;\r
+ }\r
+\r
+ Quit ("MM_ClearBlock: No purgable blocks!");\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= MM_Startup\r
+=\r
+= Grabs all space from turbo with malloc/farmalloc\r
+= Allocates bufferseg misc buffer\r
+=\r
+===================\r
+*/\r
+\r
+static char *ParmStrings[] = {"noems","noxms",""};\r
+\r
+void MM_Startup (void)\r
+{\r
+ int i;\r
+ unsigned long length;\r
+ void far *start;\r
+ unsigned segstart,seglength,endfree;\r
+\r
+ if (mmstarted)\r
+ MM_Shutdown ();\r
+\r
+\r
+ mmstarted = true;\r
+ bombonerror = true;\r
+//\r
+// set up the linked list (everything in the free list;\r
+//\r
+ mmhead = NULL;\r
+ mmfree = &mmblocks[0];\r
+ for (i=0;i<MAXBLOCKS-1;i++)\r
+ mmblocks[i].next = &mmblocks[i+1];\r
+ mmblocks[i].next = NULL;\r
+\r
+//\r
+// locked block of all memory until we punch out free space\r
+//\r
+ GETNEWBLOCK;\r
+ mmhead = mmnew; // this will allways be the first node\r
+ mmnew->start = 0;\r
+ mmnew->length = 0xffff;\r
+ mmnew->attributes = LOCKBIT;\r
+ mmnew->next = NULL;\r
+ mmrover = mmhead;\r
+\r
+\r
+//\r
+// get all available near conventional memory segments\r
+//\r
+ length=coreleft();\r
+ start = (void far *)(nearheap = malloc(length));\r
+\r
+ length -= 16-(FP_OFF(start)&15);\r
+ length -= SAVENEARHEAP;\r
+ seglength = length / 16; // now in paragraphs\r
+ segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+ MML_UseSpace (segstart,seglength);\r
+ mminfo.nearheap = length;\r
+\r
+//\r
+// get all available far conventional memory segments\r
+//\r
+ length=farcoreleft();\r
+ start = farheap = farmalloc(length);\r
+ length -= 16-(FP_OFF(start)&15);\r
+ length -= SAVEFARHEAP;\r
+ seglength = length / 16; // now in paragraphs\r
+ segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+ MML_UseSpace (segstart,seglength);\r
+ mminfo.farheap = length;\r
+ mminfo.mainmem = mminfo.nearheap + mminfo.farheap;\r
+\r
+\r
+//\r
+// detect EMS and allocate up to 64K at page frame\r
+//\r
+ mminfo.EMSmem = 0;\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+ goto emsskip; // param NOEMS\r
+ }\r
+\r
+ if (MML_CheckForEMS())\r
+ {\r
+ MML_SetupEMS(); // allocate space\r
+ MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);\r
+ MM_MapEMS(); // map in used pages\r
+ mminfo.EMSmem = EMSpagesmapped*0x4000l;\r
+ }\r
+\r
+//\r
+// detect XMS and get upper memory blocks\r
+//\r
+emsskip:\r
+ mminfo.XMSmem = 0;\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+ goto xmsskip; // param NOXMS\r
+ }\r
+\r
+ if (MML_CheckForXMS())\r
+ MML_SetupXMS(); // allocate as many UMBs as possible\r
+\r
+//\r
+// allocate the misc buffer\r
+//\r
+xmsskip:\r
+ mmrover = mmhead; // start looking for space after low block\r
+\r
+ MM_GetPtr (&bufferseg,BUFFERSIZE);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_Shutdown\r
+=\r
+= Frees all conventional, EMS, and XMS allocated\r
+=\r
+====================\r
+*/\r
+\r
+void MM_Shutdown (void)\r
+{\r
+ if (!mmstarted)\r
+ return;\r
+\r
+ farfree (farheap);\r
+ free (nearheap);\r
+ MML_ShutdownEMS ();\r
+ MML_ShutdownXMS ();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_GetPtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size)\r
+{\r
+ mmblocktype far *scan,far *lastscan,far *endscan\r
+ ,far *purge,far *next;\r
+ int search;\r
+ unsigned needed,startseg;\r
+\r
+ needed = (size+15)/16; // convert size from bytes to paragraphs\r
+\r
+ GETNEWBLOCK; // fill in start and next after a spot is found\r
+ mmnew->length = needed;\r
+ mmnew->useptr = baseptr;\r
+ mmnew->attributes = BASEATTRIBUTES;\r
+\r
+ for (search = 0; search<3; search++)\r
+ {\r
+ //\r
+ // first search: try to allocate right after the rover, then on up\r
+ // second search: search from the head pointer up to the rover\r
+ // third search: compress memory, then scan from start\r
+ if (search == 1 && mmrover == mmhead)\r
+ search++;\r
+\r
+ switch (search)\r
+ {\r
+ case 0:\r
+ lastscan = mmrover;\r
+ scan = mmrover->next;\r
+ endscan = NULL;\r
+ break;\r
+ case 1:\r
+ lastscan = mmhead;\r
+ scan = mmhead->next;\r
+ endscan = mmrover;\r
+ break;\r
+ case 2:\r
+ MM_SortMem ();\r
+ lastscan = mmhead;\r
+ scan = mmhead->next;\r
+ endscan = NULL;\r
+ break;\r
+ }\r
+\r
+ startseg = lastscan->start + lastscan->length;\r
+\r
+ while (scan != endscan)\r
+ {\r
+ if (scan->start - startseg >= needed)\r
+ {\r
+ //\r
+ // got enough space between the end of lastscan and\r
+ // the start of scan, so throw out anything in the middle\r
+ // and allocate the new block\r
+ //\r
+ purge = lastscan->next;\r
+ lastscan->next = mmnew;\r
+ mmnew->start = *(unsigned *)baseptr = startseg;\r
+ mmnew->next = scan;\r
+ while ( purge != scan)\r
+ { // free the purgable block\r
+ next = purge->next;\r
+ FREEBLOCK(purge);\r
+ purge = next; // purge another if not at scan\r
+ }\r
+ mmrover = mmnew;\r
+ return; // good allocation!\r
+ }\r
+\r
+ //\r
+ // if this block is purge level zero or locked, skip past it\r
+ //\r
+ if ( (scan->attributes & LOCKBIT)\r
+ || !(scan->attributes & PURGEBITS) )\r
+ {\r
+ lastscan = scan;\r
+ startseg = lastscan->start + lastscan->length;\r
+ }\r
+\r
+\r
+ scan=scan->next; // look at next line\r
+ }\r
+ }\r
+\r
+ if (bombonerror)\r
+ Quit (OUT_OF_MEM_MSG,(size-mminfo.nearheap));\r
+ else\r
+ mmerror = true;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_FreePtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_FreePtr (memptr *baseptr)\r
+{\r
+ mmblocktype far *scan,far *last;\r
+\r
+ last = mmhead;\r
+ scan = last->next;\r
+\r
+ if (baseptr == mmrover->useptr) // removed the last allocated block\r
+ mmrover = mmhead;\r
+\r
+ while (scan->useptr != baseptr && scan)\r
+ {\r
+ last = scan;\r
+ scan = scan->next;\r
+ }\r
+\r
+ if (!scan)\r
+ Quit ("MM_FreePtr: Block not found!");\r
+\r
+ last->next = scan->next;\r
+\r
+ FREEBLOCK(scan);\r
+}\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetPurge\r
+=\r
+= Sets the purge level for a block (locked blocks cannot be made purgable)\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge)\r
+{\r
+ mmblocktype far *start;\r
+\r
+ start = mmrover;\r
+\r
+ do\r
+ {\r
+ if (mmrover->useptr == baseptr)\r
+ break;\r
+\r
+ mmrover = mmrover->next;\r
+\r
+ if (!mmrover)\r
+ mmrover = mmhead;\r
+ else if (mmrover == start)\r
+ Quit ("MM_SetPurge: Block not found!");\r
+\r
+ } while (1);\r
+\r
+ mmrover->attributes &= ~PURGEBITS;\r
+ mmrover->attributes |= purge;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetLock\r
+=\r
+= Locks / unlocks the block\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetLock (memptr *baseptr, boolean locked)\r
+{\r
+ mmblocktype far *start;\r
+\r
+ start = mmrover;\r
+\r
+ do\r
+ {\r
+ if (mmrover->useptr == baseptr)\r
+ break;\r
+\r
+ mmrover = mmrover->next;\r
+\r
+ if (!mmrover)\r
+ mmrover = mmhead;\r
+ else if (mmrover == start)\r
+ Quit ("MM_SetLock: Block not found!");\r
+\r
+ } while (1);\r
+\r
+ mmrover->attributes &= ~LOCKBIT;\r
+ mmrover->attributes |= locked*LOCKBIT;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SortMem\r
+=\r
+= Throws out all purgable stuff and compresses movable blocks\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SortMem (void)\r
+{\r
+ mmblocktype far *scan,far *last,far *next;\r
+ unsigned start,length,source,dest,oldborder;\r
+ int playing;\r
+\r
+ //\r
+ // lock down a currently playing sound\r
+ //\r
+ playing = SD_SoundPlaying ();\r
+ if (playing)\r
+ {\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_PC:\r
+ playing += STARTPCSOUNDS;\r
+ break;\r
+ case sdm_AdLib:\r
+ playing += STARTADLIBSOUNDS;\r
+ break;\r
+ }\r
+ MM_SetLock(&(memptr)audiosegs[playing],true);\r
+ }\r
+\r
+\r
+ SD_StopSound();\r
+// oldborder = bordercolor;\r
+// VW_ColorBorder (15);\r
+\r
+ if (beforesort)\r
+ beforesort();\r
+\r
+ scan = mmhead;\r
+\r
+ last = NULL; // shut up compiler warning\r
+\r
+ while (scan)\r
+ {\r
+ if (scan->attributes & LOCKBIT)\r
+ {\r
+ //\r
+ // block is locked, so try to pile later blocks right after it\r
+ //\r
+ start = scan->start + scan->length;\r
+ }\r
+ else\r
+ {\r
+ if (scan->attributes & PURGEBITS)\r
+ {\r
+ //\r
+ // throw out the purgable block\r
+ //\r
+ next = scan->next;\r
+ FREEBLOCK(scan);\r
+ last->next = next;\r
+ scan = next;\r
+ continue;\r
+ }\r
+ else\r
+ {\r
+ //\r
+ // push the non purgable block on top of the last moved block\r
+ //\r
+ if (scan->start != start)\r
+ {\r
+ length = scan->length;\r
+ source = scan->start;\r
+ dest = start;\r
+ while (length > 0xf00)\r
+ {\r
+ movedata(source,0,dest,0,0xf00*16);\r
+ length -= 0xf00;\r
+ source += 0xf00;\r
+ dest += 0xf00;\r
+ }\r
+ movedata(source,0,dest,0,length*16);\r
+\r
+ scan->start = start;\r
+ *(unsigned *)scan->useptr = start;\r
+ }\r
+ start = scan->start + scan->length;\r
+ }\r
+ }\r
+\r
+ last = scan;\r
+ scan = scan->next; // go to next block\r
+ }\r
+\r
+ mmrover = mmhead;\r
+\r
+ if (aftersort)\r
+ aftersort();\r
+\r
+// VW_ColorBorder (oldborder);\r
+\r
+ if (playing)\r
+ MM_SetLock(&(memptr)audiosegs[playing],false);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+=====================\r
+=\r
+= MM_ShowMemory\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_ShowMemory (void)\r
+{\r
+ mmblocktype far *scan;\r
+ unsigned color,temp;\r
+ long end,owner;\r
+ char scratch[80],str[10];\r
+\r
+ VW_SetDefaultColors();\r
+ VW_SetLineWidth(40);\r
+ temp = bufferofs;\r
+ bufferofs = 0;\r
+ VW_SetScreen (0,0);\r
+\r
+ scan = mmhead;\r
+\r
+ end = -1;\r
+\r
+//CA_OpenDebug ();\r
+\r
+ while (scan)\r
+ {\r
+ if (scan->attributes & PURGEBITS)\r
+ color = 5; // dark purple = purgable\r
+ else\r
+ color = 9; // medium blue = non purgable\r
+ if (scan->attributes & LOCKBIT)\r
+ color = 12; // red = locked\r
+ if (scan->start<=end)\r
+ Quit ("MM_ShowMemory: Memory block order currupted!");\r
+ end = scan->start+scan->length-1;\r
+ VW_Hlin(scan->start,(unsigned)end,0,color);\r
+ VW_Plot(scan->start,0,15);\r
+ if (scan->next->start > end+1)\r
+ VW_Hlin(end+1,scan->next->start,0,0); // black = free\r
+\r
+#if 0\r
+strcpy (scratch,"Size:");\r
+ltoa ((long)scan->length*16,str,10);\r
+strcat (scratch,str);\r
+strcat (scratch,"\tOwner:0x");\r
+owner = (unsigned)scan->useptr;\r
+ultoa (owner,str,16);\r
+strcat (scratch,str);\r
+strcat (scratch,"\n");\r
+write (debughandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+ scan = scan->next;\r
+ }\r
+\r
+//CA_CloseDebug ();\r
+\r
+ IN_Ack();\r
+ VW_SetLineWidth(64);\r
+ bufferofs = temp;\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_UnusedMemory\r
+=\r
+= Returns the total free space without purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_UnusedMemory (void)\r
+{\r
+ unsigned free;\r
+ mmblocktype far *scan;\r
+\r
+ free = 0;\r
+ scan = mmhead;\r
+\r
+ while (scan->next)\r
+ {\r
+ free += scan->next->start - (scan->start + scan->length);\r
+ scan = scan->next;\r
+ }\r
+\r
+ return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_TotalFree\r
+=\r
+= Returns the total free space with purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_TotalFree (void)\r
+{\r
+ unsigned free;\r
+ mmblocktype far *scan;\r
+\r
+ free = 0;\r
+ scan = mmhead;\r
+\r
+ while (scan->next)\r
+ {\r
+ if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))\r
+ free += scan->length;\r
+ free += scan->next->start - (scan->start + scan->length);\r
+ scan = scan->next;\r
+ }\r
+\r
+ return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_BombOnError\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_BombOnError (boolean bomb)\r
+{\r
+ bombonerror = bomb;\r
+}\r
+\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_MM.H
+
+#ifndef __ID_EXMM__\r
+\r
+#define __ID_EXMM__
+
+#include "LIB_HEAD.H"\r
+\r
+#define SAVENEARHEAP 0x400 // space to leave in data segment\r
+#define SAVEFARHEAP 0 // space to leave in far heap\r
+\r
+#define BUFFERSIZE 0x1000 // miscelanious, allways available buffer\r
+\r
+#define MAXBLOCKS 600\r
+\r
+\r
+//--------\r
+\r
+#define EMS_INT 0x67\r
+\r
+#define EMS_STATUS 0x40\r
+#define EMS_GETFRAME 0x41\r
+#define EMS_GETPAGES 0x42\r
+#define EMS_ALLOCPAGES 0x43\r
+#define EMS_MAPPAGE 0x44\r
+#define EMS_FREEPAGES 0x45\r
+#define EMS_VERSION 0x46\r
+\r
+//--------\r
+\r
+#define XMS_VERSION 0x00\r
+\r
+#define XMS_ALLOCHMA 0x01\r
+#define XMS_FREEHMA 0x02\r
+\r
+#define XMS_GENABLEA20 0x03\r
+#define XMS_GDISABLEA20 0x04\r
+#define XMS_LENABLEA20 0x05\r
+#define XMS_LDISABLEA20 0x06\r
+#define XMS_QUERYA20 0x07\r
+\r
+#define XMS_QUERYREE 0x08\r
+#define XMS_ALLOC 0x09\r
+#define XMS_FREE 0x0A\r
+#define XMS_MOVE 0x0B\r
+#define XMS_LOCK 0x0C\r
+#define XMS_UNLOCK 0x0D\r
+#define XMS_GETINFO 0x0E\r
+#define XMS_RESIZE 0x0F\r
+\r
+#define XMS_ALLOCUMB 0x10\r
+#define XMS_FREEUMB 0x11\r
+\r
+//==========================================================================\r
+\r
+typedef void /*_seg*/ * memptr;\r
+\r
+typedef struct\r
+{\r
+ long nearheap,farheap,EMSmem,XMSmem,mainmem;\r
+} mminfotype;\r
+\r
+//==========================================================================\r
+\r
+extern mminfotype mminfo;\r
+extern memptr bufferseg;\r
+extern boolean mmerror;\r
+\r
+extern void (* beforesort) (void);\r
+extern void (* aftersort) (void);\r
+\r
+//==========================================================================\r
+\r
+void MM_Startup (void);\r
+void MM_Shutdown (void);\r
+void MM_MapEMS (void);\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size);\r
+void MM_FreePtr (memptr *baseptr);\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge);\r
+void MM_SetLock (memptr *baseptr, boolean locked);\r
+void MM_SortMem (void);\r
+\r
+void MM_ShowMemory (void);\r
+\r
+long MM_UnusedMemory (void);\r
+long MM_TotalFree (void);\r
+\r
+void MM_BombOnError (boolean bomb);
+
+#endif
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_RF.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+notes\r
+-----\r
+\r
+scrolling more than one tile / refresh forces a total redraw\r
+\r
+two overlapping sprites of equal priority can change drawing order when\r
+updated\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define SCREENTILESWIDE 20\r
+#define SCREENTILESHIGH 13\r
+\r
+#define SCREENSPACE (SCREENWIDTH*240)\r
+#define FREEEGAMEM (0x10000l-3l*SCREENSPACE)\r
+\r
+//\r
+// the update array must have enough space for two screens that can float\r
+// up two two tiles each way\r
+//\r
+// (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared\r
+// by word width instructions\r
+\r
+#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define UPDATESPARESIZE (UPDATEWIDE*2+4)\r
+#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+\r
+#define G_EGASX_SHIFT 7 // global >> ?? = screen x\r
+#define G_CGASX_SHIFT 6 // global >> ?? = screen x\r
+#define G_SY_SHIFT 4 // global >> ?? = screen y\r
+\r
+unsigned SX_T_SHIFT; // screen x >> ?? = tile EGA = 1, CGA = 2;\r
+#define SY_T_SHIFT 4 // screen y >> ?? = tile\r
+\r
+\r
+#define EGAPORTSCREENWIDE 42\r
+#define CGAPORTSCREENWIDE 84\r
+#define PORTSCREENHIGH 224\r
+\r
+#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define UPDATESPARESIZE (UPDATEWIDE*2+4)\r
+#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+\r
+#define MAXSCROLLEDGES 6\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL TYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+typedef struct spriteliststruct\r
+{\r
+ int screenx,screeny;\r
+ int width,height;\r
+\r
+ unsigned grseg,sourceofs,planesize;\r
+ drawtype draw;\r
+ unsigned tilex,tiley,tilewide,tilehigh;\r
+ int priority,updatecount;\r
+ struct spriteliststruct **prevptr,*nextsprite;\r
+} spritelisttype;\r
+\r
+\r
+typedef struct\r
+{\r
+ int screenx,screeny;\r
+ int width,height;\r
+} eraseblocktype;\r
+\r
+\r
+typedef struct\r
+{\r
+ unsigned current; // foreground tiles have high bit set\r
+ int count;\r
+} tiletype;\r
+\r
+\r
+typedef struct animtilestruct\r
+{\r
+ unsigned x,y,tile;\r
+ tiletype *chain;\r
+ unsigned far *mapplane;\r
+ struct animtilestruct **prevptr,*nexttile;\r
+} animtiletype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+unsigned tics;\r
+long lasttimecount;\r
+\r
+boolean compatability; // crippled refresh for wierdo SVGAs\r
+\r
+unsigned mapwidth,mapheight,mapbyteswide,mapwordswide\r
+ ,mapbytesextra,mapwordsextra;\r
+unsigned mapbwidthtable[MAXMAPHEIGHT];\r
+\r
+//\r
+// Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow\r
+// for fractional movement and acceleration.\r
+//\r
+// Tiles : Tile offsets from the upper left corner of the current map.\r
+//\r
+// Screen : Graphics level offsets from map origin, x in bytes, y in pixels.\r
+// originxscreen is the same spot as originxtile, just with extra precision\r
+// so graphics don't need to be done in tile boundaries.\r
+//\r
+\r
+unsigned originxglobal,originyglobal;\r
+unsigned originxtile,originytile;\r
+unsigned originxscreen,originyscreen;\r
+unsigned originmap;\r
+unsigned originxmin,originxmax,originymin,originymax;\r
+\r
+unsigned masterofs;\r
+\r
+//\r
+// Table of the offsets from bufferofs of each tile spot in the\r
+// view port. The extra wide tile should never be drawn, but the space\r
+// is needed to account for the extra 0 in the update arrays. Built by\r
+// RF_Startup\r
+//\r
+\r
+unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH];\r
+\r
+unsigned uwidthtable[PORTTILESHIGH]; // lookup instead of multiply\r
+\r
+byte update[2][UPDATESIZE];\r
+byte *updateptr,*baseupdateptr, // current start of update window\r
+ *updatestart[2],\r
+ *baseupdatestart[2];\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+static char scratch[20],str[80];\r
+\r
+tiletype allanims[MAXANIMTYPES];\r
+unsigned numanimchains;\r
+\r
+void (*refreshvector) (void);\r
+\r
+unsigned screenstart[3] =\r
+ {0,SCREENSPACE,SCREENSPACE*2};\r
+\r
+unsigned xpanmask; // prevent panning to odd pixels\r
+\r
+unsigned screenpage; // screen currently being displayed\r
+unsigned otherpage;\r
+\r
+\r
+spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],\r
+ *spritefreeptr;\r
+\r
+animtiletype animarray[MAXANIMTILES],*animhead,*animfreeptr;\r
+\r
+int animfreespot;\r
+\r
+eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];\r
+\r
+int hscrollblocks,vscrollblocks;\r
+int hscrolledge[MAXSCROLLEDGES],vscrolledge[MAXSCROLLEDGES];\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL PROTOTYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void RFL_NewTile (unsigned updateoffset);\r
+void RFL_MaskForegroundTiles (void);\r
+void RFL_UpdateTiles (void);\r
+\r
+void RFL_BoundScroll (int x, int y);\r
+void RFL_CalcOriginStuff (long x, long y);\r
+void RFL_ClearScrollBlocks (void);\r
+void RFL_InitSpriteList (void);\r
+void RFL_InitAnimList (void);\r
+void RFL_CheckForAnimTile (unsigned x, unsigned y);\r
+void RFL_AnimateTiles (void);\r
+void RFL_RemoveAnimsOnX (unsigned x);\r
+void RFL_RemoveAnimsOnY (unsigned y);\r
+void RFL_EraseBlocks (void);\r
+void RFL_UpdateSprites (void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GRMODE INDEPENDANT ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Startup\r
+=\r
+=====================\r
+*/\r
+\r
+static char *ParmStrings[] = {"comp",""};\r
+\r
+void RF_Startup (void)\r
+{\r
+ int i,x,y;\r
+ unsigned *blockstart;\r
+\r
+ if (grmode == EGAGR)\r
+ for (i = 1;i < _argc;i++)\r
+ if (US_CheckParm(_argv[i],ParmStrings) == 0)\r
+ {\r
+ compatability = true;\r
+ break;\r
+ }\r
+\r
+ for (i=0;i<PORTTILESHIGH;i++)\r
+ uwidthtable[i] = UPDATEWIDE*i;\r
+\r
+ originxmin = originymin = MAPBORDER*TILEGLOBAL;\r
+\r
+ eraselistptr[0] = &eraselist[0][0];\r
+ eraselistptr[1] = &eraselist[1][0];\r
+\r
+\r
+\r
+ if (grmode == EGAGR)\r
+ {\r
+ SX_T_SHIFT = 1;\r
+\r
+ baseupdatestart[0] = &update[0][UPDATESPARESIZE];\r
+ baseupdatestart[1] = &update[1][UPDATESPARESIZE];\r
+\r
+ screenpage = 0;\r
+ otherpage = 1;\r
+ displayofs = screenstart[screenpage];\r
+ bufferofs = screenstart[otherpage];\r
+ masterofs = screenstart[2];\r
+\r
+ updateptr = baseupdatestart[otherpage];\r
+\r
+ blockstart = &blockstarts[0];\r
+ for (y=0;y<UPDATEHIGH;y++)\r
+ for (x=0;x<UPDATEWIDE;x++)\r
+ *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+\r
+ xpanmask = 6; // dont pan to odd pixels\r
+ }\r
+\r
+ else if (grmode == CGAGR)\r
+ {\r
+ SX_T_SHIFT = 2;\r
+\r
+ updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];\r
+\r
+ bufferofs = 0;\r
+ masterofs = 0x8000;\r
+\r
+ blockstart = &blockstarts[0];\r
+ for (y=0;y<UPDATEHIGH;y++)\r
+ for (x=0;x<UPDATEWIDE;x++)\r
+ *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+ }\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Shutdown\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Shutdown (void)\r
+{\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_FixOfs\r
+=\r
+= Sets bufferofs,displayofs, and masterofs to regular values, for the\r
+= occasions when you have moved them around manually\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_FixOfs (void)\r
+{\r
+ if (grmode == EGAGR)\r
+ {\r
+ screenpage = 0;\r
+ otherpage = 1;\r
+ panx = pany = pansx = pansy = panadjust = 0;\r
+ displayofs = screenstart[screenpage];\r
+ bufferofs = screenstart[otherpage];\r
+ masterofs = screenstart[2];\r
+ VW_SetScreen (displayofs,0);\r
+ }\r
+ else\r
+ {\r
+ bufferofs = 0;\r
+ masterofs = 0x8000;\r
+ }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewMap\r
+=\r
+= Makes some convienient calculations based on maphead->\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewMap (void)\r
+{\r
+ int i,x,y;\r
+ unsigned spot,*table;\r
+\r
+ mapwidth = mapheaderseg[mapon]->width;\r
+ mapbyteswide = 2*mapwidth;\r
+ mapheight = mapheaderseg[mapon]->height;\r
+ mapwordsextra = mapwidth-PORTTILESWIDE;\r
+ mapbytesextra = 2*mapwordsextra;\r
+\r
+//\r
+// make a lookup table for the maps left edge\r
+//\r
+ if (mapheight > MAXMAPHEIGHT)\r
+ Quit ("RF_NewMap: Map too tall!");\r
+ spot = 0;\r
+ for (i=0;i<mapheight;i++)\r
+ {\r
+ mapbwidthtable[i] = spot;\r
+ spot += mapbyteswide;\r
+ }\r
+\r
+//\r
+// fill in updatemapofs with the new width info\r
+//\r
+ table = &updatemapofs[0];\r
+ for (y=0;y<PORTTILESHIGH;y++)\r
+ for (x=0;x<UPDATEWIDE;x++)\r
+ *table++ = mapbwidthtable[y]+x*2;\r
+\r
+//\r
+// the y max value clips off the bottom half of a tile so a map that is\r
+// 13 + MAPBORDER*2 tile high will not scroll at all vertically\r
+//\r
+ originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;\r
+ originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;\r
+ if (originxmax<originxmin) // for very small maps\r
+ originxmax=originxmin;\r
+ if (originymax<originymin)\r
+ originymax=originymin;\r
+\r
+//\r
+// clear out the lists\r
+//\r
+ RFL_InitSpriteList ();\r
+ RFL_InitAnimList ();\r
+ RFL_ClearScrollBlocks ();\r
+ RF_SetScrollBlock (0,MAPBORDER-1,true);\r
+ RF_SetScrollBlock (0,mapheight-MAPBORDER,true);\r
+ RF_SetScrollBlock (MAPBORDER-1,0,false);\r
+ RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);\r
+\r
+\r
+ lasttimecount = TimeCount; // setup for adaptive timing\r
+ tics = 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= RF_MarkTileGraphics\r
+=\r
+= Goes through mapplane[0/1] and marks all background/foreground tiles\r
+= needed, then follows all animation sequences to make sure animated\r
+= tiles get all the stages. Every unique animating tile is given an\r
+= entry in allanims[], so every instance of that tile will animate at the\r
+= same rate. The info plane for each animating tile will hold a pointer\r
+= into allanims[], therefore you can't have both an animating foreground\r
+= and background tile in the same spot!\r
+=\r
+==========================\r
+*/\r
+\r
+void RF_MarkTileGraphics (void)\r
+{\r
+ unsigned size;\r
+ int tile,next,anims,change;\r
+ unsigned far *start,far *end,far *info;\r
+ unsigned i,tilehigh;\r
+ char str[80],str2[10];\r
+\r
+ memset (allanims,0,sizeof(allanims));\r
+ numanimchains = 0;\r
+\r
+ size = mapwidth*mapheight;\r
+\r
+//\r
+// background plane\r
+//\r
+ start = mapsegs[0];\r
+ info = mapsegs[2];\r
+ end = start+size;\r
+ do\r
+ {\r
+ tile = *start++;\r
+ if (tile>=0) // <0 is a tile that is never drawn\r
+ {\r
+ CA_MarkGrChunk(STARTTILE16+tile);\r
+ if (tinf[ANIM+tile])\r
+ {\r
+ // this tile will animated\r
+\r
+ if (tinf[SPEED+tile])\r
+ {\r
+ if (!tinf[ANIM+tile])\r
+ {\r
+ strcpy (str,"RF_MarkTileGraphics: Background anim of 0:");\r
+ itoa (tile,str2,10);\r
+ strcat (str,str2);\r
+ Quit (str);\r
+ }\r
+ for (i=0;i<numanimchains;i++)\r
+ if (allanims[i].current == tile)\r
+ {\r
+ *info = (unsigned)&allanims[i];\r
+ goto nextback;\r
+ }\r
+\r
+ // new chain of animating tiles\r
+\r
+ if (i>=MAXANIMTYPES)\r
+ Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
+ allanims[i].current = tile;\r
+ allanims[i].count = tinf[SPEED+tile];\r
+ *info = (unsigned)&allanims[i];\r
+ numanimchains++;\r
+ }\r
+\r
+ anims = 0;\r
+ change = (signed char)(tinf[ANIM+tile]);\r
+ next = tile+change;\r
+ while (change && next != tile)\r
+ {\r
+ CA_MarkGrChunk(STARTTILE16+next);\r
+ change = (signed char)(tinf[ANIM+next]);\r
+ next += change;\r
+ if (++anims > 20)\r
+ {\r
+ strcpy (str,"RF_MarkTileGraphics: Unending background animation:");\r
+ itoa (next,str2,10);\r
+ strcat (str,str2);\r
+ Quit (str);\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+nextback:\r
+ info++;\r
+ } while (start<end);\r
+\r
+//\r
+// foreground plane\r
+//\r
+ start = mapsegs[1];\r
+ info = mapsegs[2];\r
+ end = start+size;\r
+ do\r
+ {\r
+ tile = *start++;\r
+ if (tile>=0) // <0 is a tile that is never drawn\r
+ {\r
+ CA_MarkGrChunk(STARTTILE16M+tile);\r
+ if (tinf[MANIM+tile])\r
+ {\r
+ // this tile will animated\r
+\r
+ if (tinf[MSPEED+tile])\r
+ {\r
+ if (!tinf[MANIM+tile])\r
+ {\r
+ strcpy (str,"RF_MarkTileGraphics: Foreground anim of 0:");\r
+ itoa (tile,str2,10);\r
+ strcat (str,str2);\r
+ Quit (str);\r
+ }\r
+ tilehigh = tile | 0x8000; // foreground tiles have high bit\r
+ for (i=0;i<numanimchains;i++)\r
+ if (allanims[i].current == tilehigh)\r
+ {\r
+ *info = (unsigned)&allanims[i];\r
+ goto nextfront;\r
+ }\r
+\r
+ // new chain of animating tiles\r
+\r
+ if (i>=MAXANIMTYPES)\r
+ Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
+ allanims[i].current = tilehigh;\r
+ allanims[i].count = tinf[MSPEED+tile];\r
+\r
+ *info = (unsigned)&allanims[i];\r
+ numanimchains++;\r
+ }\r
+\r
+ anims = 0;\r
+ change = (signed char)(tinf[MANIM+tile]);\r
+ next = tile+change;\r
+ while (change && next != tile)\r
+ {\r
+ CA_MarkGrChunk(STARTTILE16M+next);\r
+ change = (signed char)(tinf[MANIM+next]);\r
+ next += change;\r
+ if (++anims > 20)\r
+ {\r
+ strcpy (str,"RF_MarkTileGraphics: Unending foreground animation:");\r
+ itoa (next,str2,10);\r
+ strcat (str,str2);\r
+ Quit (str);\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+nextfront:\r
+ info++;\r
+ } while (start<end);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=========================\r
+=\r
+= RFL_InitAnimList\r
+=\r
+= Call to clear out the entire animating tile list and return all of them to\r
+= the free list.\r
+=\r
+=========================\r
+*/\r
+\r
+void RFL_InitAnimList (void)\r
+{\r
+ int i;\r
+\r
+ animfreeptr = &animarray[0];\r
+\r
+ for (i=0;i<MAXANIMTILES-1;i++)\r
+ animarray[i].nexttile = &animarray[i+1];\r
+\r
+ animarray[i].nexttile = NULL;\r
+\r
+ animhead = NULL; // nothing in list\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_CheckForAnimTile\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_CheckForAnimTile (unsigned x, unsigned y)\r
+{\r
+ unsigned tile,offset,speed,lasttime,thistime,timemissed;\r
+ unsigned far *map;\r
+ animtiletype *anim,*next;\r
+\r
+// the info plane of each animating tile has a near pointer into allanims[]\r
+// which gives the current state of all concurrently animating tiles\r
+\r
+ offset = mapbwidthtable[y]/2+x;\r
+\r
+//\r
+// background\r
+//\r
+ map = mapsegs[0]+offset;\r
+ tile = *map;\r
+ if (tinf[ANIM+tile] && tinf[SPEED+tile])\r
+ {\r
+ if (!animfreeptr)\r
+ Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
+ anim = animfreeptr;\r
+ animfreeptr = animfreeptr->nexttile;\r
+ next = animhead; // stick it at the start of the list\r
+ animhead = anim;\r
+ if (next)\r
+ next->prevptr = &anim->nexttile;\r
+ anim->nexttile = next;\r
+ anim->prevptr = &animhead;\r
+\r
+ anim->x = x;\r
+ anim->y = y;\r
+ anim->tile = tile;\r
+ anim->mapplane = map;\r
+ anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
+ }\r
+\r
+//\r
+// foreground\r
+//\r
+ map = mapsegs[1]+offset;\r
+ tile = *map;\r
+ if (tinf[MANIM+tile] && tinf[MSPEED+tile])\r
+ {\r
+ if (!animfreeptr)\r
+ Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
+ anim = animfreeptr;\r
+ animfreeptr = animfreeptr->nexttile;\r
+ next = animhead; // stick it at the start of the list\r
+ animhead = anim;\r
+ if (next)\r
+ next->prevptr = &anim->nexttile;\r
+ anim->nexttile = next;\r
+ anim->prevptr = &animhead;\r
+\r
+ anim->x = x;\r
+ anim->y = y;\r
+ anim->tile = tile;\r
+ anim->mapplane = map;\r
+ anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
+ }\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsOnX\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsOnX (unsigned x)\r
+{\r
+ animtiletype *current,*next;\r
+\r
+ current = animhead;\r
+ while (current)\r
+ {\r
+ if (current->x == x)\r
+ {\r
+ *(void **)current->prevptr = current->nexttile;\r
+ if (current->nexttile)\r
+ current->nexttile->prevptr = current->prevptr;\r
+ next = current->nexttile;\r
+ current->nexttile = animfreeptr;\r
+ animfreeptr = current;\r
+ current = next;\r
+ }\r
+ else\r
+ current = current->nexttile;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsOnY\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsOnY (unsigned y)\r
+{\r
+ animtiletype *current,*next;\r
+\r
+ current = animhead;\r
+ while (current)\r
+ {\r
+ if (current->y == y)\r
+ {\r
+ *(void **)current->prevptr = current->nexttile;\r
+ if (current->nexttile)\r
+ current->nexttile->prevptr = current->prevptr;\r
+ next = current->nexttile;\r
+ current->nexttile = animfreeptr;\r
+ animfreeptr = current;\r
+ current = next;\r
+ }\r
+ else\r
+ current = current->nexttile;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsInBlock\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)\r
+{\r
+ animtiletype *current,*next;\r
+\r
+ current = animhead;\r
+ while (current)\r
+ {\r
+ if (current->x - x < width && current->y - y < height)\r
+ {\r
+ *(void **)current->prevptr = current->nexttile;\r
+ if (current->nexttile)\r
+ current->nexttile->prevptr = current->prevptr;\r
+ next = current->nexttile;\r
+ current->nexttile = animfreeptr;\r
+ animfreeptr = current;\r
+ current = next;\r
+ }\r
+ else\r
+ current = current->nexttile;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_AnimateTiles\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_AnimateTiles (void)\r
+{\r
+ animtiletype *current;\r
+ unsigned updateofs,tile,x,y;\r
+ tiletype *anim;\r
+\r
+//\r
+// animate the lists of tiles\r
+//\r
+ anim = &allanims[0];\r
+ while (anim->current)\r
+ {\r
+ anim->count-=tics;\r
+ while ( anim->count < 1)\r
+ {\r
+ if (anim->current & 0x8000)\r
+ {\r
+ tile = anim->current & 0x7fff;\r
+ tile += (signed char)tinf[MANIM+tile];\r
+ anim->count += tinf[MSPEED+tile];\r
+ tile |= 0x8000;\r
+ }\r
+ else\r
+ {\r
+ tile = anim->current;\r
+ tile += (signed char)tinf[ANIM+tile];\r
+ anim->count += tinf[SPEED+tile];\r
+ }\r
+ anim->current = tile;\r
+ }\r
+ anim++;\r
+ }\r
+\r
+\r
+//\r
+// traverse the list of animating tiles\r
+//\r
+ current = animhead;\r
+ while (current)\r
+ {\r
+ tile =current->chain->current;\r
+ if ( tile != current->tile)\r
+ {\r
+ // tile has animated\r
+ //\r
+ // remove tile from master screen cache,\r
+ // change a tile to its next state, set the structure up for\r
+ // next animation, and post an update region to both update pages\r
+ //\r
+ current->tile = tile;\r
+\r
+ *(current->mapplane) = tile & 0x7fff; // change in map\r
+\r
+ x = current->x-originxtile;\r
+ y = current->y-originytile;\r
+\r
+ if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)\r
+ Quit ("RFL_AnimateTiles: Out of bounds!");\r
+\r
+ updateofs = uwidthtable[y] + x;\r
+ RFL_NewTile(updateofs); // puts "1"s in both pages\r
+ }\r
+ current = current->nexttile;\r
+ }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= RFL_InitSpriteList\r
+=\r
+= Call to clear out the entire sprite list and return all of them to\r
+= the free list.\r
+=\r
+=========================\r
+*/\r
+\r
+void RFL_InitSpriteList (void)\r
+{\r
+ int i;\r
+\r
+ spritefreeptr = &spritearray[0];\r
+ for (i=0;i<MAXSPRITES-1;i++)\r
+ spritearray[i].nextsprite = &spritearray[i+1];\r
+\r
+ spritearray[i].nextsprite = NULL;\r
+\r
+// NULL in all priority levels\r
+\r
+ memset (prioritystart,0,sizeof(prioritystart));\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_CalcOriginStuff\r
+=\r
+= Calculate all the global variables for a new position\r
+= Long parms so position can be clipped to a maximum near 64k\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_CalcOriginStuff (long x, long y)\r
+{\r
+ originxglobal = x;\r
+ originyglobal = y;\r
+ originxtile = originxglobal>>G_T_SHIFT;\r
+ originytile = originyglobal>>G_T_SHIFT;\r
+ originxscreen = originxtile<<SX_T_SHIFT;\r
+ originyscreen = originytile<<SY_T_SHIFT;\r
+ originmap = mapbwidthtable[originytile] + originxtile*2;\r
+\r
+#if GRMODE == EGAGR\r
+ panx = (originxglobal>>G_P_SHIFT) & 15;\r
+ pansx = panx & 8;\r
+ pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
+ panadjust = panx/8 + ylookup[pany];\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+ panx = (originxglobal>>G_P_SHIFT) & 15;\r
+ pansx = panx & 12;\r
+ pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
+ panadjust = pansx/4 + ylookup[pansy];\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_ClearScrollBlocks\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_ClearScrollBlocks (void)\r
+{\r
+ hscrollblocks = vscrollblocks = 0;\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RF_SetScrollBlock\r
+=\r
+= Sets a horizontal or vertical scroll block\r
+= a horizontal block is ----, meaning it blocks up/down movement\r
+=\r
+=================\r
+*/\r
+\r
+void RF_SetScrollBlock (int x, int y, boolean horizontal)\r
+{\r
+ if (horizontal)\r
+ {\r
+ hscrolledge[hscrollblocks] = y;\r
+ if (hscrollblocks++ == MAXSCROLLEDGES)\r
+ Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks");\r
+ }\r
+ else\r
+ {\r
+ vscrolledge[vscrollblocks] = x;\r
+ if (vscrollblocks++ == MAXSCROLLEDGES)\r
+ Quit ("RF_SetScrollBlock: Too many vertical scroll blocks");\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_BoundScroll\r
+=\r
+= Bound a given x/y movement to scroll blocks\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_BoundScroll (int x, int y)\r
+{\r
+ int check,newxtile,newytile;\r
+\r
+ originxglobal += x;\r
+ originyglobal += y;\r
+\r
+ newxtile= originxglobal >> G_T_SHIFT;\r
+ newytile = originyglobal >> G_T_SHIFT;\r
+\r
+ if (x>0)\r
+ {\r
+ newxtile+=SCREENTILESWIDE;\r
+ for (check=0;check<vscrollblocks;check++)\r
+ if (vscrolledge[check] == newxtile)\r
+ {\r
+ originxglobal = originxglobal&0xff00;\r
+ break;\r
+ }\r
+ }\r
+ else if (x<0)\r
+ {\r
+ for (check=0;check<vscrollblocks;check++)\r
+ if (vscrolledge[check] == newxtile)\r
+ {\r
+ originxglobal = (originxglobal&0xff00)+0x100;\r
+ break;\r
+ }\r
+ }\r
+\r
+\r
+ if (y>0)\r
+ {\r
+ newytile+=SCREENTILESHIGH;\r
+ for (check=0;check<hscrollblocks;check++)\r
+ if (hscrolledge[check] == newytile)\r
+ {\r
+ originyglobal = originyglobal&0xff00;\r
+ break;\r
+ }\r
+ }\r
+ else if (y<0)\r
+ {\r
+ for (check=0;check<hscrollblocks;check++)\r
+ if (hscrolledge[check] == newytile)\r
+ {\r
+ originyglobal = (originyglobal&0xff00)+0x100;\r
+ break;\r
+ }\r
+ }\r
+\r
+\r
+ RFL_CalcOriginStuff (originxglobal, originyglobal);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_SetRefreshHook\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_SetRefreshHook (void (*func) (void) )\r
+{\r
+ refreshvector = func;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_NewRow\r
+=\r
+= Bring a new row of tiles onto the port, spawning animating tiles\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_NewRow (int dir)\r
+{\r
+ unsigned count,updatespot,updatestep;\r
+ int x,y,xstep,ystep;\r
+\r
+ switch (dir)\r
+ {\r
+ case 0: // top row\r
+ updatespot = 0;\r
+ updatestep = 1;\r
+ x = originxtile;\r
+ y = originytile;\r
+ xstep = 1;\r
+ ystep = 0;\r
+ count = PORTTILESWIDE;\r
+ break;\r
+\r
+ case 1: // right row\r
+ updatespot = PORTTILESWIDE-1;\r
+ updatestep = UPDATEWIDE;\r
+ x = originxtile + PORTTILESWIDE-1;\r
+ y = originytile;\r
+ xstep = 0;\r
+ ystep = 1;\r
+ count = PORTTILESHIGH;\r
+ break;\r
+\r
+ case 2: // bottom row\r
+ updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
+ updatestep = 1;\r
+ x = originxtile;\r
+ y = originytile + PORTTILESHIGH-1;\r
+ xstep = 1;\r
+ ystep = 0;\r
+ count = PORTTILESWIDE;\r
+ break;\r
+\r
+ case 3: // left row\r
+ updatespot = 0;\r
+ updatestep = UPDATEWIDE;\r
+ x = originxtile;\r
+ y = originytile;\r
+ xstep = 0;\r
+ ystep = 1;\r
+ count = PORTTILESHIGH;\r
+ break;\r
+ default:\r
+ Quit ("RFL_NewRow: Bad dir!");\r
+ }\r
+\r
+ while (count--)\r
+ {\r
+ RFL_NewTile(updatespot);\r
+ RFL_CheckForAnimTile (x,y);\r
+ updatespot+=updatestep;\r
+ x+=xstep;\r
+ y+=ystep;\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_ForceRefresh\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_ForceRefresh (void)\r
+{\r
+ RF_NewPosition (originxglobal,originyglobal);\r
+ RF_Refresh ();\r
+ RF_Refresh ();\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_MapToMap\r
+=\r
+= Copies a block of tiles (all three planes) from one point\r
+= in the map to another, accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_MapToMap (unsigned srcx, unsigned srcy,\r
+ unsigned destx, unsigned desty,\r
+ unsigned width, unsigned height)\r
+{\r
+ int x,y;\r
+ unsigned source,destofs,xspot,yspot;\r
+ unsigned linedelta,p0,p1,p2,updatespot;\r
+ unsigned far *source0, far *source1, far *source2;\r
+ unsigned far *dest0, far *dest1, far *dest2;\r
+ boolean changed;\r
+\r
+ RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
+\r
+ source = mapbwidthtable[srcy]/2 + srcx;\r
+\r
+ source0 = mapsegs[0]+source;\r
+ source1 = mapsegs[1]+source;\r
+ source2 = mapsegs[2]+source;\r
+\r
+ destofs = mapbwidthtable[desty]/2 + destx;\r
+ destofs -= source;\r
+\r
+ linedelta = mapwidth - width;\r
+\r
+ for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)\r
+ for (x=0;x<width;x++,source0++,source1++,source2++)\r
+ {\r
+ p0 = *source0;\r
+ p1 = *source1;\r
+ p2 = *source2;\r
+\r
+ dest0 = source0 + destofs;\r
+ dest1 = source1 + destofs;\r
+ dest2 = source2 + destofs;\r
+\r
+//\r
+// only make a new tile if it is different\r
+//\r
+ if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)\r
+ {\r
+ *dest0 = p0;\r
+ *dest1 = p1;\r
+ *dest2 = p2;\r
+ changed = true;\r
+ }\r
+ else\r
+ changed = false;\r
+\r
+//\r
+// if tile is on the view port\r
+//\r
+ xspot = destx+x-originxtile;\r
+ yspot = desty+y-originytile;\r
+ if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
+ {\r
+ if (changed)\r
+ {\r
+ updatespot = uwidthtable[yspot]+xspot;\r
+ RFL_NewTile(updatespot);\r
+ }\r
+ RFL_CheckForAnimTile (destx+x,desty+y);\r
+ }\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_MemToMap\r
+=\r
+= Copies a string of tiles from main memory to the map,\r
+= accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_MemToMap (unsigned far *source, unsigned plane,\r
+ unsigned destx, unsigned desty,\r
+ unsigned width, unsigned height)\r
+{\r
+ int x,y;\r
+ unsigned xspot,yspot;\r
+ unsigned linedelta,updatespot;\r
+ unsigned far *dest,old,new;\r
+ boolean changed;\r
+\r
+ RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
+\r
+ dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;\r
+\r
+ linedelta = mapwidth - width;\r
+\r
+ for (y=0;y<height;y++,dest+=linedelta)\r
+ for (x=0;x<width;x++)\r
+ {\r
+ old = *dest;\r
+ new = *source++;\r
+ if (old != new)\r
+ {\r
+ *dest = new;\r
+ changed = true;\r
+ }\r
+ else\r
+ changed = false;\r
+\r
+ dest++;\r
+ xspot = destx+x-originxtile;\r
+ yspot = desty+y-originytile;\r
+ if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
+ {\r
+ if (changed)\r
+ {\r
+ updatespot = uwidthtable[yspot]+xspot;\r
+ RFL_NewTile(updatespot);\r
+ }\r
+ RFL_CheckForAnimTile (destx+x,desty+y);\r
+ }\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RFL_BoundNewOrigin\r
+=\r
+= Copies a string of tiles from main memory to the map,\r
+= accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)\r
+{\r
+ int check,edge;\r
+\r
+//\r
+// calculate new origin related globals\r
+//\r
+ if (orgx<originxmin)\r
+ orgx=originxmin;\r
+ else if (orgx>originxmax)\r
+ orgx=originxmax;\r
+\r
+ if (orgy<originymin)\r
+ orgy=originymin;\r
+ else if (orgy>originymax)\r
+ orgy=originymax;\r
+\r
+ originxtile = orgx>>G_T_SHIFT;\r
+ originytile = orgy>>G_T_SHIFT;\r
+\r
+ for (check=0;check<vscrollblocks;check++)\r
+ {\r
+ edge = vscrolledge[check];\r
+ if (edge>=originxtile && edge <=originxtile+10)\r
+ {\r
+ orgx = (edge+1)*TILEGLOBAL;\r
+ break;\r
+ }\r
+ if (edge>=originxtile+11 && edge <=originxtile+20)\r
+ {\r
+ orgx = (edge-20)*TILEGLOBAL;\r
+ break;\r
+ }\r
+ }\r
+\r
+ for (check=0;check<hscrollblocks;check++)\r
+ {\r
+ edge = hscrolledge[check];\r
+ if (edge>=originytile && edge <=originytile+6)\r
+ {\r
+ orgy = (edge+1)*TILEGLOBAL;\r
+ break;\r
+ }\r
+ if (edge>=originytile+7 && edge <=originytile+13)\r
+ {\r
+ orgy = (edge-13)*TILEGLOBAL;\r
+ break;\r
+ }\r
+ }\r
+\r
+\r
+ RFL_CalcOriginStuff (orgx,orgy);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_ClearBlock\r
+=\r
+= Posts erase blocks to clear a certain area of the screen to the master\r
+= screen, to erase text or something draw directly to the screen\r
+=\r
+= Parameters in pixels, but erasure is byte bounded\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_ClearBlock (int x, int y, int width, int height)\r
+{\r
+ eraseblocktype block;\r
+\r
+#if GRMODE == EGAGR\r
+ block.screenx = x/8+originxscreen;\r
+ block.screeny = y+originyscreen;\r
+ block.width = (width+(x&7)+7)/8;\r
+ block.height = height;\r
+ memcpy (eraselistptr[0]++,&block,sizeof(block));\r
+ memcpy (eraselistptr[1]++,&block,sizeof(block));\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+ block.screenx = x/4+originxscreen;\r
+ block.screeny = y+originyscreen;\r
+ block.width = (width+(x&3)+3)/4;\r
+ block.height = height;\r
+ memcpy (eraselistptr[0]++,&block,sizeof(block));\r
+#endif\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RedrawBlock\r
+=\r
+= Causes a number of tiles to be redrawn to the master screen and updated\r
+=\r
+= Parameters in pixels, but erasure is tile bounded\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RedrawBlock (int x, int y, int width, int height)\r
+{\r
+ int xx,yy,xl,xh,yl,yh;\r
+\r
+ xl=(x+panx)/16;\r
+ xh=(x+panx+width+15)/16;\r
+ yl=(y+pany)/16;\r
+ yh=(y+pany+height+15)/16;\r
+ for (yy=yl;yy<=yh;yy++)\r
+ for (xx=xl;xx<=xh;xx++)\r
+ RFL_NewTile (yy*UPDATEWIDE+xx);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_CalcTics\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_CalcTics (void)\r
+{\r
+ long newtime,oldtimecount;\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+ if (lasttimecount > TimeCount)\r
+ TimeCount = lasttimecount; // if the game was paused a LONG time\r
+\r
+ if (DemoMode) // demo recording and playback needs\r
+ { // to be constant\r
+//\r
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken\r
+//\r
+ oldtimecount = lasttimecount;\r
+ while (TimeCount<oldtimecount+DEMOTICS*2)\r
+ ;\r
+ lasttimecount = oldtimecount + DEMOTICS;\r
+ TimeCount = lasttimecount + DEMOTICS;\r
+ tics = DEMOTICS;\r
+ }\r
+ else\r
+ {\r
+//\r
+// non demo, so report actual time\r
+//\r
+ do\r
+ {\r
+ newtime = TimeCount;\r
+ tics = newtime-lasttimecount;\r
+ } while (tics<MINTICS);\r
+ lasttimecount = newtime;\r
+\r
+#ifdef PROFILE\r
+ strcpy (scratch,"\tTics:");\r
+ itoa (tics,str,10);\r
+ strcat (scratch,str);\r
+ strcat (scratch,"\n");\r
+ write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+ if (tics>MAXTICS)\r
+ {\r
+ TimeCount -= (tics-MAXTICS);\r
+ tics = MAXTICS;\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ EGA specific routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == EGAGR\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_FindFreeBuffer\r
+=\r
+= Finds the start of unused, non visable buffer space\r
+=\r
+=====================\r
+*/\r
+\r
+unsigned RF_FindFreeBuffer (void)\r
+{\r
+ unsigned spot,i,j;\r
+ boolean ok;\r
+\r
+ for (i=0;i<3;i++)\r
+ {\r
+ spot = screenstart[i]+SCREENSPACE;\r
+ ok = true;\r
+ for (j=0;j<3;j++)\r
+ if (spot == screenstart[j])\r
+ {\r
+ ok = false;\r
+ break;\r
+ }\r
+ if (ok)\r
+ return spot;\r
+ }\r
+\r
+ return 0; // never get here...\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewPosition EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewPosition (unsigned x, unsigned y)\r
+{\r
+ int mx,my;\r
+ byte *page0ptr,*page1ptr;\r
+ unsigned updatenum;\r
+\r
+ RFL_BoundNewOrigin (x,y);\r
+//\r
+// clear out all animating tiles\r
+//\r
+ RFL_InitAnimList ();\r
+\r
+//\r
+// set up the new update arrays at base position\r
+//\r
+ updatestart[0] = baseupdatestart[0];\r
+ updatestart[1] = baseupdatestart[1];\r
+ updateptr = updatestart[otherpage];\r
+\r
+ page0ptr = updatestart[0]+PORTTILESWIDE; // used to stick "0"s after rows\r
+ page1ptr = updatestart[1]+PORTTILESWIDE;\r
+\r
+ updatenum = 0; // start at first visable tile\r
+\r
+ for (my=0;my<PORTTILESHIGH;my++)\r
+ {\r
+ for (mx=0;mx<PORTTILESWIDE;mx++)\r
+ {\r
+ RFL_NewTile(updatenum); // puts "1"s in both pages\r
+ RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
+ updatenum++;\r
+ }\r
+ updatenum++;\r
+ *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles\r
+ page0ptr+=(PORTTILESWIDE+1);\r
+ page1ptr+=(PORTTILESWIDE+1);\r
+ }\r
+ *(word *)(page0ptr-PORTTILESWIDE)\r
+ = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Scroll EGA\r
+=\r
+= Move the origin x/y global coordinates, readjust the screen panning, and\r
+= scroll if needed. If the scroll distance is greater than one tile, the\r
+= entire screen will be redrawn (this could be generalized, but scrolling\r
+= more than one tile per refresh is a bad idea!).\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Scroll (int x, int y)\r
+{\r
+ long neworgx,neworgy;\r
+ int i,deltax,deltay,absdx,absdy;\r
+ int oldxt,oldyt,move,yy;\r
+ unsigned updatespot;\r
+ byte *update0,*update1;\r
+ unsigned oldpanx,oldpanadjust,oldscreen,newscreen,screencopy;\r
+ int screenmove;\r
+\r
+ oldxt = originxtile;\r
+ oldyt = originytile;\r
+ oldpanadjust = panadjust;\r
+ oldpanx = panx;\r
+\r
+ RFL_BoundScroll (x,y);\r
+\r
+ deltax = originxtile - oldxt;\r
+ absdx = abs(deltax);\r
+ deltay = originytile - oldyt;\r
+ absdy = abs(deltay);\r
+\r
+ if (absdx>1 || absdy>1)\r
+ {\r
+ //\r
+ // scrolled more than one tile, so start from scratch\r
+ //\r
+ RF_NewPosition(originxglobal,originyglobal);\r
+ return;\r
+ }\r
+\r
+ if (!absdx && !absdy)\r
+ return; // the screen has not scrolled an entire tile\r
+\r
+\r
+//\r
+// adjust screens and handle SVGA crippled compatability mode\r
+//\r
+ screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
+ for (i=0;i<3;i++)\r
+ {\r
+ screenstart[i]+= screenmove;\r
+ if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )\r
+ {\r
+ //\r
+ // move the screen to the opposite end of the buffer\r
+ //\r
+ screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;\r
+ oldscreen = screenstart[i] - screenmove;\r
+ newscreen = oldscreen + screencopy;\r
+ screenstart[i] = newscreen + screenmove;\r
+ VW_ScreenToScreen (oldscreen,newscreen,\r
+ PORTTILESWIDE*2,PORTTILESHIGH*16);\r
+\r
+ if (i==screenpage)\r
+ VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);\r
+ }\r
+ }\r
+ bufferofs = screenstart[otherpage];\r
+ displayofs = screenstart[screenpage];\r
+ masterofs = screenstart[2];\r
+\r
+\r
+//\r
+// float the update regions\r
+//\r
+ move = deltax;\r
+ if (deltay==1)\r
+ move += UPDATEWIDE;\r
+ else if (deltay==-1)\r
+ move -= UPDATEWIDE;\r
+\r
+ updatestart[0]+=move;\r
+ updatestart[1]+=move;\r
+\r
+//\r
+// draw the new tiles just scrolled on to the master screen, and\r
+// mark them as needing to be copied to each screen next refreshes\r
+// Make sure a zero is at the end of each row in update\r
+//\r
+\r
+ if (deltax)\r
+ {\r
+ if (deltax==1)\r
+ {\r
+ RFL_NewRow (1); // new right row\r
+ RFL_RemoveAnimsOnX (originxtile-1);\r
+ }\r
+ else\r
+ {\r
+ RFL_NewRow (3); // new left row\r
+ RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
+ }\r
+\r
+ update0 = updatestart[0]+PORTTILESWIDE;\r
+ update1 = updatestart[1]+PORTTILESWIDE;\r
+ for (yy=0;yy<PORTTILESHIGH;yy++)\r
+ {\r
+ *update0 = *update1 = 0; // drop a 0 at end of each row\r
+ update0+=UPDATEWIDE;\r
+ update1+=UPDATEWIDE;\r
+ }\r
+ }\r
+\r
+//----------------\r
+\r
+ if (deltay)\r
+ {\r
+ if (deltay==1)\r
+ {\r
+ updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
+ RFL_NewRow (2); // new bottom row\r
+ RFL_RemoveAnimsOnY (originytile-1);\r
+ }\r
+ else\r
+ {\r
+ updatespot = 0;\r
+ RFL_NewRow (0); // new top row\r
+ RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
+ }\r
+\r
+ *(updatestart[0]+updatespot+PORTTILESWIDE) =\r
+ *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;\r
+ }\r
+\r
+//----------------\r
+\r
+ //\r
+ // place a new terminator\r
+ //\r
+ update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;\r
+ update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;\r
+ *update0++ = *update1++ = 0;\r
+ *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_PlaceSprite EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+ unsigned spritenumber, drawtype draw, int priority)\r
+{\r
+ spritelisttype register *sprite,*next;\r
+ spritetabletype far *spr;\r
+ spritetype _seg *block;\r
+ unsigned shift,pixx;\r
+ char str[80],str2[10];\r
+\r
+ if (!spritenumber || spritenumber == (unsigned)-1)\r
+ {\r
+ RF_RemoveSprite (user);\r
+ return;\r
+ }\r
+\r
+ sprite = (spritelisttype *)*user;\r
+\r
+ if (sprite)\r
+ {\r
+ // sprite allready exists in the list, so we can use it's block\r
+\r
+ //\r
+ // post an erase block to both pages by copying screenx,screeny,width,height\r
+ // both pages may not need to be erased if the sprite just changed last frame\r
+ //\r
+ if (sprite->updatecount<2)\r
+ {\r
+ if (!sprite->updatecount)\r
+ memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
+ memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
+ }\r
+\r
+ if (priority != sprite->priority)\r
+ {\r
+ // sprite mvoed to another priority, so unlink the old one and\r
+ // relink it in the new priority\r
+\r
+ next = sprite->nextsprite; // cut old links\r
+ if (next)\r
+ next->prevptr = sprite->prevptr;\r
+ *sprite->prevptr = next;\r
+ goto linknewspot;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // this is a brand new sprite, so allocate a block from the array\r
+\r
+ if (!spritefreeptr)\r
+ Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
+\r
+ sprite = spritefreeptr;\r
+ spritefreeptr = spritefreeptr->nextsprite;\r
+\r
+linknewspot:\r
+ next = prioritystart[priority]; // stick it in new spot\r
+ if (next)\r
+ next->prevptr = &sprite->nextsprite;\r
+ sprite->nextsprite = next;\r
+ prioritystart[priority] = sprite;\r
+ sprite->prevptr = &prioritystart[priority];\r
+ }\r
+\r
+//\r
+// write the new info to the sprite\r
+//\r
+ spr = &spritetable[spritenumber-STARTSPRITES];\r
+ block = (spritetype _seg *)grsegs[spritenumber];\r
+\r
+ if (!block)\r
+ {\r
+ strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");\r
+ itoa (spritenumber,str2,10);\r
+ strcat (str,str2);\r
+ Quit (str);\r
+ }\r
+\r
+ globaly+=spr->orgy;\r
+ globalx+=spr->orgx;\r
+\r
+ pixx = globalx >> G_SY_SHIFT;\r
+ shift = (pixx&7)/2;\r
+\r
+ sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);\r
+ sprite->screeny = globaly >> G_SY_SHIFT;\r
+ sprite->width = block->width[shift];\r
+ sprite->height = spr->height;\r
+ sprite->grseg = spritenumber;\r
+ sprite->sourceofs = block->sourceoffset[shift];\r
+ sprite->planesize = block->planesize[shift];\r
+ sprite->draw = draw;\r
+ sprite->priority = priority;\r
+ sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
+ sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
+ sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
+ - sprite->tilex + 1;\r
+ sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
+ - sprite->tiley + 1;\r
+\r
+ sprite->updatecount = 2; // draw on next two refreshes\r
+\r
+// save the sprite pointer off in the user's pointer so it can be moved\r
+// again later\r
+\r
+ *user = sprite;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RemoveSprite EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RemoveSprite (void **user)\r
+{\r
+ spritelisttype *sprite,*next;\r
+\r
+ sprite = (spritelisttype *)*user;\r
+ if (!sprite)\r
+ return;\r
+\r
+//\r
+// post an erase block to both pages by copying screenx,screeny,width,height\r
+// both pages may not need to be erased if the sprite just changed last frame\r
+//\r
+ if (sprite->updatecount<2)\r
+ {\r
+ if (!sprite->updatecount)\r
+ memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
+ memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
+ }\r
+\r
+//\r
+// unlink the sprite node\r
+//\r
+ next = sprite->nextsprite;\r
+ if (next) // if (!next), sprite is last in chain\r
+ next->prevptr = sprite->prevptr;\r
+ *sprite->prevptr = next;\r
+\r
+//\r
+// add it back to the free list\r
+//\r
+ sprite->nextsprite = spritefreeptr;\r
+ spritefreeptr = sprite;\r
+\r
+//\r
+// null the users pointer, so next time that actor gets placed, it will\r
+// allocate a new block\r
+//\r
+\r
+ *user = 0;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_EraseBlocks EGA\r
+=\r
+= Write mode 1 should be set\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_EraseBlocks (void)\r
+{\r
+ eraseblocktype *block,*done;\r
+ int screenxh,screenyh;\r
+ unsigned pos,xtl,ytl,xth,yth,x,y;\r
+ byte *updatespot;\r
+ unsigned updatedelta;\r
+ unsigned erasecount;\r
+\r
+#ifdef PROFILE\r
+ erasecount = 0;\r
+#endif\r
+\r
+ block = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
+\r
+ done = eraselistptr[otherpage];\r
+\r
+ while (block != done)\r
+ {\r
+\r
+ //\r
+ // clip the block to the current screen view\r
+ //\r
+ block->screenx -= originxscreen;\r
+ block->screeny -= originyscreen;\r
+\r
+ if (block->screenx < 0)\r
+ {\r
+ block->width += block->screenx;\r
+ if (block->width<1)\r
+ goto next;\r
+ block->screenx = 0;\r
+ }\r
+\r
+ if (block->screeny < 0)\r
+ {\r
+ block->height += block->screeny;\r
+ if (block->height<1)\r
+ goto next;\r
+ block->screeny = 0;\r
+ }\r
+\r
+ screenxh = block->screenx + block->width;\r
+ screenyh = block->screeny + block->height;\r
+\r
+ if (screenxh > EGAPORTSCREENWIDE)\r
+ {\r
+ block->width = EGAPORTSCREENWIDE-block->screenx;\r
+ screenxh = block->screenx + block->width;\r
+ }\r
+\r
+ if (screenyh > PORTSCREENHIGH)\r
+ {\r
+ block->height = PORTSCREENHIGH-block->screeny;\r
+ screenyh = block->screeny + block->height;\r
+ }\r
+\r
+ if (block->width<1 || block->height<1)\r
+ goto next;\r
+\r
+ //\r
+ // erase the block by copying from the master screen\r
+ //\r
+ pos = ylookup[block->screeny]+block->screenx;\r
+ VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
+ block->width,block->height);\r
+\r
+ //\r
+ // put 2s in update where the block was, to force sprites to update\r
+ //\r
+ xtl = block->screenx >> SX_T_SHIFT;\r
+ xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
+ ytl = block->screeny >> SY_T_SHIFT;\r
+ yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
+\r
+ updatespot = updateptr + uwidthtable[ytl] + xtl;\r
+ updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+ for (y=ytl;y<=yth;y++)\r
+ {\r
+ for (x=xtl;x<=xth;x++)\r
+ *updatespot++ = 2;\r
+ updatespot += updatedelta; // down to next line\r
+ }\r
+#ifdef PROFILE\r
+ erasecount++;\r
+#endif\r
+\r
+next:\r
+ block++;\r
+ }\r
+ eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
+#ifdef PROFILE\r
+ strcpy (scratch,"\tErase:");\r
+ itoa (erasecount,str,10);\r
+ strcat (scratch,str);\r
+ write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_UpdateSprites EGA\r
+=\r
+= NOTE: Implement vertical clipping!\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_UpdateSprites (void)\r
+{\r
+ spritelisttype *sprite;\r
+ int portx,porty,x,y,xtl,xth,ytl,yth;\r
+ int priority;\r
+ unsigned dest;\r
+ byte *updatespot,*baseupdatespot;\r
+ unsigned updatedelta;\r
+ unsigned updatecount;\r
+ unsigned height,sourceofs;\r
+\r
+#ifdef PROFILE\r
+ updatecount = 0;\r
+#endif\r
+\r
+ for (priority=0;priority<PRIORITIES;priority++)\r
+ {\r
+ if (priority==MASKEDTILEPRIORITY)\r
+ RFL_MaskForegroundTiles ();\r
+\r
+ for (sprite = prioritystart[priority]; sprite ;\r
+ sprite = (spritelisttype *)sprite->nextsprite)\r
+ {\r
+ //\r
+ // see if the sprite has any visable area in the port\r
+ //\r
+\r
+ portx = sprite->screenx - originxscreen;\r
+ porty = sprite->screeny - originyscreen;\r
+ xtl = portx >> SX_T_SHIFT;\r
+ xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
+ ytl = porty >> SY_T_SHIFT;\r
+ yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
+\r
+ if (xtl<0)\r
+ xtl = 0;\r
+ if (xth>=PORTTILESWIDE)\r
+ xth = PORTTILESWIDE-1;\r
+ if (ytl<0)\r
+ ytl = 0;\r
+ if (yth>=PORTTILESHIGH)\r
+ yth = PORTTILESHIGH-1;\r
+\r
+ if (xtl>xth || ytl>yth)\r
+ continue;\r
+\r
+ //\r
+ // see if it's visable area covers any non 0 update tiles\r
+ //\r
+ updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
+ updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+ if (sprite->updatecount)\r
+ {\r
+ sprite->updatecount--; // the sprite was just placed,\r
+ goto redraw; // so draw it for sure\r
+ }\r
+\r
+ for (y=ytl;y<=yth;y++)\r
+ {\r
+ for (x=xtl;x<=xth;x++)\r
+ if (*updatespot++)\r
+ goto redraw;\r
+ updatespot += updatedelta; // down to next line\r
+ }\r
+ continue; // no need to update\r
+\r
+redraw:\r
+ //\r
+ // set the tiles it covers to 3, because those tiles are being\r
+ // updated\r
+ //\r
+ updatespot = baseupdatespot;\r
+ for (y=ytl;y<=yth;y++)\r
+ {\r
+ for (x=xtl;x<=xth;x++)\r
+ *updatespot++ = 3;\r
+ updatespot += updatedelta; // down to next line\r
+ }\r
+ //\r
+ // draw it!\r
+ //\r
+ height = sprite->height;\r
+ sourceofs = sprite->sourceofs;\r
+ if (porty<0)\r
+ {\r
+ height += porty; // clip top off\r
+ sourceofs -= porty*sprite->width;\r
+ porty = 0;\r
+ }\r
+ else if (porty+height>PORTSCREENHIGH)\r
+ {\r
+ height = PORTSCREENHIGH - porty; // clip bottom off\r
+ }\r
+\r
+ dest = bufferofs + ylookup[porty] + portx;\r
+\r
+ switch (sprite->draw)\r
+ {\r
+ case spritedraw:\r
+ VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
+ dest,sprite->width,height,sprite->planesize);\r
+ break;\r
+\r
+ case maskdraw:\r
+ break;\r
+\r
+ }\r
+#ifdef PROFILE\r
+ updatecount++;\r
+#endif\r
+\r
+\r
+ }\r
+ }\r
+#ifdef PROFILE\r
+ strcpy (scratch,"\tSprites:");\r
+ itoa (updatecount,str,10);\r
+ strcat (scratch,str);\r
+ write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Refresh EGA\r
+=\r
+= All routines will draw at the port at bufferofs, possibly copying from\r
+= the port at masterofs. The EGA version then page flips, while the\r
+= CGA version updates the screen from the buffer port.\r
+=\r
+= Screenpage is the currently displayed page, not the one being drawn\r
+= Otherpage is the page to be worked with now\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Refresh (void)\r
+{\r
+ byte *newupdate;\r
+\r
+ updateptr = updatestart[otherpage];\r
+\r
+ RFL_AnimateTiles (); // DEBUG\r
+\r
+//\r
+// update newly scrolled on tiles and animated tiles from the master screen\r
+//\r
+ EGAWRITEMODE(1);\r
+ EGAMAPMASK(15);\r
+ RFL_UpdateTiles ();\r
+ RFL_EraseBlocks ();\r
+\r
+//\r
+// Update is all 0 except where sprites have changed or new area has\r
+// been scrolled on. Go through all sprites and update the ones that cover\r
+// a non 0 update tile\r
+//\r
+ EGAWRITEMODE(0);\r
+ RFL_UpdateSprites ();\r
+\r
+//\r
+// if the main program has a refresh hook set, call their function before\r
+// displaying the new page\r
+//\r
+ if (refreshvector)\r
+ refreshvector();\r
+\r
+//\r
+// display the changed screen\r
+//\r
+ VW_SetScreen(bufferofs+panadjust,panx & xpanmask);\r
+\r
+//\r
+// prepare for next refresh\r
+//\r
+// Set the update array to the middle position and clear it out to all "0"s\r
+// with an UPDATETERMINATE at the end\r
+//\r
+ updatestart[otherpage] = newupdate = baseupdatestart[otherpage];\r
+asm mov ax,ds\r
+asm mov es,ax\r
+asm xor ax,ax\r
+asm mov cx,(UPDATESCREENSIZE-2)/2\r
+asm mov di,[newupdate]\r
+asm rep stosw\r
+asm mov [WORD PTR es:di],UPDATETERMINATE\r
+\r
+ screenpage ^= 1;\r
+ otherpage ^= 1;\r
+ bufferofs = screenstart[otherpage];\r
+ displayofs = screenstart[screenpage];\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+ RF_CalcTics ();\r
+}\r
+\r
+#endif // GRMODE == EGAGR\r
+\r
+/*\r
+=============================================================================\r
+\r
+ CGA specific routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewPosition CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewPosition (unsigned x, unsigned y)\r
+{\r
+ int mx,my;\r
+ byte *spotptr;\r
+ unsigned updatenum;\r
+\r
+ RFL_BoundNewOrigin (x,y);\r
+\r
+//\r
+// clear out all animating tiles\r
+//\r
+ RFL_InitAnimList ();\r
+\r
+//\r
+// set up the new update arrays at base position\r
+//\r
+ updateptr = baseupdateptr;\r
+\r
+ spotptr = updateptr + PORTTILESWIDE; // used to stick "0"s after rows\r
+\r
+ updatenum = 0; // start at first visable tile\r
+\r
+ for (my=0;my<PORTTILESHIGH;my++)\r
+ {\r
+ for (mx=0;mx<PORTTILESWIDE;mx++)\r
+ {\r
+ RFL_NewTile(updatenum); // puts "1"s in both pages\r
+ RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
+ updatenum++;\r
+ }\r
+ updatenum++;\r
+ *spotptr = 0; // set a 0 at end of a line of tiles\r
+ spotptr +=(PORTTILESWIDE+1);\r
+ }\r
+ *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Scroll CGA\r
+=\r
+= Move the origin x/y global coordinates, readjust the screen panning, and\r
+= scroll if needed. If the scroll distance is greater than one tile, the\r
+= entire screen will be redrawn (this could be generalized, but scrolling\r
+= more than one tile per refresh is a bad idea!).\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Scroll (int x, int y)\r
+{\r
+ long neworgx,neworgy;\r
+ int i,deltax,deltay,absdx,absdy;\r
+ int oldxt,oldyt,move,yy;\r
+ unsigned updatespot;\r
+ byte *spotptr;\r
+ unsigned oldoriginmap,oldscreen,newscreen,screencopy;\r
+ int screenmove;\r
+\r
+ oldxt = originxtile;\r
+ oldyt = originytile;\r
+\r
+ RFL_BoundScroll (x,y);\r
+\r
+ deltax = originxtile - oldxt;\r
+ absdx = abs(deltax);\r
+ deltay = originytile - oldyt;\r
+ absdy = abs(deltay);\r
+\r
+ if (absdx>1 || absdy>1)\r
+ {\r
+ //\r
+ // scrolled more than one tile, so start from scratch\r
+ //\r
+ RF_NewPosition(originxglobal,originyglobal);\r
+ return;\r
+ }\r
+\r
+ if (!absdx && !absdy)\r
+ return; // the screen has not scrolled an entire tile\r
+\r
+\r
+//\r
+// float screens\r
+//\r
+ screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
+ bufferofs += screenmove;\r
+ masterofs += screenmove;\r
+\r
+\r
+//\r
+// float the update regions\r
+//\r
+ move = deltax;\r
+ if (deltay==1)\r
+ move += UPDATEWIDE;\r
+ else if (deltay==-1)\r
+ move -= UPDATEWIDE;\r
+\r
+ updateptr+=move;\r
+\r
+//\r
+// draw the new tiles just scrolled on to the master screen, and\r
+// mark them as needing to be copied to each screen next refreshes\r
+// Make sure a zero is at the end of each row in update\r
+//\r
+\r
+ if (deltax)\r
+ {\r
+ if (deltax==1)\r
+ {\r
+ RFL_NewRow (1); // new right row\r
+ RFL_RemoveAnimsOnX (originxtile-1);\r
+ }\r
+ else\r
+ {\r
+ RFL_NewRow (3); // new left row\r
+ RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
+ }\r
+\r
+ spotptr = updateptr+PORTTILESWIDE;\r
+ for (yy=0;yy<PORTTILESHIGH;yy++)\r
+ {\r
+ *spotptr = 0; // drop a 0 at end of each row\r
+ spotptr+=UPDATEWIDE;\r
+ }\r
+ }\r
+\r
+//----------------\r
+\r
+ if (deltay)\r
+ {\r
+ if (deltay==1)\r
+ {\r
+ RFL_NewRow (2); // new bottom row\r
+ *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;\r
+ RFL_RemoveAnimsOnY (originytile-1);\r
+ }\r
+ else\r
+ {\r
+ RFL_NewRow (0); // new top row\r
+ *(updateptr+PORTTILESWIDE) = 0;\r
+ RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
+ }\r
+ }\r
+\r
+//----------------\r
+\r
+ //\r
+ // place a new terminator\r
+ //\r
+ spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;\r
+ *spotptr++ = 0;\r
+ *(unsigned *)spotptr = UPDATETERMINATE;\r
+}\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_PlaceSprite CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+ unsigned spritenumber, drawtype draw, int priority)\r
+{\r
+ spritelisttype register *sprite,*next;\r
+ spritetabletype far *spr;\r
+ spritetype _seg *block;\r
+ unsigned shift,pixx;\r
+ char str[80],str2[10];\r
+\r
+ if (!spritenumber || spritenumber == (unsigned)-1)\r
+ {\r
+ RF_RemoveSprite (user);\r
+ return;\r
+ }\r
+\r
+ sprite = (spritelisttype *)*user;\r
+\r
+ if (sprite)\r
+ {\r
+ // sprite allready exists in the list, so we can use it's block\r
+\r
+ //\r
+ // post an erase block to erase the old position by copying\r
+ // screenx,screeny,width,height\r
+ //\r
+ if (!sprite->updatecount) // may not have been drawn at all yet\r
+ memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
+\r
+ if (priority != sprite->priority)\r
+ {\r
+ // sprite moved to another priority, so unlink the old one and\r
+ // relink it in the new priority\r
+\r
+ next = sprite->nextsprite; // cut old links\r
+ if (next)\r
+ next->prevptr = sprite->prevptr;\r
+ *sprite->prevptr = next;\r
+ goto linknewspot;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // this is a brand new sprite, so allocate a block from the array\r
+\r
+ if (!spritefreeptr)\r
+ Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
+\r
+ sprite = spritefreeptr;\r
+ spritefreeptr = spritefreeptr->nextsprite;\r
+\r
+linknewspot:\r
+ next = prioritystart[priority]; // stick it in new spot\r
+ if (next)\r
+ next->prevptr = &sprite->nextsprite;\r
+ sprite->nextsprite = next;\r
+ prioritystart[priority] = sprite;\r
+ sprite->prevptr = &prioritystart[priority];\r
+ }\r
+\r
+//\r
+// write the new info to the sprite\r
+//\r
+ spr = &spritetable[spritenumber-STARTSPRITES];\r
+ block = (spritetype _seg *)grsegs[spritenumber];\r
+\r
+ if (!block)\r
+ {\r
+ strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");\r
+ itoa (spritenumber,str2,10);\r
+ strcat (str,str2);\r
+ Quit (str);\r
+ }\r
+\r
+\r
+ globaly+=spr->orgy;\r
+ globalx+=spr->orgx;\r
+\r
+ sprite->screenx = globalx >> G_CGASX_SHIFT;\r
+ sprite->screeny = globaly >> G_SY_SHIFT;\r
+ sprite->width = block->width[0];\r
+ sprite->height = spr->height;\r
+ sprite->grseg = spritenumber;\r
+ sprite->sourceofs = block->sourceoffset[0];\r
+ sprite->planesize = block->planesize[0];\r
+ sprite->draw = draw;\r
+ sprite->priority = priority;\r
+ sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
+ sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
+ sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
+ - sprite->tilex + 1;\r
+ sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
+ - sprite->tiley + 1;\r
+\r
+ sprite->updatecount = 1; // draw on next refresh\r
+\r
+// save the sprite pointer off in the user's pointer so it can be moved\r
+// again later\r
+\r
+ *user = sprite;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RemoveSprite CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RemoveSprite (void **user)\r
+{\r
+ spritelisttype *sprite,*next;\r
+\r
+ sprite = (spritelisttype *)*user;\r
+ if (!sprite)\r
+ return;\r
+\r
+//\r
+// post an erase block to erase the old position by copying\r
+// screenx,screeny,width,height\r
+//\r
+ if (!sprite->updatecount)\r
+ {\r
+ memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
+ }\r
+\r
+//\r
+// unlink the sprite node\r
+//\r
+ next = sprite->nextsprite;\r
+ if (next) // if (!next), sprite is last in chain\r
+ next->prevptr = sprite->prevptr;\r
+ *sprite->prevptr = next;\r
+\r
+//\r
+// add it back to the free list\r
+//\r
+ sprite->nextsprite = spritefreeptr;\r
+ spritefreeptr = sprite;\r
+\r
+//\r
+// null the users pointer, so next time that actor gets placed, it will\r
+// allocate a new block\r
+//\r
+\r
+ *user = 0;\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_EraseBlocks CGA\r
+=\r
+= Write mode 1 should be set\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_EraseBlocks (void)\r
+{\r
+ eraseblocktype *block,*done;\r
+ int screenxh,screenyh;\r
+ unsigned pos,xtl,ytl,xth,yth,x,y;\r
+ byte *updatespot;\r
+ unsigned updatedelta;\r
+\r
+ block = &eraselist[0][0];\r
+\r
+ done = eraselistptr[0];\r
+\r
+ while (block != done)\r
+ {\r
+\r
+ //\r
+ // clip the block to the current screen view\r
+ //\r
+ block->screenx -= originxscreen;\r
+ block->screeny -= originyscreen;\r
+\r
+ if (block->screenx < 0)\r
+ {\r
+ block->width += block->screenx;\r
+ if (block->width<1)\r
+ goto next;\r
+ block->screenx = 0;\r
+ }\r
+\r
+ if (block->screeny < 0)\r
+ {\r
+ block->height += block->screeny;\r
+ if (block->height<1)\r
+ goto next;\r
+ block->screeny = 0;\r
+ }\r
+\r
+ screenxh = block->screenx + block->width;\r
+ screenyh = block->screeny + block->height;\r
+\r
+ if (screenxh > CGAPORTSCREENWIDE)\r
+ {\r
+ block->width = CGAPORTSCREENWIDE-block->screenx;\r
+ screenxh = block->screenx + block->width;\r
+ }\r
+\r
+ if (screenyh > PORTSCREENHIGH)\r
+ {\r
+ block->height = PORTSCREENHIGH-block->screeny;\r
+ screenyh = block->screeny + block->height;\r
+ }\r
+\r
+ if (block->width<1 || block->height<1)\r
+ goto next;\r
+\r
+ //\r
+ // erase the block by copying from the master screen\r
+ //\r
+ pos = ylookup[block->screeny]+block->screenx;\r
+ block->width = (block->width + (pos&1) + 1)& ~1;\r
+ pos &= ~1; // make sure a word copy gets used\r
+ VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
+ block->width,block->height);\r
+\r
+ //\r
+ // put 2s in update where the block was, to force sprites to update\r
+ //\r
+ xtl = block->screenx >> SX_T_SHIFT;\r
+ xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
+ ytl = block->screeny >> SY_T_SHIFT;\r
+ yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
+\r
+ updatespot = updateptr + uwidthtable[ytl] + xtl;\r
+ updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+ for (y=ytl;y<=yth;y++)\r
+ {\r
+ for (x=xtl;x<=xth;x++)\r
+ *updatespot++ = 2;\r
+ updatespot += updatedelta; // down to next line\r
+ }\r
+\r
+next:\r
+ block++;\r
+ }\r
+ eraselistptr[0] = &eraselist[0][0];\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_UpdateSprites CGA\r
+=\r
+= NOTE: Implement vertical clipping!\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_UpdateSprites (void)\r
+{\r
+ spritelisttype *sprite;\r
+ int portx,porty,x,y,xtl,xth,ytl,yth;\r
+ int priority;\r
+ unsigned dest;\r
+ byte *updatespot,*baseupdatespot;\r
+ unsigned updatedelta;\r
+\r
+ unsigned updatecount;\r
+ unsigned height,sourceofs;\r
+\r
+#ifdef PROFILE\r
+ updatecount = 0;\r
+#endif\r
+\r
+\r
+ for (priority=0;priority<PRIORITIES;priority++)\r
+ {\r
+ if (priority==MASKEDTILEPRIORITY)\r
+ RFL_MaskForegroundTiles ();\r
+\r
+ for (sprite = prioritystart[priority]; sprite ;\r
+ sprite = (spritelisttype *)sprite->nextsprite)\r
+ {\r
+ //\r
+ // see if the sprite has any visable area in the port\r
+ //\r
+\r
+ portx = sprite->screenx - originxscreen;\r
+ porty = sprite->screeny - originyscreen;\r
+ xtl = portx >> SX_T_SHIFT;\r
+ xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
+ ytl = porty >> SY_T_SHIFT;\r
+ yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
+\r
+ if (xtl<0)\r
+ xtl = 0;\r
+ if (xth>=PORTTILESWIDE)\r
+ xth = PORTTILESWIDE-1;\r
+ if (ytl<0)\r
+ ytl = 0;\r
+ if (yth>=PORTTILESHIGH)\r
+ yth = PORTTILESHIGH-1;\r
+\r
+ if (xtl>xth || ytl>yth)\r
+ continue;\r
+\r
+ //\r
+ // see if it's visable area covers any non 0 update tiles\r
+ //\r
+ updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
+ updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+ if (sprite->updatecount)\r
+ {\r
+ sprite->updatecount--; // the sprite was just placed,\r
+ goto redraw; // so draw it for sure\r
+ }\r
+\r
+ for (y=ytl;y<=yth;y++)\r
+ {\r
+ for (x=xtl;x<=xth;x++)\r
+ if (*updatespot++)\r
+ goto redraw;\r
+ updatespot += updatedelta; // down to next line\r
+ }\r
+ continue; // no need to update\r
+\r
+redraw:\r
+ //\r
+ // set the tiles it covers to 3, because those tiles are being\r
+ // updated\r
+ //\r
+ updatespot = baseupdatespot;\r
+ for (y=ytl;y<=yth;y++)\r
+ {\r
+ for (x=xtl;x<=xth;x++)\r
+ *updatespot++ = 3;\r
+ updatespot += updatedelta; // down to next line\r
+ }\r
+ //\r
+ // draw it!\r
+ //\r
+ height = sprite->height;\r
+ sourceofs = sprite->sourceofs;\r
+ if (porty<0)\r
+ {\r
+ height += porty; // clip top off\r
+ sourceofs -= porty*sprite->width;\r
+ porty = 0;\r
+ }\r
+ else if (porty+height>PORTSCREENHIGH)\r
+ {\r
+ height = PORTSCREENHIGH - porty; // clip bottom off\r
+ }\r
+\r
+ dest = bufferofs + ylookup[porty] + portx;\r
+\r
+ switch (sprite->draw)\r
+ {\r
+ case spritedraw:\r
+ VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
+ dest,sprite->width,height,sprite->planesize);\r
+ break;\r
+\r
+ case maskdraw:\r
+ break;\r
+\r
+ }\r
+#ifdef PROFILE\r
+ updatecount++;\r
+#endif\r
+\r
+\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Refresh CGA\r
+=\r
+= All routines will draw at the port at bufferofs, possibly copying from\r
+= the port at masterofs. The EGA version then page flips, while the\r
+= CGA version updates the screen from the buffer port.\r
+=\r
+= Screenpage is the currently displayed page, not the one being drawn\r
+= Otherpage is the page to be worked with now\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Refresh (void)\r
+{\r
+ long newtime,oldtimecount;\r
+\r
+ RFL_AnimateTiles ();\r
+\r
+//\r
+// update newly scrolled on tiles and animated tiles from the master screen\r
+//\r
+ RFL_UpdateTiles ();\r
+ RFL_EraseBlocks ();\r
+\r
+//\r
+// Update is all 0 except where sprites have changed or new area has\r
+// been scrolled on. Go through all sprites and update the ones that cover\r
+// a non 0 update tile\r
+//\r
+ RFL_UpdateSprites ();\r
+\r
+//\r
+// if the main program has a refresh hook set, call their function before\r
+// displaying the new page\r
+//\r
+ if (refreshvector)\r
+ refreshvector();\r
+\r
+//\r
+// update everything to the screen\r
+//\r
+ VW_CGAFullUpdate ();\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+ RFL_CalcTics ();\r
+}\r
+\r
+#endif // GRMODE == CGAGR\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_RF.H\r
+\r
+#define __ID_RF__\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+ CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define MINTICS 2\r
+#define MAXTICS 6\r
+#define DEMOTICS 3\r
+\r
+#define MAPBORDER 2 // map border must be at least 1\r
+\r
+#define MAXSPRITES 50 // max tracked sprites\r
+#define MAXANIMTILES 90 // max animating tiles on screen\r
+#define MAXANIMTYPES 50 // max different unique anim tiles on map\r
+\r
+#define MAXMAPHEIGHT 200\r
+\r
+#define PRIORITIES 4\r
+#define MASKEDTILEPRIORITY 3 // planes go: 0,1,2,MTILES,3\r
+\r
+#define TILEGLOBAL 256\r
+#define PIXGLOBAL 16\r
+\r
+#define G_T_SHIFT 8 // global >> ?? = tile\r
+#define G_P_SHIFT 4 // global >> ?? = pixels\r
+#define P_T_SHIFT 4 // pixels >> ?? = tile\r
+\r
+#define PORTTILESWIDE 21 // all drawing takes place inside a\r
+#define PORTTILESHIGH 14 // non displayed port of this size\r
+\r
+//#define PORTGLOBALWIDE (21*TILEGLOBAL)\r
+//#define PORTGLOBALHIGH (14*TILEGLOBAL)\r
+\r
+#define UPDATEWIDE (PORTTILESWIDE+1)\r
+#define UPDATEHIGH PORTTILESHIGH\r
+\r
+\r
+//===========================================================================\r
+\r
+typedef enum {spritedraw,maskdraw} drawtype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ PUBLIC VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+extern boolean compatability; // crippled refresh for wierdo SVGAs\r
+\r
+extern unsigned tics;\r
+extern long lasttimecount;\r
+\r
+extern unsigned originxglobal,originyglobal;\r
+extern unsigned originxtile,originytile;\r
+extern unsigned originxscreen,originyscreen;\r
+\r
+extern unsigned mapwidth,mapheight,mapbyteswide,mapwordswide\r
+ ,mapbytesextra,mapwordsextra;\r
+extern unsigned mapbwidthtable[MAXMAPHEIGHT];\r
+\r
+extern unsigned originxmin,originxmax,originymin,originymax;\r
+\r
+extern unsigned masterofs;\r
+\r
+//\r
+// the floating update window is also used by the view manager for\r
+// double buffer tracking\r
+//\r
+\r
+extern byte *updateptr; // current start of update window\r
+\r
+#if GRMODE == CGAGR\r
+extern byte *baseupdateptr;\r
+#endif\r
+\r
+extern unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+extern unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH];\r
+extern unsigned uwidthtable[UPDATEHIGH]; // lookup instead of multiple\r
+\r
+#define UPDATETERMINATE 0x0301\r
+\r
+/*\r
+=============================================================================\r
+\r
+ PUBLIC FUNCTIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void RF_Startup (void);\r
+void RF_Shutdown (void);\r
+\r
+void RF_FixOfs (void);\r
+void RF_NewMap (void);\r
+void RF_MarkTileGraphics (void);\r
+void RF_SetScrollBlock (int x, int y, boolean horizontal);\r
+void RF_NewPosition (unsigned x, unsigned y);\r
+void RF_Scroll (int x, int y);\r
+\r
+void RF_MapToMap (unsigned srcx, unsigned srcy,\r
+ unsigned destx, unsigned desty,\r
+ unsigned width, unsigned height);\r
+void RF_MemToMap (unsigned far *source, unsigned plane,\r
+ unsigned destx, unsigned desty,\r
+ unsigned width, unsigned height);\r
+\r
+void RF_ClearBlock (int x, int y, int width, int height);\r
+void RF_RedrawBlock (int x, int y, int width, int height);\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+ unsigned spritenumber, drawtype draw, int priority);\r
+void RF_RemoveSprite (void **user);\r
+\r
+void RF_CalcTics (void);\r
+\r
+void RF_Refresh (void);\r
+void RF_ForceRefresh (void);\r
+void RF_SetRefreshHook (void (*func) (void) );\r
+\r
+unsigned RF_FindFreeBuffer (void);\r
+\r
--- /dev/null
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; ID_RF_A.ASM\r
+\r
+IDEAL\r
+MODEL MEDIUM,C\r
+\r
+INCLUDE "ID_ASM.EQU"\r
+\r
+;============================================================================\r
+\r
+TILESWIDE = 21\r
+TILESHIGH = 14\r
+\r
+UPDATESIZE = (TILESWIDE+1)*TILESHIGH+1\r
+\r
+DATASEG\r
+\r
+EXTRN screenseg:WORD\r
+EXTRN updateptr:WORD\r
+EXTRN updatestart:WORD\r
+EXTRN masterofs:WORD ;start of master tile port\r
+EXTRN bufferofs:WORD ;start of current buffer port\r
+EXTRN screenstart:WORD ;starts of three screens (0/1/master) in EGA mem\r
+EXTRN grsegs:WORD\r
+EXTRN mapsegs:WORD\r
+EXTRN originmap:WORD\r
+EXTRN updatemapofs:WORD\r
+EXTRN tinf:WORD ;seg pointer to map header and tile info\r
+EXTRN blockstarts:WORD ;offsets from bufferofs for each update block\r
+\r
+planemask db ?\r
+planenum db ?\r
+\r
+CODESEG\r
+\r
+screenstartcs dw ? ;in code segment for accesability\r
+\r
+\r
+\r
+\r
+IFE GRMODE-CGAGR\r
+;============================================================================\r
+;\r
+; CGA refresh routines\r
+;\r
+;============================================================================\r
+\r
+TILEWIDTH = 4\r
+\r
+;=================\r
+;\r
+; RFL_NewTile\r
+;\r
+; Draws a composit two plane tile to the master screen and sets the update\r
+; spot to 1 in both update pages, forcing the tile to be copied to the\r
+; view pages the next two refreshes\r
+;\r
+; Called to draw newlly scrolled on strips and animating tiles\r
+;\r
+;=================\r
+\r
+PROC RFL_NewTile updateoffset:WORD\r
+PUBLIC RFL_NewTile\r
+USES SI,DI\r
+\r
+;\r
+; mark both update lists at this spot\r
+;\r
+ mov di,[updateoffset]\r
+\r
+ mov bx,[updateptr] ;start of update matrix\r
+ mov [BYTE bx+di],1\r
+\r
+ mov dx,SCREENWIDTH-TILEWIDTH ;add to get to start of next line\r
+\r
+;\r
+; set di to the location in screenseg to draw the tile\r
+;\r
+ shl di,1\r
+ mov si,[updatemapofs+di] ;offset in map from origin\r
+ add si,[originmap]\r
+ mov di,[blockstarts+di] ;screen location for tile\r
+ add di,[masterofs]\r
+\r
+;\r
+; set BX to the foreground tile number and SI to the background number\r
+; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
+; as one of the planes totally eclipses the other\r
+;\r
+ mov es,[mapsegs+2] ;foreground plane\r
+ mov bx,[es:si]\r
+ mov es,[mapsegs] ;background plane\r
+ mov si,[es:si]\r
+\r
+ mov es,[screenseg]\r
+\r
+ or bx,bx\r
+ jz @@singletile\r
+ jmp @@maskeddraw ;draw both together\r
+\r
+;=============\r
+;\r
+; Draw single background tile from main memory\r
+;\r
+;=============\r
+\r
+@@singletile:\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16*2+si]\r
+\r
+ xor si,si ;block is segment aligned\r
+\r
+REPT 15\r
+ movsw\r
+ movsw\r
+ add di,dx\r
+ENDM\r
+ movsw\r
+ movsw\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+ ret\r
+\r
+\r
+;=========\r
+;\r
+; Draw a masked tile combo\r
+; Interupts are disabled and the stack segment is reassigned\r
+;\r
+;=========\r
+@@maskeddraw:\r
+ cli ; don't allow ints when SS is set\r
+ shl bx,1\r
+ mov ss,[grsegs+STARTTILE16M*2+bx]\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16*2+si]\r
+\r
+ xor si,si ;first word of tile data\r
+\r
+REPT 16\r
+ mov ax,[si] ;background tile\r
+ and ax,[ss:si] ;mask\r
+ or ax,[ss:si+64] ;masked data\r
+ stosw\r
+ mov ax,[si+2] ;background tile\r
+ and ax,[ss:si+2] ;mask\r
+ or ax,[ss:si+66] ;masked data\r
+ stosw\r
+ add si,4\r
+ add di,dx\r
+ENDM\r
+\r
+ mov ax,@DATA\r
+ mov ss,ax\r
+ sti\r
+ mov ds,ax\r
+ ret\r
+ENDP\r
+\r
+ENDIF\r
+\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+;===========================================================================\r
+;\r
+; EGA refresh routines\r
+;\r
+;===========================================================================\r
+\r
+TILEWIDTH = 2\r
+\r
+;=================\r
+;\r
+; RFL_NewTile\r
+;\r
+; Draws a composit two plane tile to the master screen and sets the update\r
+; spot to 1 in both update pages, forcing the tile to be copied to the\r
+; view pages the next two refreshes\r
+;\r
+; Called to draw newlly scrolled on strips and animating tiles\r
+;\r
+; Assumes write mode 0\r
+;\r
+;=================\r
+\r
+PROC RFL_NewTile updateoffset:WORD\r
+PUBLIC RFL_NewTile\r
+USES SI,DI\r
+\r
+;\r
+; mark both update lists at this spot\r
+;\r
+ mov di,[updateoffset]\r
+\r
+ mov bx,[updatestart] ;page 0 pointer\r
+ mov [BYTE bx+di],1\r
+ mov bx,[updatestart+2] ;page 1 pointer\r
+ mov [BYTE bx+di],1\r
+\r
+;\r
+; set screenstartcs to the location in screenseg to draw the tile\r
+;\r
+ shl di,1\r
+ mov si,[updatemapofs+di] ;offset in map from origin\r
+ add si,[originmap]\r
+ mov di,[blockstarts+di] ;screen location for tile\r
+ add di,[masterofs]\r
+ mov [cs:screenstartcs],di\r
+\r
+;\r
+; set BX to the foreground tile number and SI to the background number\r
+; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
+; as one of the planes totally eclipses the other\r
+;\r
+ mov es,[mapsegs+2] ;foreground plane\r
+ mov bx,[es:si]\r
+ mov es,[mapsegs] ;background plane\r
+ mov si,[es:si]\r
+\r
+ mov es,[screenseg]\r
+ mov dx,SC_INDEX ;for stepping through map mask planes\r
+\r
+ or bx,bx\r
+ jz @@singletile\r
+ jmp @@maskeddraw ;draw both together\r
+\r
+;=========\r
+;\r
+; No foreground tile, so draw a single background tile.\r
+;\r
+;=========\r
+@@singletile:\r
+\r
+ mov bx,SCREENWIDTH-2 ;add to get to start of next line\r
+ shl si,1\r
+\r
+ mov ax,[cs:screenstartcs]\r
+ mov ds,[grsegs+STARTTILE16*2+si]\r
+\r
+ xor si,si ;block is segment aligned\r
+\r
+ mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+\r
+ mov cx,4 ;draw four planes\r
+@@planeloop:\r
+ mov dx,SC_INDEX\r
+ WORDOUT\r
+\r
+ mov di,[cs:screenstartcs] ;start at same place in all planes\r
+\r
+REPT 15\r
+ movsw\r
+ add di,bx\r
+ENDM\r
+ movsw\r
+\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ loop @@planeloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+ ret\r
+\r
+\r
+;=========\r
+;\r
+; Draw a masked tile combo\r
+; Interupts are disabled and the stack segment is reassigned\r
+;\r
+;=========\r
+@@maskeddraw:\r
+ cli ; don't allow ints when SS is set\r
+ shl bx,1\r
+ mov ss,[grsegs+STARTTILE16M*2+bx]\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16*2+si]\r
+\r
+ xor si,si ;first word of tile data\r
+\r
+ mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+\r
+ mov di,[cs:screenstartcs]\r
+@@planeloopm:\r
+ WORDOUT\r
+tileofs = 0\r
+lineoffset = 0\r
+REPT 16\r
+ mov bx,[si+tileofs] ;background tile\r
+ and bx,[ss:tileofs] ;mask\r
+ or bx,[ss:si+tileofs+32] ;masked data\r
+ mov [es:di+lineoffset],bx\r
+tileofs = tileofs + 2\r
+lineoffset = lineoffset + SCREENWIDTH\r
+ENDM\r
+ add si,32\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ cmp ah,10000b\r
+ je @@done ;drawn all four planes\r
+ jmp @@planeloopm\r
+\r
+@@done:\r
+ mov ax,@DATA\r
+ mov ss,ax\r
+ sti\r
+ mov ds,ax\r
+ ret\r
+ENDP\r
+\r
+ENDIF\r
+\r
+IFE GRMODE-VGAGR\r
+;============================================================================\r
+;\r
+; VGA refresh routines\r
+;\r
+;============================================================================\r
+\r
+\r
+ENDIF\r
+\r
+\r
+;============================================================================\r
+;\r
+; reasonably common refresh routines\r
+;\r
+;============================================================================\r
+\r
+\r
+;=================\r
+;\r
+; RFL_UpdateTiles\r
+;\r
+; Scans through the update matrix pointed to by updateptr, looking for 1s.\r
+; A 1 represents a tile that needs to be copied from the master screen to the\r
+; current screen (a new row or an animated tiled). If more than one adjacent\r
+; tile in a horizontal row needs to be copied, they will be copied as a group.\r
+;\r
+; Assumes write mode 1\r
+;\r
+;=================\r
+\r
+\r
+; AX 0/1 for scasb, temp for segment register transfers\r
+; BX width for block copies\r
+; CX REP counter\r
+; DX line width deltas\r
+; SI source for copies\r
+; DI scas dest / movsb dest\r
+; BP pointer to UPDATETERMINATE\r
+;\r
+; DS\r
+; ES\r
+; SS\r
+\r
+PROC RFL_UpdateTiles\r
+PUBLIC RFL_UpdateTiles\r
+USES SI,DI,BP\r
+\r
+ jmp SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+ ret\r
+\r
+@@realstart:\r
+ mov di,[updateptr]\r
+ mov bp,(TILESWIDE+1)*TILESHIGH+1\r
+ add bp,di ; when di = bx, all tiles have been scanned\r
+ push di\r
+ mov cx,-1 ; definately scan the entire thing\r
+\r
+;\r
+; scan for a 1 in the update list, meaning a tile needs to be copied\r
+; from the master screen to the current screen\r
+;\r
+@@findtile:\r
+ pop di ; place to continue scaning from\r
+ mov ax,ss\r
+ mov es,ax ; search in the data segment\r
+ mov ds,ax\r
+ mov al,1\r
+ repne scasb\r
+ cmp di,bp\r
+ je @@done\r
+\r
+ cmp [BYTE di],al\r
+ jne @@singletile\r
+ jmp @@tileblock\r
+\r
+;============\r
+;\r
+; copy a single tile\r
+;\r
+;============\r
+EVEN\r
+@@singletile:\r
+ inc di ; we know the next tile is nothing\r
+ push di ; save off the spot being scanned\r
+ sub di,[updateptr]\r
+ shl di,1\r
+ mov di,[blockstarts-4+di] ; start of tile location on screen\r
+ mov si,di\r
+ add di,[bufferofs] ; dest in current screen\r
+ add si,[masterofs] ; source in master screen\r
+\r
+ mov dx,SCREENWIDTH-TILEWIDTH\r
+ mov ax,[screenseg]\r
+ mov ds,ax\r
+ mov es,ax\r
+\r
+;--------------------------\r
+\r
+IFE GRMODE-CGAGR\r
+\r
+REPT 15\r
+ movsw\r
+ movsw\r
+ add si,dx\r
+ add di,dx\r
+ENDM\r
+ movsw\r
+ movsw\r
+\r
+ENDIF\r
+\r
+;--------------------------\r
+\r
+IFE GRMODE-EGAGR\r
+\r
+REPT 15\r
+ movsb\r
+ movsb\r
+ add si,dx\r
+ add di,dx\r
+ENDM\r
+ movsb\r
+ movsb\r
+\r
+ENDIF\r
+\r
+;--------------------------\r
+\r
+ jmp @@findtile\r
+\r
+;============\r
+;\r
+; more than one tile in a row needs to be updated, so do it as a group\r
+;\r
+;============\r
+EVEN\r
+@@tileblock:\r
+ mov dx,di ; hold starting position + 1 in dx\r
+ inc di ; we know the next tile also gets updated\r
+ repe scasb ; see how many more in a row\r
+ push di ; save off the spot being scanned\r
+\r
+ mov bx,di\r
+ sub bx,dx ; number of tiles in a row\r
+ shl bx,1 ; number of bytes / row\r
+\r
+ mov di,dx ; lookup position of start tile\r
+ sub di,[updateptr]\r
+ shl di,1\r
+ mov di,[blockstarts-2+di] ; start of tile location\r
+ mov si,di\r
+ add di,[bufferofs] ; dest in current screen\r
+ add si,[masterofs] ; source in master screen\r
+\r
+ mov dx,SCREENWIDTH\r
+ sub dx,bx ; offset to next line on screen\r
+IFE GRMODE-CGAGR\r
+ sub dx,bx ; bx is words wide in CGA tiles\r
+ENDIF\r
+\r
+ mov ax,[screenseg]\r
+ mov ds,ax\r
+ mov es,ax\r
+\r
+REPT 15\r
+ mov cx,bx\r
+IFE GRMODE-CGAGR\r
+ rep movsw\r
+ENDIF\r
+IFE GRMODE-EGAGR\r
+ rep movsb\r
+ENDIF\r
+ add si,dx\r
+ add di,dx\r
+ENDM\r
+ mov cx,bx\r
+IFE GRMODE-CGAGR\r
+ rep movsw\r
+ENDIF\r
+IFE GRMODE-EGAGR\r
+ rep movsb\r
+ENDIF\r
+\r
+ dec cx ; was 0 from last rep movsb, now $ffff for scasb\r
+ jmp @@findtile\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;=================\r
+;\r
+; RFL_MaskForegroundTiles\r
+;\r
+; Scan through update looking for 3's. If the foreground tile there is a\r
+; masked foreground tile, draw it to the screen\r
+;\r
+;=================\r
+\r
+PROC RFL_MaskForegroundTiles\r
+PUBLIC RFL_MaskForegroundTiles\r
+USES SI,DI,BP\r
+ jmp SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+ ret\r
+\r
+@@realstart:\r
+ mov di,[updateptr]\r
+ mov bp,(TILESWIDE+1)*TILESHIGH+2\r
+ add bp,di ; when di = bx, all tiles have been scanned\r
+ push di\r
+ mov cx,-1 ; definately scan the entire thing\r
+;\r
+; scan for a 3 in the update list\r
+;\r
+@@findtile:\r
+ mov ax,ss\r
+ mov es,ax ; scan in the data segment\r
+ mov al,3\r
+ pop di ; place to continue scaning from\r
+ repne scasb\r
+ cmp di,bp\r
+ je @@done\r
+\r
+;============\r
+;\r
+; found a tile, see if it needs to be masked on\r
+;\r
+;============\r
+\r
+ push di\r
+\r
+ sub di,[updateptr]\r
+ shl di,1\r
+ mov si,[updatemapofs-2+di] ; offset from originmap\r
+ add si,[originmap]\r
+\r
+ mov es,[mapsegs+2] ; foreground map plane segment\r
+ mov si,[es:si] ; foreground tile number\r
+\r
+ or si,si\r
+ jz @@findtile ; 0 = no foreground tile\r
+\r
+ mov bx,si\r
+ add bx,INTILE ;INTILE tile info table\r
+ mov es,[tinf]\r
+ test [BYTE PTR es:bx],80h ;high bit = masked tile\r
+ jz @@findtile\r
+\r
+;-------------------\r
+\r
+IFE GRMODE-CGAGR\r
+;=================\r
+;\r
+; mask the tile CGA\r
+;\r
+;=================\r
+\r
+ mov di,[blockstarts-2+di]\r
+ add di,[bufferofs]\r
+ mov es,[screenseg]\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16M*2+si]\r
+\r
+ mov bx,64 ;data starts 64 bytes after mask\r
+\r
+ xor si,si\r
+\r
+lineoffset = 0\r
+REPT 16\r
+ mov ax,[es:di+lineoffset] ;background\r
+ and ax,[si] ;mask\r
+ or ax,[si+bx] ;masked data\r
+ mov [es:di+lineoffset],ax ;background\r
+ inc si\r
+ inc si\r
+ mov ax,[es:di+lineoffset+2] ;background\r
+ and ax,[si] ;mask\r
+ or ax,[si+bx] ;masked data\r
+ mov [es:di+lineoffset+2],ax ;background\r
+ inc si\r
+ inc si\r
+lineoffset = lineoffset + SCREENWIDTH\r
+ENDM\r
+ENDIF\r
+\r
+;-------------------\r
+\r
+IFE GRMODE-EGAGR\r
+;=================\r
+;\r
+; mask the tile\r
+;\r
+;=================\r
+\r
+ mov [BYTE planemask],1\r
+ mov [BYTE planenum],0\r
+\r
+ mov di,[blockstarts-2+di]\r
+ add di,[bufferofs]\r
+ mov [cs:screenstartcs],di\r
+ mov es,[screenseg]\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16M*2+si]\r
+\r
+ mov bx,32 ;data starts 32 bytes after mask\r
+\r
+@@planeloopm:\r
+ mov dx,SC_INDEX\r
+ mov al,SC_MAPMASK\r
+ mov ah,[ss:planemask]\r
+ WORDOUT\r
+ mov dx,GC_INDEX\r
+ mov al,GC_READMAP\r
+ mov ah,[ss:planenum]\r
+ WORDOUT\r
+\r
+ xor si,si\r
+ mov di,[cs:screenstartcs]\r
+lineoffset = 0\r
+REPT 16\r
+ mov cx,[es:di+lineoffset] ;background\r
+ and cx,[si] ;mask\r
+ or cx,[si+bx] ;masked data\r
+ inc si\r
+ inc si\r
+ mov [es:di+lineoffset],cx\r
+lineoffset = lineoffset + SCREENWIDTH\r
+ENDM\r
+ add bx,32 ;the mask is now further away\r
+ inc [ss:planenum]\r
+ shl [ss:planemask],1 ;shift plane mask over for next plane\r
+ cmp [ss:planemask],10000b ;done all four planes?\r
+ je @@drawn ;drawn all four planes\r
+ jmp @@planeloopm\r
+\r
+@@drawn:\r
+ENDIF\r
+\r
+;-------------------\r
+\r
+ mov ax,ss\r
+ mov ds,ax\r
+ mov cx,-1 ;definately scan the entire thing\r
+\r
+ jmp @@findtile\r
+\r
+ENDP\r
+\r
+\r
+END\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_SD.c - Sound Manager\r
+// v1.1d1\r
+// By Jason Blochowiak\r
+//\r
+\r
+//\r
+// This module handles dealing with generating sound on the appropriate\r
+// hardware\r
+//\r
+// Depends on: User Mgr (for parm checking)\r
+//\r
+// Globals:\r
+// For User Mgr:\r
+// SoundSourcePresent - Sound Source thingie present?\r
+// SoundBlasterPresent - SoundBlaster card present?\r
+// AdLibPresent - AdLib card present?\r
+// SoundMode - What device is used for sound effects\r
+// (Use SM_SetSoundMode() to set)\r
+// MusicMode - What device is used for music\r
+// (Use SM_SetMusicMode() to set)\r
+// For Cache Mgr:\r
+// NeedsDigitized - load digitized sounds?\r
+// NeedsMusic - load music?\r
+//\r
+\r
+#define USE_MUSIC 0\r
+\r
+#pragma hdrstop // Wierdo thing with MUSE\r
+\r
+#include <dos.h>\r
+\r
+#ifdef _MUSE_ // Will be defined in ID_Types.h\r
+#include "ID_SD.h"\r
+#else\r
+#include "ID_HEADS.H"\r
+#endif\r
+#pragma hdrstop\r
+#pragma warn -pia\r
+\r
+#define SDL_SoundFinished() {SoundNumber = SoundPriority = 0;}\r
+\r
+// Macros for AdLib stuff\r
+#define selreg(n) outportb(0x388,n)\r
+#define writereg(n) outportb(0x389,n)\r
+#define readstat() inportb(0x388)\r
+\r
+// Global variables\r
+ boolean SoundSourcePresent,SoundBlasterPresent,AdLibPresent,\r
+ NeedsDigitized,NeedsMusic;\r
+ SDMode SoundMode;\r
+ SMMode MusicMode;\r
+ longword TimeCount;\r
+ word HackCount;\r
+ word *SoundTable; // Really * _seg *SoundTable, but that don't work\r
+ boolean ssIsTandy;\r
+ word ssPort = 2;\r
+\r
+// Internal variables\r
+static boolean SD_Started;\r
+static boolean TimerDone;\r
+static word TimerVal,TimerDelay10,TimerDelay25,TimerDelay100;\r
+static longword TimerDivisor,TimerCount;\r
+static char *ParmStrings[] =\r
+ {\r
+ "noal",\r
+ nil\r
+ };\r
+static void (*SoundUserHook)(void);\r
+static word SoundNumber,SoundPriority;\r
+static void interrupt (*t0OldService)(void);\r
+//static word t0CountTable[] = {8,8,8,8,40,40};\r
+static long LocalTime;\r
+\r
+// PC Sound variables\r
+static byte pcLastSample,far *pcSound;\r
+static longword pcLengthLeft;\r
+static word pcSoundLookup[255];\r
+\r
+// AdLib variables\r
+static boolean alNoCheck;\r
+static byte far *alSound;\r
+static word alBlock;\r
+static longword alLengthLeft;\r
+static longword alTimeCount;\r
+static Instrument alZeroInst;\r
+\r
+// This table maps channel numbers to carrier and modulator op cells\r
+static byte carriers[9] = { 3, 4, 5,11,12,13,19,20,21},\r
+ modifiers[9] = { 0, 1, 2, 8, 9,10,16,17,18},\r
+// This table maps percussive voice numbers to op cells\r
+ pcarriers[5] = {19,0xff,0xff,0xff,0xff},\r
+ pmodifiers[5] = {16,17,18,20,21};\r
+\r
+// Sequencer variables\r
+static boolean sqActive;\r
+static word alFXReg;\r
+static ActiveTrack *tracks[sqMaxTracks],\r
+ mytracks[sqMaxTracks];\r
+static word sqMode,sqFadeStep;\r
+static word far *sqHack,far *sqHackPtr,sqHackLen,sqHackSeqLen;\r
+static long sqHackTime;\r
+\r
+// Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_SetTimer0() - Sets system timer 0 to the specified speed\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma argsused\r
+static void\r
+SDL_SetTimer0(word speed)\r
+{\r
+#ifndef TPROF // If using Borland's profiling, don't screw with the timer\r
+ outportb(0x43,0x36); // Change timer 0\r
+ outportb(0x40,speed);\r
+ outportb(0x40,speed >> 8);\r
+ TimerDivisor = speed;\r
+#else\r
+ TimerDivisor = 0x10000;\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_SetIntsPerSec() - Uses SDL_SetTimer0() to set the number of\r
+// interrupts generated by system timer 0 per second\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SetIntsPerSec(word ints)\r
+{\r
+ SDL_SetTimer0(1192030 / ints);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_TimingService() - Used by SDL_InitDelay() to determine a timing\r
+// value for the current system that we're running on\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+SDL_TimingService(void)\r
+{\r
+ TimerVal = _CX;\r
+ TimerDone++;\r
+\r
+ outportb(0x20,0x20); // Ack interrupt\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_InitDelay() - Sets up TimerDelay's for SDL_Delay()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_InitDelay(void)\r
+{\r
+ int i;\r
+ word timer;\r
+\r
+ setvect(8,SDL_TimingService); // Set to my timer 0 ISR\r
+\r
+ SDL_SetIntsPerSec(1000); // Time 1ms\r
+\r
+ for (i = 0,timer = 0;i < 10;i++) // Do timing test 10 times\r
+ {\r
+ asm xor dx,dx // Zero DX\r
+ asm mov cx,0xffff // Put starting value in CX\r
+ asm mov [TimerDone],cx // TimerDone = false - 1\r
+startloop:\r
+ asm or [TimerDone],0\r
+ asm jnz startloop // Make sure we're at the start\r
+loop:\r
+ asm test [TimerDone],1 // See if TimerDone flag got hit\r
+ asm jnz done // Yep - drop out of the loop\r
+ asm loop loop\r
+done:\r
+\r
+ if (0xffff - TimerVal > timer)\r
+ timer = 0xffff - TimerVal;\r
+ }\r
+ timer += timer / 2; // Use some slop\r
+ TimerDelay10 = timer / (1000 / 10);\r
+ TimerDelay25 = timer / (1000 / 25);\r
+ TimerDelay100 = timer / (1000 / 100);\r
+\r
+ SDL_SetTimer0(0); // Reset timer 0\r
+\r
+ setvect(8,t0OldService); // Set back to old ISR\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_Delay() - Delays the specified amount of time\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_Delay(word delay)\r
+{\r
+ if (!delay)\r
+ return;\r
+\r
+asm mov cx,[delay]\r
+loop:\r
+asm test [TimerDone],0 // Useless code - just for timing equivilency\r
+asm jnz done\r
+asm loop loop\r
+done:;\r
+}\r
+\r
+//\r
+// PC Sound code\r
+//\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_PCPlaySound() - Plays the specified sound on the PC speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_PCPlaySound(PCSound far *sound)\r
+{\r
+asm pushf\r
+asm cli\r
+\r
+ pcLastSample = -1;\r
+ pcLengthLeft = sound->common.length;\r
+ pcSound = sound->data;\r
+\r
+asm popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_PCStopSound() - Stops the current sound playing on the PC Speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_PCStopSound(void)\r
+{\r
+asm pushf\r
+asm cli\r
+\r
+ (long)pcSound = 0;\r
+\r
+asm in al,0x61 // Turn the speaker off\r
+asm and al,0xfd // ~2\r
+asm out 0x61,al\r
+\r
+asm popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_PCService() - Handles playing the next sample in a PC sound\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_PCService(void)\r
+{\r
+ byte s;\r
+ word t;\r
+\r
+ if (pcSound)\r
+ {\r
+ s = *pcSound++;\r
+ if (s != pcLastSample)\r
+ {\r
+ asm pushf\r
+ asm cli\r
+\r
+ pcLastSample = s;\r
+ if (s) // We have a frequency!\r
+ {\r
+ t = pcSoundLookup[s];\r
+ asm mov bx,[t]\r
+\r
+ asm mov al,0xb6 // Write to channel 2 (speaker) timer\r
+ asm out 43h,al\r
+ asm mov al,bl\r
+ asm out 42h,al // Low byte\r
+ asm mov al,bh\r
+ asm out 42h,al // High byte\r
+\r
+ asm in al,0x61 // Turn the speaker & gate on\r
+ asm or al,3\r
+ asm out 0x61,al\r
+ }\r
+ else // Time for some silence\r
+ {\r
+ asm in al,0x61 // Turn the speaker & gate off\r
+ asm and al,0xfc // ~3\r
+ asm out 0x61,al\r
+ }\r
+\r
+ asm popf\r
+ }\r
+\r
+ if (!(--pcLengthLeft))\r
+ {\r
+ SDL_PCStopSound();\r
+ SDL_SoundFinished();\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_ShutPC() - Turns off the pc speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutPC(void)\r
+{\r
+asm pushf\r
+asm cli\r
+\r
+ pcSound = 0;\r
+\r
+asm in al,0x61 // Turn the speaker & gate off\r
+asm and al,0xfc // ~3\r
+asm out 0x61,al\r
+\r
+asm popf\r
+}\r
+\r
+// AdLib Code\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// alOut(n,b) - Puts b in AdLib card register n\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+alOut(byte n,byte b)\r
+{\r
+asm pushf\r
+asm cli\r
+\r
+asm mov dx,0x388\r
+asm mov al,[n]\r
+asm out dx,al\r
+#if 0\r
+ SDL_Delay(TimerDelay10);\r
+#else\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+#endif\r
+\r
+asm mov dx,0x389\r
+asm mov al,[b]\r
+asm out dx,al\r
+\r
+asm popf\r
+\r
+#if 0\r
+ SDL_Delay(TimerDelay25);\r
+#else\r
+asm mov dx,0x388\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+asm in al, dx\r
+#endif\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_SetInstrument() - Puts an instrument into a generator\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SetInstrument(int track,int which,Instrument far *inst,boolean percussive)\r
+{\r
+ byte c,m;\r
+\r
+ if (percussive)\r
+ {\r
+ c = pcarriers[which];\r
+ m = pmodifiers[which];\r
+ }\r
+ else\r
+ {\r
+ c = carriers[which];\r
+ m = modifiers[which];\r
+ }\r
+\r
+ tracks[track - 1]->inst = *inst;\r
+ tracks[track - 1]->percussive = percussive;\r
+\r
+ alOut(m + alChar,inst->mChar);\r
+ alOut(m + alScale,inst->mScale);\r
+ alOut(m + alAttack,inst->mAttack);\r
+ alOut(m + alSus,inst->mSus);\r
+ alOut(m + alWave,inst->mWave);\r
+\r
+ // Most percussive instruments only use one cell\r
+ if (c != 0xff)\r
+ {\r
+ alOut(c + alChar,inst->cChar);\r
+ alOut(c + alScale,inst->cScale);\r
+ alOut(c + alAttack,inst->cAttack);\r
+ alOut(c + alSus,inst->cSus);\r
+ alOut(c + alWave,inst->cWave);\r
+ }\r
+\r
+ alOut(which + alFeedCon,inst->nConn); // DEBUG - I think this is right\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_ALStopSound() - Turns off any sound effects playing through the\r
+// AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_ALStopSound(void)\r
+{\r
+asm pushf\r
+asm cli\r
+\r
+ (long)alSound = 0;\r
+ alOut(alFreqH + 0,0);\r
+\r
+asm popf\r
+}\r
+\r
+static void\r
+SDL_AlSetFXInst(Instrument far *inst)\r
+{\r
+ byte c,m;\r
+\r
+ m = modifiers[0];\r
+ c = carriers[0];\r
+ alOut(m + alChar,inst->mChar);\r
+ alOut(m + alScale,inst->mScale);\r
+ alOut(m + alAttack,inst->mAttack);\r
+ alOut(m + alSus,inst->mSus);\r
+ alOut(m + alWave,inst->mWave);\r
+ alOut(c + alChar,inst->cChar);\r
+ alOut(c + alScale,inst->cScale);\r
+ alOut(c + alAttack,inst->cAttack);\r
+ alOut(c + alSus,inst->cSus);\r
+ alOut(c + alWave,inst->cWave);\r
+ // DEBUG!!! - I just put this in\r
+// alOut(alFeedCon,inst->nConn);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_ALPlaySound() - Plays the specified sound on the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_ALPlaySound(AdLibSound far *sound)\r
+{\r
+ Instrument far *inst;\r
+\r
+ SDL_ALStopSound();\r
+\r
+asm pushf\r
+asm cli\r
+\r
+ alLengthLeft = sound->common.length;\r
+ alSound = sound->data;\r
+ alBlock = ((sound->block & 7) << 2) | 0x20;\r
+ inst = &sound->inst;\r
+\r
+ if (!(inst->mSus | inst->cSus))\r
+ {\r
+ asm popf\r
+ Quit("SDL_ALPlaySound() - Bad instrument");\r
+ }\r
+\r
+ SDL_AlSetFXInst(inst);\r
+\r
+asm popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_ALSoundService() - Plays the next sample out through the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ALSoundService(void)\r
+{\r
+ byte s;\r
+\r
+ if (alSound)\r
+ {\r
+ s = *alSound++;\r
+ if (!s)\r
+ alOut(alFreqH + 0,0);\r
+ else\r
+ {\r
+ alOut(alFreqL + 0,s);\r
+ alOut(alFreqH + 0,alBlock);\r
+ }\r
+\r
+ if (!(--alLengthLeft))\r
+ {\r
+ (long)alSound = 0;\r
+ alOut(alFreqH + 0,0);\r
+ SDL_SoundFinished();\r
+ }\r
+ }\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_SelectMeasure() - sets up sequencing variables for a given track\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SelectMeasure(ActiveTrack *track)\r
+{\r
+ track->seq = track->moods[track->mood];\r
+ track->nextevent = 0;\r
+}\r
+#endif\r
+\r
+static void\r
+SDL_ALService(void)\r
+{\r
+ byte a,v;\r
+ word w;\r
+\r
+ if (!sqActive)\r
+ return;\r
+\r
+ while (sqHackLen && (sqHackTime <= alTimeCount))\r
+ {\r
+ w = *sqHackPtr++;\r
+ sqHackTime = alTimeCount + *sqHackPtr++;\r
+ asm mov dx,[w]\r
+ asm mov [a],dl\r
+ asm mov [v],dh\r
+ alOut(a,v);\r
+ sqHackLen -= 4;\r
+ }\r
+ alTimeCount++;\r
+ if (!sqHackLen)\r
+ {\r
+ sqHackPtr = (word far *)sqHack;\r
+ sqHackLen = sqHackSeqLen;\r
+ alTimeCount = sqHackTime = 0;\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_ShutAL() - Shuts down the AdLib card for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutAL(void)\r
+{\r
+asm pushf\r
+asm cli\r
+\r
+ alOut(alEffects,0);\r
+ alOut(alFreqH + 0,0);\r
+ SDL_AlSetFXInst(&alZeroInst);\r
+ alSound = 0;\r
+\r
+asm popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_CleanAL() - Totally shuts down the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_CleanAL(void)\r
+{\r
+ int i;\r
+\r
+asm pushf\r
+asm cli\r
+\r
+ alOut(alEffects,0);\r
+ for (i = 1;i < 0xf5;i++)\r
+ alOut(i,0);\r
+\r
+asm popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_StartAL() - Starts up the AdLib card for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_StartAL(void)\r
+{\r
+ alFXReg = 0;\r
+ alOut(alEffects,alFXReg);\r
+ SDL_AlSetFXInst(&alZeroInst);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_DetectAdLib() - Determines if there's an AdLib (or SoundBlaster\r
+// emulating an AdLib) present\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+SDL_DetectAdLib(void)\r
+{\r
+ byte status1,status2;\r
+ int i;\r
+\r
+ alOut(4,0x60); // Reset T1 & T2\r
+ alOut(4,0x80); // Reset IRQ\r
+ status1 = readstat();\r
+ alOut(2,0xff); // Set timer 1\r
+ alOut(4,0x21); // Start timer 1\r
+ SDL_Delay(TimerDelay100);\r
+\r
+ status2 = readstat();\r
+ alOut(4,0x60);\r
+ alOut(4,0x80);\r
+\r
+ if (((status1 & 0xe0) == 0x00) && ((status2 & 0xe0) == 0xc0))\r
+ {\r
+ for (i = 1;i <= 0xf5;i++) // Zero all the registers\r
+ alOut(i,0);\r
+\r
+ alOut(1,0x20); // Set WSE=1\r
+ alOut(8,0); // Set CSM=0 & SEL=0\r
+\r
+ return(true);\r
+ }\r
+ else\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_t0Service() - My timer 0 ISR which handles the different timings and\r
+// dispatches to whatever other routines are appropriate\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+SDL_t0Service(void)\r
+{\r
+static word count = 1;\r
+\r
+#if 0 // for debugging\r
+asm mov dx,STATUS_REGISTER_1\r
+asm in al,dx\r
+asm mov dx,ATR_INDEX\r
+asm mov al,ATR_OVERSCAN\r
+asm out dx,al\r
+asm mov al,4 // red\r
+asm out dx,al\r
+#endif\r
+\r
+ HackCount++;\r
+\r
+#if USE_MUSIC\r
+ if (MusicMode == smm_AdLib)\r
+ {\r
+ SDL_ALService();\r
+ if (!(++count & 7))\r
+ {\r
+ LocalTime++;\r
+ TimeCount++;\r
+ if (SoundUserHook)\r
+ SoundUserHook();\r
+ }\r
+ if (!(count & 3))\r
+ {\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_PC:\r
+ SDL_PCService();\r
+ break;\r
+ case sdm_AdLib:\r
+ SDL_ALSoundService();\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ else\r
+#endif\r
+ {\r
+ if (!(++count & 1))\r
+ {\r
+ LocalTime++;\r
+ TimeCount++;\r
+ if (SoundUserHook)\r
+ SoundUserHook();\r
+ }\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_PC:\r
+ SDL_PCService();\r
+ break;\r
+ case sdm_AdLib:\r
+ SDL_ALSoundService();\r
+ break;\r
+ }\r
+ }\r
+\r
+asm mov ax,[WORD PTR TimerCount]\r
+asm add ax,[WORD PTR TimerDivisor]\r
+asm mov [WORD PTR TimerCount],ax\r
+asm jnc myack\r
+ t0OldService(); // If we overflow a word, time to call old int handler\r
+asm jmp olddone\r
+myack:;\r
+ outportb(0x20,0x20); // Ack the interrupt\r
+olddone:;\r
+\r
+#if 0 // for debugging\r
+asm mov dx,STATUS_REGISTER_1\r
+asm in al,dx\r
+asm mov dx,ATR_INDEX\r
+asm mov al,ATR_OVERSCAN\r
+asm out dx,al\r
+asm mov al,3 // blue\r
+asm out dx,al\r
+asm mov al,0x20 // normal\r
+asm out dx,al\r
+#endif\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_ShutDevice() - turns off whatever device was being used for sound fx\r
+//\r
+////////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutDevice(void)\r
+{\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_PC:\r
+ SDL_ShutPC();\r
+ break;\r
+ case sdm_AdLib:\r
+ SDL_ShutAL();\r
+ break;\r
+ }\r
+ SoundMode = sdm_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_CleanDevice() - totally shuts down all sound devices\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_CleanDevice(void)\r
+{\r
+ if ((SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib))\r
+ SDL_CleanAL();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SDL_StartDevice() - turns on whatever device is to be used for sound fx\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_StartDevice(void)\r
+{\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_AdLib:\r
+ SDL_StartAL();\r
+ break;\r
+ }\r
+ SoundNumber = SoundPriority = 0;\r
+}\r
+\r
+static void\r
+SDL_SetTimerSpeed(void)\r
+{\r
+ word rate;\r
+\r
+#if USE_MUSIC\r
+ if (MusicMode == smm_AdLib)\r
+ rate = TickBase * 8;\r
+ else\r
+#endif\r
+ rate = TickBase * 2;\r
+ SDL_SetIntsPerSec(rate);\r
+}\r
+\r
+// Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_SetSoundMode() - Sets which sound hardware to use for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_SetSoundMode(SDMode mode)\r
+{\r
+ boolean result;\r
+ word tableoffset;\r
+\r
+ SD_StopSound();\r
+\r
+#ifndef _MUSE_\r
+ switch (mode)\r
+ {\r
+ case sdm_Off:\r
+ NeedsDigitized = false;\r
+ result = true;\r
+ break;\r
+ case sdm_PC:\r
+ tableoffset = STARTPCSOUNDS;\r
+ NeedsDigitized = false;\r
+ result = true;\r
+ break;\r
+ case sdm_AdLib:\r
+ if (AdLibPresent)\r
+ {\r
+ tableoffset = STARTADLIBSOUNDS;\r
+ NeedsDigitized = false;\r
+ result = true;\r
+ }\r
+ break;\r
+ default:\r
+ result = false;\r
+ break;\r
+ }\r
+#endif\r
+\r
+ if (result && (mode != SoundMode))\r
+ {\r
+ SDL_ShutDevice();\r
+ SoundMode = mode;\r
+#ifndef _MUSE_\r
+ SoundTable = (word *)(&audiosegs[tableoffset]);\r
+#endif\r
+ SDL_StartDevice();\r
+ }\r
+\r
+ SDL_SetTimerSpeed();\r
+\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_SetMusicMode() - sets the device to use for background music\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_SetMusicMode(SMMode mode)\r
+{\r
+#if USE_MUSIC\r
+ boolean result;\r
+\r
+ SD_FadeOutMusic();\r
+ while (SD_MusicPlaying())\r
+ ;\r
+\r
+ switch (mode)\r
+ {\r
+ case smm_Off:\r
+ NeedsMusic = false;\r
+ result = true;\r
+ break;\r
+ case smm_AdLib:\r
+ if (AdLibPresent)\r
+ {\r
+ NeedsMusic = true;\r
+ result = true;\r
+ }\r
+ break;\r
+ default:\r
+ result = false;\r
+ break;\r
+ }\r
+\r
+ if (result)\r
+ MusicMode = mode;\r
+\r
+ SDL_SetTimerSpeed();\r
+\r
+ return(result);\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_Startup() - starts up the Sound Mgr\r
+// Detects all additional sound hardware and installs my ISR\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Startup(void)\r
+{\r
+ int i;\r
+\r
+ if (SD_Started)\r
+ return;\r
+\r
+ ssIsTandy = false;\r
+ alNoCheck = false;\r
+#ifndef _MUSE_\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ switch (US_CheckParm(_argv[i],ParmStrings))\r
+ {\r
+ case 0: // No AdLib detection\r
+ alNoCheck = true;\r
+ break;\r
+ }\r
+ }\r
+#endif\r
+\r
+ SoundUserHook = 0;\r
+\r
+ t0OldService = getvect(8); // Get old timer 0 ISR\r
+\r
+ SDL_InitDelay(); // SDL_InitDelay() uses t0OldService\r
+\r
+ setvect(8,SDL_t0Service); // Set to my timer 0 ISR\r
+ LocalTime = TimeCount = alTimeCount = 0;\r
+\r
+ SD_SetSoundMode(sdm_Off);\r
+#if USE_MUSIC\r
+ SD_SetMusicMode(smm_Off);\r
+#endif\r
+\r
+ if (!alNoCheck)\r
+ AdLibPresent = SDL_DetectAdLib();\r
+\r
+ for (i = 0;i < 255;i++)\r
+ pcSoundLookup[i] = i * 60;\r
+\r
+ SD_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_Default() - Sets up the default behaviour for the Sound Mgr whether\r
+// the config file was present or not.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Default(boolean gotit,SDMode sd,SMMode sm)\r
+{\r
+ boolean gotsd,gotsm;\r
+\r
+ gotsd = gotsm = gotit;\r
+\r
+ if (gotsd) // Make sure requested sound hardware is available\r
+ {\r
+ switch (sd)\r
+ {\r
+ case sdm_AdLib:\r
+ gotsd = AdLibPresent;\r
+ break;\r
+ }\r
+ }\r
+ if (!gotsd)\r
+ {\r
+ if (AdLibPresent)\r
+ sd = sdm_AdLib;\r
+ else\r
+ sd = sdm_PC;\r
+ }\r
+ if (sd != SoundMode)\r
+ SD_SetSoundMode(sd);\r
+\r
+\r
+ if (gotsm) // Make sure requested music hardware is available\r
+ {\r
+ switch (sm)\r
+ {\r
+ case sdm_AdLib:\r
+ gotsm = AdLibPresent;\r
+ break;\r
+ }\r
+ }\r
+ if (!gotsm)\r
+ {\r
+ if (AdLibPresent)\r
+ sm = smm_AdLib;\r
+ }\r
+#if USE_MUSIC\r
+ if (sm != MusicMode)\r
+ SD_SetMusicMode(sm);\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_Shutdown() - shuts down the Sound Mgr\r
+// Removes sound ISR and turns off whatever sound hardware was active\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Shutdown(void)\r
+{\r
+ if (!SD_Started)\r
+ return;\r
+\r
+#if USE_MUSIC\r
+ SD_MusicOff();\r
+#endif\r
+ SDL_ShutDevice();\r
+ SDL_CleanDevice();\r
+\r
+ asm pushf\r
+ asm cli\r
+\r
+ SDL_SetTimer0(0);\r
+\r
+ setvect(8,t0OldService);\r
+\r
+ asm popf\r
+\r
+ SD_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_SetUserHook() - sets the routine that the Sound Mgr calls every 1/70th\r
+// of a second from its timer 0 ISR\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_SetUserHook(void (* hook)(void))\r
+{\r
+ SoundUserHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_PlaySound() - plays the specified sound on the appropriate hardware\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_PlaySound(soundnames sound)\r
+{\r
+ SoundCommon far *s;\r
+\r
+ if ((SoundMode == sdm_Off) || (sound == -1))\r
+ return;\r
+\r
+ s = MK_FP(SoundTable[sound],0);\r
+ if (!s)\r
+ Quit("SD_PlaySound() - Uncached sound");\r
+ if (!s->length)\r
+ Quit("SD_PlaySound() - Zero length sound");\r
+ if (s->priority < SoundPriority)\r
+ return;\r
+\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_PC:\r
+ SDL_PCPlaySound((void far *)s);\r
+ break;\r
+ case sdm_AdLib:\r
+ SDL_ALPlaySound((void far *)s);\r
+ break;\r
+ }\r
+\r
+ SoundNumber = sound;\r
+ SoundPriority = s->priority;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_SoundPlaying() - returns the sound number that's playing, or 0 if\r
+// no sound is playing\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+SD_SoundPlaying(void)\r
+{\r
+ boolean result = false;\r
+\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_PC:\r
+ result = pcSound? true : false;\r
+ break;\r
+ case sdm_AdLib:\r
+ result = alSound? true : false;\r
+ break;\r
+ }\r
+\r
+ if (result)\r
+ return(SoundNumber);\r
+ else\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_StopSound() - if a sound is playing, stops it\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_StopSound(void)\r
+{\r
+ switch (SoundMode)\r
+ {\r
+ case sdm_PC:\r
+ SDL_PCStopSound();\r
+ break;\r
+ case sdm_AdLib:\r
+ SDL_ALStopSound();\r
+ break;\r
+ }\r
+\r
+ SDL_SoundFinished();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_WaitSoundDone() - waits until the current sound is done playing\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_WaitSoundDone(void)\r
+{\r
+ while (SD_SoundPlaying())\r
+ ;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_MusicOn() - turns on the sequencer\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_MusicOn(void)\r
+{\r
+#if USE_MUSIC\r
+ sqActive = true;\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_MusicOff() - turns off the sequencer and any playing notes\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_MusicOff(void)\r
+{\r
+#if USE_MUSIC\r
+ word i;\r
+\r
+\r
+ switch (MusicMode)\r
+ {\r
+ case smm_AdLib:\r
+ alFXReg = 0;\r
+ alOut(alEffects,0);\r
+ for (i = 0;i < sqMaxTracks;i++)\r
+ alOut(alFreqH + i + 1,0);\r
+ break;\r
+ }\r
+ sqActive = false;\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_StartMusic() - starts playing the music pointed to\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_StartMusic(MusicGroup far *music)\r
+{\r
+#if USE_MUSIC\r
+ SD_MusicOff();\r
+asm pushf\r
+asm cli\r
+\r
+ if (MusicMode == smm_AdLib)\r
+ {\r
+ sqHackPtr = sqHack = music->values;\r
+ sqHackSeqLen = sqHackLen = music->length;\r
+ sqHackTime = 0;\r
+ alTimeCount = 0;\r
+ SD_MusicOn();\r
+ }\r
+\r
+asm popf\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_FadeOutMusic() - starts fading out the music. Call SD_MusicPlaying()\r
+// to see if the fadeout is complete\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_FadeOutMusic(void)\r
+{\r
+#if USE_MUSIC\r
+ switch (MusicMode)\r
+ {\r
+ case smm_AdLib:\r
+ // DEBUG - quick hack to turn the music off\r
+ SD_MusicOff();\r
+ break;\r
+ }\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SD_MusicPlaying() - returns true if music is currently playing, false if\r
+// not\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_MusicPlaying(void)\r
+{\r
+#if USE_MUSIC\r
+ boolean result;\r
+\r
+ switch (MusicMode)\r
+ {\r
+ case smm_AdLib:\r
+ result = false;\r
+ // DEBUG - not written\r
+ break;\r
+ default:\r
+ result = false;\r
+ }\r
+\r
+ return(result);\r
+#endif\r
+}\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_SD.h - Sound Manager Header\r
+// v1.0d1\r
+// By Jason Blochowiak\r
+//\r
+\r
+#ifndef __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef __ID_SD__\r
+#define __ID_SD__\r
+\r
+#ifdef __DEBUG__\r
+#define __DEBUG_SoundMgr__\r
+#endif\r
+\r
+#define TickBase 70 // 70Hz per tick - used as a base for timer 0\r
+\r
+typedef enum {\r
+ sdm_Off,\r
+ sdm_PC,sdm_AdLib,\r
+ } SDMode;\r
+typedef enum {\r
+ smm_Off,smm_AdLib\r
+ } SMMode;\r
+\r
+typedef struct\r
+ {\r
+ longword length;\r
+ word priority;\r
+ } SoundCommon;\r
+\r
+// PC Sound stuff\r
+#define pcTimer 0x42\r
+#define pcTAccess 0x43\r
+#define pcSpeaker 0x61\r
+\r
+#define pcSpkBits 3\r
+\r
+typedef struct\r
+ {\r
+ SoundCommon common;\r
+ byte data[1];\r
+ } PCSound;\r
+\r
+// Registers for the Sound Blaster card - needs to be offset by n0\r
+#define sbReset 0x206\r
+#define sbReadData 0x20a\r
+#define sbWriteCmd 0x20c\r
+#define sbWriteData 0x20c\r
+#define sbWriteStat 0x20c\r
+#define sbDataAvail 0x20e\r
+\r
+typedef struct\r
+ {\r
+ SoundCommon common;\r
+ word hertz;\r
+ byte bits,\r
+ reference,\r
+ data[1];\r
+ } SampledSound;\r
+\r
+// Registers for the AdLib card\r
+// Operator stuff\r
+#define alChar 0x20\r
+#define alScale 0x40\r
+#define alAttack 0x60\r
+#define alSus 0x80\r
+#define alWave 0xe0\r
+// Channel stuff\r
+#define alFreqL 0xa0\r
+#define alFreqH 0xb0\r
+#define alFeedCon 0xc0\r
+// Global stuff\r
+#define alEffects 0xbd\r
+\r
+typedef struct\r
+ {\r
+ byte mChar,cChar,\r
+ mScale,cScale,\r
+ mAttack,cAttack,\r
+ mSus,cSus,\r
+ mWave,cWave,\r
+ nConn,\r
+\r
+ // These are only for Muse - these bytes are really unused\r
+ voice,\r
+ mode,\r
+ unused[3];\r
+ } Instrument;\r
+\r
+typedef struct\r
+ {\r
+ SoundCommon common;\r
+ Instrument inst;\r
+ byte block,\r
+ data[1];\r
+ } AdLibSound;\r
+\r
+//\r
+// Sequencing stuff\r
+//\r
+#define sqMaxTracks 10\r
+#define sqMaxMoods 1 // DEBUG\r
+\r
+#define sev_Null 0 // Does nothing\r
+#define sev_NoteOff 1 // Turns a note off\r
+#define sev_NoteOn 2 // Turns a note on\r
+#define sev_NotePitch 3 // Sets the pitch of a currently playing note\r
+#define sev_NewInst 4 // Installs a new instrument\r
+#define sev_NewPerc 5 // Installs a new percussive instrument\r
+#define sev_PercOn 6 // Turns a percussive note on\r
+#define sev_PercOff 7 // Turns a percussive note off\r
+#define sev_SeqEnd -1 // Terminates a sequence\r
+\r
+// Flags for MusicGroup.flags\r
+#define sf_Melodic 0\r
+#define sf_Percussive 1\r
+\r
+#if 1\r
+typedef struct\r
+ {\r
+ word length,\r
+ values[1];\r
+ } MusicGroup;\r
+#else\r
+typedef struct\r
+ {\r
+ word flags,\r
+ count,\r
+ offsets[1];\r
+ } MusicGroup;\r
+#endif\r
+\r
+typedef struct\r
+ {\r
+ /* This part needs to be set up by the user */\r
+ word mood,far *moods[sqMaxMoods];\r
+\r
+ /* The rest is set up by the code */\r
+ Instrument inst;\r
+ boolean percussive;\r
+ word far *seq;\r
+ longword nextevent;\r
+ } ActiveTrack;\r
+\r
+#define sqmode_Normal 0\r
+#define sqmode_FadeIn 1\r
+#define sqmode_FadeOut 2\r
+\r
+#define sqMaxFade 64 // DEBUG\r
+\r
+\r
+// Global variables\r
+extern boolean AdLibPresent,\r
+ NeedsMusic; // For Caching Mgr\r
+extern SDMode SoundMode;\r
+extern SMMode MusicMode;\r
+extern longword TimeCount; // Global time in ticks\r
+extern SDMode oldsoundmode;\r
+\r
+// Function prototypes\r
+extern void SD_Startup(void),\r
+ SD_Shutdown(void),\r
+ SD_Default(boolean gotit,SDMode sd,SMMode sm),\r
+ SD_PlaySound(soundnames sound),\r
+ SD_StopSound(void),\r
+ SD_WaitSoundDone(void),\r
+ SD_StartMusic(MusicGroup far *music),\r
+ SD_MusicOn(void),\r
+ SD_MusicOff(void),\r
+ SD_FadeOutMusic(void),\r
+ SD_SetUserHook(void (*hook)(void));\r
+extern boolean SD_MusicPlaying(void),\r
+ SD_SetSoundMode(SDMode mode),\r
+ SD_SetMusicMode(SMMode mode);\r
+extern word SD_SoundPlaying(void);\r
+\r
+#ifdef _MUSE_ // MUSE Goes directly to the lower level routines\r
+extern void SDL_PCPlaySound(PCSound far *sound),\r
+ SDL_PCStopSound(void),\r
+ SDL_ALPlaySound(AdLibSound far *sound),\r
+ SDL_ALStopSound(void);\r
+#endif\r
+\r
+#endif\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#define S_LOADING "Loading"\r
+#define S_EMPTYSPOT "Empty"\r
+#define S_SVGACOMP "SVGA Compatibility Mode Enabled."\r
+#define S_READYPRESS " Ready - Press a Key "\r
+#define S_NOSFX "NO SOUND EFFECTS"\r
+#define S_PCSPKR "PC SPEAKER"\r
+#define S_ALSB "ADLIB/SOUNDBLASTER"\r
+#define S_QUIET "QUIET ADLIB/SOUNDBLASTER"\r
+#define S_NOMUSIC "NO MUSIC"\r
+#define S_BEGINE "BEGIN EASY GAME"\r
+#define S_BEGINN "BEGIN NORMAL GAME"\r
+#define S_BEGINH "BEGIN HARD GAME"\r
+#define S_UPLEFT "UP & LEFT"\r
+#define S_UP "UP"\r
+#define S_UPRIGHT "UP & RIGHT"\r
+#define S_RIGHT "RIGHT"\r
+#define S_DNRIGHT "DOWN & RIGHT"\r
+#define S_DN "DOWN"\r
+#define S_DNLEFT "DOWN & LEFT"\r
+#define S_LEFT "LEFT"\r
+\r
+#define S_JUMP "JUMP"\r
+#define S_POGO "POGO"\r
+#define S_FIRE "FIRE"\r
+#define S_MOVEMENT "MOVEMENT"\r
+#define S_BUTTONS "BUTTONS"\r
+\r
+#define S_SOUND "SOUND"\r
+#define S_MUSIC "MUSIC"\r
+#define S_OPTIONS "OPTIONS"\r
+#define S_USEKB "USE KEYBOARD"\r
+#define S_USEJOY1 "USE JOYSTICK #1"\r
+#define S_USEJOY2 "USE JOYSTICK #2"\r
+#define S_NEWGAME "NEW GAME"\r
+#define S_LOADGAME "LOAD GAME"\r
+#define S_SAVEGAME "SAVE GAME"\r
+#define S_CONFIG "CONFIGURE"\r
+#define S_ENDGAME "END GAME"\r
+#define S_PADDLEWAR "PADDLE WAR"\r
+#define S_QUIT "QUIT"\r
+\r
+#define S_ESCBACK "ESC TO BACK OUT"\r
+#define S_ESCBACK1 "ESC to back out"\r
+#define S_ESCQUIT "ESC to quit"\r
+#define S_F1HELP "F1 for help"\r
+#define S_REALLYEND "REALLY END CURRENT GAME?"\r
+#define S_PRESSY "PRESS Y TO END IT"\r
+#define S_REALLYQUIT "REALLY QUIT?"\r
+#define S_PRESSYQ "PRESS Y TO QUIT"\r
+#define S_INAGAME "YOU'RE IN A GAME"\r
+#define S_PRESSY2L "PRESS Y TO LOAD GAME"\r
+#define S_PRESSY4N "PRESS Y FOR NEW GAME"\r
+\r
+#define S_USLERROR "Error: "\r
+#define S_USLUNKNOWN "Unknown"\r
+#define S_USLDISKFULL "Disk is Full"\r
+#define S_USLFILEINC "File is Incomplete"\r
+#define S_PRESSKEY "PRESS ANY KEY"\r
+#define S_PRESSKEY1 "Press any key"\r
+\r
+#define S_SBOXON "SCORE BOX (ON)"\r
+#define S_SBOXOFF "SCORE BOX (OFF)"\r
+#define S_SVGAON "SVGA COMPATIBILITY (ON)"\r
+#define S_SVGAOFF "SVGA COMPATIBILITY (OFF)"\r
+#define S_2BON "TWO-BUTTON FIRING (ON)"\r
+#define S_2BOFF "TWO-BUTTON FIRING (OFF)"\r
+\r
+#define S_SBOXNOWON "Score box now on"\r
+#define S_SBOXNOWOFF "Score box now off"\r
+#define S_SVGANOWON "SVGA compatibility now on"\r
+#define S_SVGANOWOFF "SVGA compatibility now off"\r
+#define S_2BNOWON "Two-button firing now on"\r
+#define S_2BNOWOFF "Two-button firing now off"\r
+\r
+#define S_KEYBOARD "KEYBOARD"\r
+#define S_JOY1 "JOYSTICK #1"\r
+#define S_JOY2 "JOYSTICK #2"\r
+#define S_MOUSE "MOUSE"\r
+#define S_CONTROL "CONTROL: "\r
+#define S_KEYUSED "Key already used"\r
+#define S_PB1 "and press button #1"\r
+#define S_PB2 "and press button #2"\r
+#define S_MJUL "Move Joystick to upper left"\r
+#define S_MJLR "Move Joystick to lower right"\r
+\r
+#define S_USINGJ1 "USING "S_JOY1\r
+#define S_USINGJ2 "USING "S_JOY2\r
+#define S_TYPENAME "Type name"\r
+#define S_ENTERACC "Enter accepts"\r
+#define S_UNTITLED "Untitled"\r
+#define S_SAVING "Saving"\r
+#define S_YOULOST "You lost!"\r
+#define S_YOUWON "You won!"\r
+#define S_ARRMOVE "Arrows move"\r
+#define S_ENTERSEL "Enter selects"\r
+\r
+#define S_RETGAME "RETURN TO GAME"\r
+#define S_RETDEMO "RETURN TO DEMO"\r
+#define S_CTRLPANEL "Control Panel"\r
+#define S_QUITTING "Quitting..."\r
+\r
+#define S_WHATNAME "What is the name of this creature?"\r
+#define S_SORRY "Sorry, that's not quite right."\r
+#define S_CHECKMAN "Please check your manual and try again."\r
+\r
+#define S_BADCARD "Improper video card! If you really have an EGA/VGA card that I am not\n"\\r
+ "detecting, use the -HIDDENCARD command line parameter!"\r
+#define S_BADCARD1 "Improper video card! If you really have a CGA card that I am not\n"\\r
+ "detecting, use the -HIDDENCARD command line parameter!"\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_US.c - User Manager\r
+// v1.0d1\r
+// By Jason Blochowiak\r
+//\r
+\r
+//\r
+// This module handles dealing with user input & feedback\r
+//\r
+// Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching,\r
+// and Refresh Mgrs, Memory Mgr for background save/restore\r
+//\r
+// Globals:\r
+// ingame - Flag set by game indicating if a game is in progress\r
+// abortgame - Flag set if the current game should be aborted (if a load\r
+// game fails)\r
+// loadedgame - Flag set if a game was loaded\r
+// abortprogram - Normally nil, this points to a terminal error message\r
+// if the program needs to abort\r
+// restartgame - Normally set to gd_Continue, this is set to one of the\r
+// difficulty levels if a new game should be started\r
+// PrintX, PrintY - Where the User Mgr will print (global coords)\r
+// WindowX,WindowY,WindowW,WindowH - The dimensions of the current\r
+// window\r
+//\r
+\r
+// DEBUG - handle LPT3 for Sound Source\r
+\r
+#include "ID_HEADS.H"\r
+\r
+#define CTL_M_ADLIBUPPIC CTL_S_ADLIBUPPIC\r
+#define CTL_M_ADLIBDNPIC CTL_S_ADLIBDNPIC\r
+\r
+#pragma hdrstop\r
+\r
+#pragma warn -pia\r
+\r
+#define MaxX 320\r
+#define MaxY 200\r
+\r
+#define MaxHelpLines 500\r
+\r
+#define MaxHighName 57\r
+#define MaxScores 10\r
+typedef struct\r
+ {\r
+ char name[MaxHighName + 1];\r
+ long score;\r
+ word completed;\r
+ } HighScore;\r
+\r
+#define MaxGameName 32\r
+#define MaxSaveGames 7\r
+typedef struct\r
+ {\r
+ char signature[4];\r
+ boolean present;\r
+ char name[MaxGameName + 1];\r
+ } SaveGame;\r
+\r
+// Hack import for TED launch support\r
+extern boolean tedlevel;\r
+extern word tedlevelnum;\r
+extern void TEDDeath(void);\r
+static char *ParmStrings[] = {"TEDLEVEL","NOWAIT",""};\r
+\r
+\r
+// Global variables\r
+ boolean ingame,abortgame,loadedgame;\r
+ char *abortprogram;\r
+ GameDiff restartgame = gd_Continue;\r
+ word PrintX,PrintY;\r
+ word WindowX,WindowY,WindowW,WindowH;\r
+\r
+// Internal variables\r
+static boolean US_Started;\r
+static boolean GameIsDirty,\r
+ HighScoresDirty,\r
+ QuitToDos,\r
+ ResumeGame,\r
+ NoWait;\r
+\r
+static memptr LineOffsets;\r
+\r
+static boolean Button0,Button1,\r
+ CursorBad;\r
+static int CursorX,CursorY;\r
+\r
+static void (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString,\r
+ (*USL_DrawString)(char far *) = VWB_DrawPropString;\r
+\r
+static boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int);\r
+static void (*USL_ResetGame)(void);\r
+static SaveGame Games[MaxSaveGames];\r
+static HighScore Scores[MaxScores] =\r
+ {\r
+ {"Sir Lancelot",500},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0}\r
+ };\r
+\r
+// Internal routines\r
+\r
+// Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed\r
+// from DOS.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma warn -par\r
+#pragma warn -rch\r
+int\r
+USL_HardError(word errval,int ax,int bp,int si)\r
+{\r
+#define IGNORE 0\r
+#define RETRY 1\r
+#define ABORT 2\r
+extern void ShutdownId(void);\r
+\r
+static char buf[32];\r
+static WindowRec wr;\r
+static boolean oldleavedriveon;\r
+ int di;\r
+ char c,*s,*t;\r
+\r
+\r
+ di = _DI;\r
+\r
+ oldleavedriveon = LeaveDriveOn;\r
+ LeaveDriveOn = false;\r
+\r
+ if (ax < 0)\r
+ s = "Device Error";\r
+ else\r
+ {\r
+ if ((di & 0x00ff) == 0)\r
+ s = "Drive ~ is Write Protected";\r
+ else\r
+ s = "Error on Drive ~";\r
+ for (t = buf;*s;s++,t++) // Can't use sprintf()\r
+ if ((*t = *s) == '~')\r
+ *t = (ax & 0x00ff) + 'A';\r
+ *t = '\0';\r
+ s = buf;\r
+ }\r
+\r
+ c = peekb(0x40,0x49); // Get the current screen mode\r
+ if ((c < 4) || (c == 7))\r
+ goto oh_kill_me;\r
+\r
+ // DEBUG - handle screen cleanup\r
+\r
+ US_SaveWindow(&wr);\r
+ US_CenterWindow(30,3);\r
+ US_CPrint(s);\r
+ US_CPrint("(R)etry or (A)bort?");\r
+ VW_UpdateScreen();\r
+ IN_ClearKeysDown();\r
+\r
+asm sti // Let the keyboard interrupts come through\r
+\r
+ while (true)\r
+ {\r
+ switch (IN_WaitForASCII())\r
+ {\r
+ case key_Escape:\r
+ case 'a':\r
+ case 'A':\r
+ goto oh_kill_me;\r
+ break;\r
+ case key_Return:\r
+ case key_Space:\r
+ case 'r':\r
+ case 'R':\r
+ US_ClearWindow();\r
+ VW_UpdateScreen();\r
+ US_RestoreWindow(&wr);\r
+ LeaveDriveOn = oldleavedriveon;\r
+ return(RETRY);\r
+ break;\r
+ }\r
+ }\r
+\r
+oh_kill_me:\r
+ abortprogram = s;\r
+ ShutdownId();\r
+ fprintf(stderr,"Terminal Error: %s\n",s);\r
+ if (tedlevel)\r
+ fprintf(stderr,"You launched from TED. I suggest that you reboot...\n");\r
+\r
+ return(ABORT);\r
+#undef IGNORE\r
+#undef RETRY\r
+#undef ABORT\r
+}\r
+#pragma warn +par\r
+#pragma warn +rch\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_GiveSaveName() - Returns a pointer to a static buffer that contains\r
+// the filename to use for the specified save game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static char *\r
+USL_GiveSaveName(word game)\r
+{\r
+static char filename[32];\r
+ char *s,*t;\r
+\r
+ for (s = "SAVEGM",t = filename;*s;)\r
+ *t++ = *s++;\r
+ *t++ = game + '0';\r
+ for (s = "."EXTENSION;*s;)\r
+ *t++ = *s++;\r
+ *t = '\0';\r
+\r
+ return(filename);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after\r
+// reading or writing the save game headers\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void))\r
+{\r
+ USL_LoadGame = load;\r
+ USL_SaveGame = save;\r
+ USL_ResetGame = reset;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ReadConfig() - Reads the configuration file, if present, and sets\r
+// things up accordingly. If it's not present, uses defaults. This file\r
+// includes the high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ReadConfig(void)\r
+{\r
+ boolean gotit;\r
+ int file;\r
+ SDMode sd;\r
+ SMMode sm;\r
+ ControlType ctl;\r
+\r
+ if ((file = open("CONFIG."EXTENSION,O_BINARY | O_RDONLY)) != -1)\r
+ {\r
+ read(file,Scores,sizeof(HighScore) * MaxScores);\r
+ read(file,&sd,sizeof(sd));\r
+ read(file,&sm,sizeof(sm));\r
+ read(file,&ctl,sizeof(ctl));\r
+ read(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+ close(file);\r
+\r
+ HighScoresDirty = false;\r
+ gotit = true;\r
+ }\r
+ else\r
+ {\r
+ sd = sdm_Off;\r
+ sm = smm_Off;\r
+ ctl = ctrl_Keyboard;\r
+\r
+ gotit = false;\r
+ HighScoresDirty = true;\r
+ }\r
+\r
+ SD_Default(gotit,sd,sm);\r
+ IN_Default(gotit,ctl);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_WriteConfig() - Writes out the current configuration, including the\r
+// high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_WriteConfig(void)\r
+{\r
+ int file;\r
+\r
+ file = open("CONFIG."EXTENSION,O_CREAT | O_BINARY | O_WRONLY,\r
+ S_IREAD | S_IWRITE | S_IFREG);\r
+ if (file != -1)\r
+ {\r
+ write(file,Scores,sizeof(HighScore) * MaxScores);\r
+ write(file,&SoundMode,sizeof(SoundMode));\r
+ write(file,&MusicMode,sizeof(MusicMode));\r
+ write(file,&(Controls[0]),sizeof(Controls[0]));\r
+ write(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+ close(file);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CheckSavedGames() - Checks to see which saved games are present\r
+// & valid\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_CheckSavedGames(void)\r
+{\r
+ boolean ok;\r
+ char *filename;\r
+ word i;\r
+ int file;\r
+ SaveGame *game;\r
+\r
+ USL_SaveGame = 0;\r
+ USL_LoadGame = 0;\r
+\r
+ for (i = 0,game = Games;i < MaxSaveGames;i++,game++)\r
+ {\r
+ filename = USL_GiveSaveName(i);\r
+ ok = false;\r
+ if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+ {\r
+ if\r
+ (\r
+ (read(file,game,sizeof(*game)) == sizeof(*game))\r
+ && (!strcmp(game->signature,EXTENSION))\r
+ )\r
+ ok = true;\r
+\r
+ close(file);\r
+ }\r
+\r
+ if (ok)\r
+ game->present = true;\r
+ else\r
+ {\r
+ strcpy(game->signature,EXTENSION);\r
+ game->present = false;\r
+ strcpy(game->name,"Empty");\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Startup() - Starts the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Startup(void)\r
+{\r
+ if (US_Started)\r
+ return;\r
+\r
+ harderr(USL_HardError); // Install the fatal error handler\r
+\r
+ US_InitRndT(true); // Initialize the random number generator\r
+\r
+ USL_ReadConfig(); // Read config file\r
+\r
+ US_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Setup() - Does the disk access part of the User Mgr's startup\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Setup(void)\r
+{\r
+ USL_CheckSavedGames(); // Check which saved games are present\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Shutdown() - Shuts down the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Shutdown(void)\r
+{\r
+ if (!US_Started)\r
+ return;\r
+\r
+ if (!abortprogram)\r
+ USL_WriteConfig();\r
+\r
+ US_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CheckParm() - checks to see if a string matches one of a set of\r
+// strings. The check is case insensitive. The routine returns the\r
+// index of the string that matched, or -1 if no matches were found\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+int\r
+US_CheckParm(char *parm,char **strings)\r
+{\r
+ char cp,cs,\r
+ *p,*s;\r
+ int i;\r
+\r
+ while (!isalpha(*parm)) // Skip non-alphas\r
+ parm++;\r
+\r
+ for (i = 0;*strings && **strings;i++)\r
+ {\r
+ for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)\r
+ {\r
+ cs = *s++;\r
+ if (!cs)\r
+ return(i);\r
+ cp = *p++;\r
+\r
+ if (isupper(cs))\r
+ cs = tolower(cs);\r
+ if (isupper(cp))\r
+ cp = tolower(cp);\r
+ }\r
+ }\r
+ return(-1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ScreenDraw() - Draws a chunk of the text screen (called only by\r
+// US_TextScreen())\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ScreenDraw(word x,word y,char *s,byte attr)\r
+{\r
+ byte far *screen;\r
+\r
+ screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2));\r
+ while (*s)\r
+ {\r
+ *screen++ = *s++;\r
+ *screen++ = attr;\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it,\r
+// and moves the cursor to the leftmost column of the bottom line\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ClearTextScreen(void)\r
+{\r
+ // Set to 80x25 color text mode\r
+ _AL = 3; // Mode 3\r
+ _AH = 0x00;\r
+ geninterrupt(0x10);\r
+\r
+ // Use BIOS to move the cursor to the bottom of the screen\r
+ _AH = 0x0f;\r
+ geninterrupt(0x10); // Get current video mode into _BH\r
+ _DL = 0; // Lefthand side of the screen\r
+ _DH = 24; // Bottom row\r
+ _AH = 0x02;\r
+ geninterrupt(0x10);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_TextScreen() - Puts up the startup text screen\r
+// Note: These are the only User Manager functions that can be safely called\r
+// before the User Mgr has been started up\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_TextScreen(void)\r
+{\r
+ word i,n,\r
+ sx,sy;\r
+extern char far introscn;\r
+\r
+ USL_ClearTextScreen();\r
+\r
+ _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2);\r
+\r
+ // Check for TED launching here\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ n = US_CheckParm(_argv[i],ParmStrings);\r
+ if (n == 0)\r
+ {\r
+ tedlevelnum = atoi(_argv[i + 1]);\r
+ if (tedlevelnum >= 0)\r
+ {\r
+ tedlevel = true;\r
+ return;\r
+ }\r
+ else\r
+ break;\r
+ }\r
+ else if (n == 1)\r
+ {\r
+ NoWait = true;\r
+ return;\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_Show() - Changes the appearance of one of the fields on the text\r
+// screen. Possibly adds a checkmark in front of it and highlights it\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_Show(word x,word y,word w,boolean show,boolean hilight)\r
+{\r
+ byte far *screen;\r
+\r
+ screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2));\r
+ *screen++ = show? 251 : ' '; // Checkmark char or space\r
+ *screen = 0x48;\r
+ if (show && hilight)\r
+ {\r
+ for (w++;w--;screen += 2)\r
+ *screen = 0x4f;\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ShowMem() - Right justifies a longword in one of the memory fields on\r
+// the text screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ShowMem(word x,word y,long mem)\r
+{\r
+ char buf[16];\r
+ word i;\r
+\r
+ for (i = strlen(ltoa(mem,buf,10));i < 5;i++)\r
+ USL_ScreenDraw(x++,y," ",0x48);\r
+ USL_ScreenDraw(x,y,buf,0x48);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_UpdateTextScreen() - Called after the ID libraries are started up.\r
+// Displays what hardware is present.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_UpdateTextScreen(void)\r
+{\r
+ boolean b;\r
+ byte far *screen;\r
+ word i;\r
+ longword totalmem;\r
+\r
+ // Show video card info\r
+ b = (grmode == CGAGR);\r
+ USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b);\r
+ b = (grmode == EGAGR);\r
+ USL_Show(21,8,4,(videocard >= EGAcard) && (videocard <= VGAcard),b);\r
+ b = (grmode == VGAGR);\r
+ USL_Show(21,9,4,videocard == VGAcard,b);\r
+ if (compatability)\r
+ USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f);\r
+\r
+ // Show input device info\r
+ USL_Show(60,7,8,true,true);\r
+ USL_Show(60,8,11,JoysPresent[0],true);\r
+ USL_Show(60,9,11,JoysPresent[1],true);\r
+ USL_Show(60,10,5,MousePresent,true);\r
+\r
+ // Show sound hardware info\r
+ USL_Show(21,14,11,true,SoundMode == sdm_PC);\r
+ b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib);\r
+ USL_Show(21,15,5,AdLibPresent && !SoundBlasterPresent,\r
+ b && !SoundBlasterPresent);\r
+ USL_Show(21,16,13,SoundBlasterPresent,\r
+ SoundBlasterPresent && (b || (SoundMode == sdm_SoundBlaster)));\r
+ USL_Show(21,17,13,SoundSourcePresent,SoundMode == sdm_SoundSource);\r
+\r
+ // Show memory available/used\r
+ USL_ShowMem(63,15,mminfo.mainmem / 1024);\r
+ USL_Show(53,15,23,true,true);\r
+ USL_ShowMem(63,16,mminfo.EMSmem / 1024);\r
+ USL_Show(53,16,23,mminfo.EMSmem? true : false,true);\r
+ USL_ShowMem(63,17,mminfo.XMSmem / 1024);\r
+ USL_Show(53,17,23,mminfo.XMSmem? true : false,true);\r
+ totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem;\r
+ USL_ShowMem(63,18,totalmem / 1024);\r
+ screen = MK_FP(0xb800,1 + (((63 - 1) * 2) + (18 * 80 * 2)));\r
+ for (i = 0;i < 13;i++,screen += 2)\r
+ *screen = 0x4f;\r
+\r
+ // Change Initializing... to Loading...\r
+ USL_ScreenDraw(27,22," Loading... ",0x9c);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_FinishTextScreen() - After the main program has finished its initial\r
+// loading, this routine waits for a keypress and then clears the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_FinishTextScreen(void)\r
+{\r
+ // Change Loading... to Press a Key\r
+ USL_ScreenDraw(29,22," Ready - Press a Key ",0x9a);\r
+\r
+ if (!(tedlevel || NoWait))\r
+ {\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+ }\r
+ IN_ClearKeysDown();\r
+\r
+ USL_ClearTextScreen();\r
+}\r
+\r
+// Window/Printing routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_SetPrintRoutines() - Sets the routines used to measure and print\r
+// from within the User Mgr. Primarily provided to allow switching\r
+// between masked and non-masked fonts\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *))\r
+{\r
+ USL_MeasureString = measure;\r
+ USL_DrawString = print;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Print() - Prints a string in the current window. Newlines are\r
+// supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Print(char *s)\r
+{\r
+ char c,*se;\r
+ word w,h;\r
+\r
+ while (*s)\r
+ {\r
+ se = s;\r
+ while ((c = *se) && (c != '\n'))\r
+ se++;\r
+ *se = '\0';\r
+\r
+ USL_MeasureString(s,&w,&h);\r
+ px = PrintX;\r
+ py = PrintY;\r
+ USL_DrawString(s);\r
+\r
+ s = se;\r
+ if (c)\r
+ {\r
+ *se = c;\r
+ s++;\r
+\r
+ PrintX = WindowX;\r
+ PrintY += h;\r
+ }\r
+ else\r
+ PrintX += w;\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_PrintUnsigned() - Prints an unsigned long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintUnsigned(longword n)\r
+{\r
+ char buffer[32];\r
+\r
+ US_Print(ultoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_PrintSigned() - Prints a signed long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintSigned(long n)\r
+{\r
+ char buffer[32];\r
+\r
+ US_Print(ltoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_PrintInCenter() - Prints a string in the center of the given rect\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_PrintInCenter(char *s,Rect r)\r
+{\r
+ word w,h,\r
+ rw,rh;\r
+\r
+ USL_MeasureString(s,&w,&h);\r
+ rw = r.lr.x - r.ul.x;\r
+ rh = r.lr.y - r.ul.y;\r
+\r
+ px = r.ul.x + ((rw - w) / 2);\r
+ py = r.ul.y + ((rh - h) / 2);\r
+ USL_DrawString(s);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_PrintCentered() - Prints a string centered in the current window.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintCentered(char *s)\r
+{\r
+ Rect r;\r
+\r
+ r.ul.x = WindowX;\r
+ r.ul.y = WindowY;\r
+ r.lr.x = r.ul.x + WindowW;\r
+ r.lr.y = r.ul.y + WindowH;\r
+\r
+ USL_PrintInCenter(s,r);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CPrintLine() - Prints a string centered on the current line and\r
+// advances to the next line. Newlines are not supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrintLine(char *s)\r
+{\r
+ word w,h;\r
+\r
+ USL_MeasureString(s,&w,&h);\r
+\r
+ if (w > WindowW)\r
+ Quit("US_CPrintLine() - String exceeds width");\r
+ px = WindowX + ((WindowW - w) / 2);\r
+ py = PrintY;\r
+ USL_DrawString(s);\r
+ PrintY += h;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CPrint() - Prints a string in the current window. Newlines are\r
+// supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrint(char *s)\r
+{\r
+ char c,*se;\r
+ word w,h;\r
+\r
+ while (*s)\r
+ {\r
+ se = s;\r
+ while ((c = *se) && (c != '\n'))\r
+ se++;\r
+ *se = '\0';\r
+\r
+ US_CPrintLine(s);\r
+\r
+ s = se;\r
+ if (c)\r
+ {\r
+ *se = c;\r
+ s++;\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_ClearWindow() - Clears the current window to white and homes the\r
+// cursor\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ClearWindow(void)\r
+{\r
+ VWB_Bar(WindowX,WindowY,WindowW,WindowH,WHITE);\r
+ PrintX = WindowX;\r
+ PrintY = WindowY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_DrawWindow() - Draws a frame and sets the current window parms\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_DrawWindow(word x,word y,word w,word h)\r
+{\r
+ word i,\r
+ sx,sy,sw,sh;\r
+\r
+ WindowX = x * 8;\r
+ WindowY = y * 8;\r
+ WindowW = w * 8;\r
+ WindowH = h * 8;\r
+\r
+ PrintX = WindowX;\r
+ PrintY = WindowY;\r
+\r
+ sx = (x - 1) * 8;\r
+ sy = (y - 1) * 8;\r
+ sw = (w + 1) * 8;\r
+ sh = (h + 1) * 8;\r
+\r
+ US_ClearWindow();\r
+\r
+ VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6);\r
+ for (i = sx + 8;i <= sx + sw - 8;i += 8)\r
+ VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7);\r
+ VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8);\r
+\r
+ for (i = sy + 8;i <= sy + sh - 8;i += 8)\r
+ VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CenterWindow() - Generates a window of a given width & height in the\r
+// middle of the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterWindow(word w,word h)\r
+{\r
+ US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CenterSaveWindow() - Generates a window of a given width & height in\r
+// the middle of the screen, saving the background\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterSaveWindow(word w,word h,memptr *save)\r
+{\r
+ word x,y,\r
+ screen;\r
+\r
+ x = ((MaxX / 8) - w) / 2;\r
+ y = ((MaxY / 8) - h) / 2;\r
+ MM_GetPtr(save,(w * h) * CHARWIDTH);\r
+ screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH);\r
+ VW_ScreenToMem(screen,*save,w * CHARWIDTH,h);\r
+ US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_RestoreSaveWindow() - Restores the background of the size of the\r
+// current window from the memory specified by save\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreSaveWindow(memptr *save)\r
+{\r
+ word screen;\r
+\r
+ screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH);\r
+ VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH);\r
+ MM_FreePtr(save);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_SaveWindow() - Saves the current window parms into a record for\r
+// later restoration\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SaveWindow(WindowRec *win)\r
+{\r
+ win->x = WindowX;\r
+ win->y = WindowY;\r
+ win->w = WindowW;\r
+ win->h = WindowH;\r
+\r
+ win->px = PrintX;\r
+ win->py = PrintY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_RestoreWindow() - Sets the current window parms to those held in the\r
+// record\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreWindow(WindowRec *win)\r
+{\r
+ WindowX = win->x;\r
+ WindowY = win->y;\r
+ WindowW = win->w;\r
+ WindowH = win->h;\r
+\r
+ PrintX = win->px;\r
+ PrintY = win->py;\r
+}\r
+\r
+// Cursor routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_StartCursor() - Sets up the cursor for User Mgr use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_StartCursor(void)\r
+{\r
+ CursorInfo info;\r
+\r
+ VW_SetCursor(CURSORARROWSPR);\r
+ CursorX = MaxX / 2;\r
+ CursorY = MaxY / 2;\r
+ VW_MoveCursor(CursorX,CursorY);\r
+ VW_ShowCursor();\r
+\r
+ IN_ReadCursor(&info); // Dispose of any accumulated movement\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_ShutCursor() - Cleans up after US_StartCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ShutCursor(void)\r
+{\r
+ VW_HideCursor();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_UpdateCursor() - Gets the new cursor position & button states from\r
+// the Input Mgr and tells the View Mgr where the cursor is\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_UpdateCursor(void)\r
+{\r
+ CursorInfo info;\r
+\r
+ IN_ReadCursor(&info);\r
+ if (info.x || info.y || CursorBad)\r
+ {\r
+ CursorX += info.x;\r
+ if (CursorX >= MaxX)\r
+ CursorX = MaxX - 1;\r
+ else if (CursorX < 0)\r
+ CursorX = 0;\r
+\r
+ CursorY += info.y;\r
+ if (CursorY >= MaxY)\r
+ CursorY = MaxY - 1;\r
+ else if (CursorY < 0)\r
+ CursorY = 0;\r
+\r
+ VW_MoveCursor(CursorX,CursorY);\r
+ CursorBad = false;\r
+ }\r
+ Button0 = info.button0;\r
+ Button1 = info.button1;\r
+ return(Button0 || Button1);\r
+}\r
+\r
+// Input routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_XORICursor(int x,int y,char *s,word cursor)\r
+{\r
+ char buf[MaxString];\r
+ word w,h;\r
+\r
+ strcpy(buf,s);\r
+ buf[cursor] = '\0';\r
+ USL_MeasureString(buf,&w,&h);\r
+\r
+ px = x + w - 1;\r
+ py = y;\r
+ USL_DrawString("\x80");\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_LineInput() - Gets a line of user input at (x,y), the string defaults\r
+// to whatever is pointed at by def. Input is restricted to maxchars\r
+// chars or maxwidth pixels wide. If the user hits escape (and escok is\r
+// true), nothing is copied into buf, and false is returned. If the\r
+// user hits return, the current string is copied into buf, and true is\r
+// returned\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_LineInput(int x,int y,char *buf,char *def,boolean escok,\r
+ int maxchars,int maxwidth)\r
+{\r
+ boolean redraw,\r
+ cursorvis,cursormoved,\r
+ done,result;\r
+ ScanCode sc;\r
+ char c,\r
+ s[MaxString],olds[MaxString];\r
+ word i,\r
+ cursor,\r
+ w,h,\r
+ len;\r
+ longword lasttime;\r
+\r
+ VW_HideCursor();\r
+\r
+ if (def)\r
+ strcpy(s,def);\r
+ else\r
+ *s = '\0';\r
+ *olds = '\0';\r
+ cursor = strlen(s);\r
+ cursormoved = redraw = true;\r
+\r
+ cursorvis = done = false;\r
+ lasttime = TimeCount;\r
+ LastASCII = key_None;\r
+ LastScan = sc_None;\r
+\r
+ while (!done)\r
+ {\r
+ if (cursorvis)\r
+ USL_XORICursor(x,y,s,cursor);\r
+\r
+ asm pushf\r
+ asm cli\r
+\r
+ sc = LastScan;\r
+ LastScan = sc_None;\r
+ c = LastASCII;\r
+ LastASCII = key_None;\r
+\r
+ asm popf\r
+\r
+ switch (sc)\r
+ {\r
+ case sc_LeftArrow:\r
+ if (cursor)\r
+ cursor--;\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+ case sc_RightArrow:\r
+ if (s[cursor])\r
+ cursor++;\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+ case sc_Home:\r
+ cursor = 0;\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+ case sc_End:\r
+ cursor = strlen(s);\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+\r
+ case sc_Return:\r
+ strcpy(buf,s);\r
+ done = true;\r
+ result = true;\r
+ c = key_None;\r
+ break;\r
+ case sc_Escape:\r
+ if (escok)\r
+ {\r
+ done = true;\r
+ result = false;\r
+ }\r
+ c = key_None;\r
+ break;\r
+\r
+ case sc_BackSpace:\r
+ if (cursor)\r
+ {\r
+ strcpy(s + cursor - 1,s + cursor);\r
+ cursor--;\r
+ redraw = true;\r
+ }\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+ case sc_Delete:\r
+ if (s[cursor])\r
+ {\r
+ strcpy(s + cursor,s + cursor + 1);\r
+ redraw = true;\r
+ }\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+\r
+ case 0x4c: // Keypad 5\r
+ case sc_UpArrow:\r
+ case sc_DownArrow:\r
+ case sc_PgUp:\r
+ case sc_PgDn:\r
+ case sc_Insert:\r
+ c = key_None;\r
+ break;\r
+ }\r
+\r
+ if (c)\r
+ {\r
+ len = strlen(s);\r
+ USL_MeasureString(s,&w,&h);\r
+\r
+ if\r
+ (\r
+ isprint(c)\r
+ && (len < MaxString - 1)\r
+ && ((!maxchars) || (len < maxchars))\r
+ && ((!maxwidth) || (w < maxwidth))\r
+ )\r
+ {\r
+ for (i = len + 1;i > cursor;i--)\r
+ s[i] = s[i - 1];\r
+ s[cursor++] = c;\r
+ redraw = true;\r
+ }\r
+ }\r
+\r
+ if (redraw)\r
+ {\r
+ px = x;\r
+ py = y;\r
+ USL_DrawString(olds);\r
+ strcpy(olds,s);\r
+\r
+ px = x;\r
+ py = y;\r
+ USL_DrawString(s);\r
+\r
+ redraw = false;\r
+ }\r
+\r
+ if (cursormoved)\r
+ {\r
+ cursorvis = false;\r
+ lasttime = TimeCount - TickBase;\r
+\r
+ cursormoved = false;\r
+ }\r
+ if (TimeCount - lasttime > TickBase / 2)\r
+ {\r
+ lasttime = TimeCount;\r
+\r
+ cursorvis ^= true;\r
+ }\r
+ if (cursorvis)\r
+ USL_XORICursor(x,y,s,cursor);\r
+\r
+ VW_UpdateScreen();\r
+ }\r
+\r
+ if (cursorvis)\r
+ USL_XORICursor(x,y,s,cursor);\r
+ if (!result)\r
+ {\r
+ px = x;\r
+ py = y;\r
+ USL_DrawString(olds);\r
+ }\r
+ VW_ShowCursor();\r
+ VW_UpdateScreen();\r
+\r
+ IN_ClearKeysDown();\r
+ return(result);\r
+}\r
+\r
+// Control panel routines\r
+\r
+static boolean FlushHelp;\r
+static WindowRec HelpWindow,BottomWindow;\r
+typedef enum\r
+ {\r
+ uic_Draw,uic_Hit\r
+ } UserCall;\r
+typedef enum\r
+ {\r
+ uii_Bad,uii_Button,uii_RadioButton,uii_CheckBox,uii_KeyCap\r
+ } UIType;\r
+#define ui_Normal 0\r
+#define ui_Selected 1\r
+#define ui_Disabled 2\r
+\r
+ // Prototype the custom routines\r
+static boolean USL_CtlButtonCustom(UserCall,word,word),\r
+ USL_CtlPButtonCustom(UserCall,word,word),\r
+ USL_CtlPSButtonCustom(UserCall,word,word),\r
+ USL_CtlPRButtonCustom(UserCall,word,word),\r
+ USL_CtlHButtonCustom(UserCall,word,word),\r
+ USL_CtlDButtonCustom(UserCall,word,word),\r
+ USL_CtlDEButtonCustom(UserCall,word,word),\r
+ USL_CtlDLButtonCustom(UserCall,word,word),\r
+ USL_CtlDSButtonCustom(UserCall,word,word),\r
+ USL_CtlSButtonCustom(UserCall,word,word),\r
+ USL_CtlCButtonCustom(UserCall,word,word),\r
+ USL_CtlCKbdButtonCustom(UserCall,word,word),\r
+ USL_CtlCJoyButtonCustom(UserCall,word,word);\r
+\r
+ // The structure of a user interaction item\r
+typedef struct {\r
+ Rect r; // The enclosing rectangle\r
+ UIType type; // The type of item\r
+ int picup,picdown; // What to draw when up/down\r
+ char *help; // Floating help string\r
+ ScanCode key; // Key equiv\r
+ word sel; // Interaction flags (ui_XXX)\r
+ boolean (*custom)(UserCall,word,word); // Custom routine\r
+ char *text; // Text for some items\r
+ } UserItem;\r
+typedef struct {\r
+ ScanCode key;\r
+ word i,n, // Hit CtlPanels2[i][n]\r
+ toi,ton; // Move to CtlPanels2[toi][ton]\r
+ } HotKey; // MARK\r
+\r
+static ScanCode *KeyMaps[] =\r
+ {\r
+ &KbdDefs[0].button0,&KbdDefs[0].button1,\r
+ &KbdDefs[0].upleft,&KbdDefs[0].up,&KbdDefs[0].upright,\r
+ &KbdDefs[0].left, &KbdDefs[0].right,\r
+ &KbdDefs[0].downleft,&KbdDefs[0].down,&KbdDefs[0].downright,\r
+ };\r
+\r
+// Some macros to make rectangle definition quite a bit less unpleasant\r
+#define CtlPanelX 8\r
+#define CtlPanelY 4\r
+#define CtlPanel2X (8*8)\r
+#define CtlPanel2Y (2*8)\r
+#define CtlPanel3X (8*8)\r
+#define CtlPanel3Y (7*8)\r
+\r
+#define CtlPanelR(n) { CtlPanelX,CtlPanelY+(32 * (n)),\\r
+ CtlPanelX+40,CtlPanelY+(32 * (n)) + 32}\r
+#define CtlPanel2R(x,y) { CtlPanel2X+(x)*8,CtlPanel2Y+(y)*8,\\r
+ CtlPanel2X+32+(x)*8,CtlPanel2Y+24+(y)*8}\r
+#define CtlPanel3R(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\\r
+ CtlPanel3X+32+(x)*8,CtlPanel3Y+24+(y)*8}\r
+static UserItem CtlPanels[] =\r
+ {\r
+{CtlPanelR(0),uii_RadioButton,CTL_STARTUPPIC,CTL_STARTDNPIC,"Start or Resume a Game",sc_None,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(1),uii_RadioButton,CTL_HELPUPPIC,CTL_HELPDNPIC,"Get Help With Commander Keen",sc_None,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(2),uii_RadioButton,CTL_DISKUPPIC,CTL_DISKDNPIC,"Load / Save / Quit",sc_None,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(3),uii_RadioButton,CTL_CONTROLSUPPIC,CTL_CONTROLSDNPIC,"Choose Controls",sc_C,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(4),uii_RadioButton,CTL_SOUNDUPPIC,CTL_SOUNDDNPIC,"Select Sound Device",sc_F2,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(5),uii_RadioButton,CTL_MUSICUPPIC,CTL_MUSICDNPIC,"Turn Music On / Off",sc_F7,ui_Normal,USL_CtlButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlPPanels[] =\r
+ {\r
+{CtlPanel2R(10,0),uii_RadioButton,CTL_P_NEWGAMEUPPIC,CTL_P_NEWGAMEDNPIC,"Choose Difficulty for the New Game",sc_F5,ui_Normal,USL_CtlPButtonCustom},\r
+{CtlPanel2R(15,0),uii_RadioButton,CTL_P_RESUMEUPPIC,CTL_P_RESUMEDNPIC,"Go Back to Current Game",sc_None,ui_Normal,USL_CtlPButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlPSPanels[] =\r
+ {\r
+{CtlPanel3R(13,5),uii_Button,CTL_P_MEDUPPIC,CTL_P_MEDDNPIC,"Start New Game in Normal Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom},\r
+{CtlPanel3R(8,5),uii_Button,CTL_P_EASYUPPIC,CTL_P_EASYDNPIC,"Start New Game in Easy Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom},\r
+{CtlPanel3R(18,5),uii_Button,CTL_P_HARDUPPIC,CTL_P_HARDDNPIC,"Start New Game in Hard Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlPRPanels[] =\r
+ {\r
+{CtlPanel3R(13,5),uii_Button,CTL_P_GORESUMEUPPIC,CTL_P_GORESUMEDNPIC,"Resume Current Game",sc_None,ui_Normal,USL_CtlPRButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlHPanels[] =\r
+ {\r
+{CtlPanel2R(8,0),uii_Button,CTL_H_LOSTUPPIC,CTL_H_LOSTDNPIC,"Help Me, I'm Lost!",sc_F1,ui_Normal,USL_CtlHButtonCustom},\r
+{CtlPanel2R(13,0),uii_Button,CTL_H_CTRLUPPIC,CTL_H_CTRLDNPIC,"Get Help with Controls",sc_None,ui_Normal,USL_CtlHButtonCustom},\r
+{CtlPanel2R(18,0),uii_Button,CTL_H_STORYUPPIC,CTL_H_STORYDNPIC,"Read Story & Game Tips",sc_None,ui_Normal,USL_CtlHButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlDPanels[] =\r
+ {\r
+{CtlPanel2R(9,0),uii_RadioButton,CTL_D_LSGAMEUPPIC,CTL_D_LSGAMEDNPIC,"Load or Save a Game",sc_F6,ui_Normal,USL_CtlDButtonCustom},\r
+{CtlPanel2R(15,0),uii_RadioButton,CTL_D_DOSUPPIC,CTL_D_DOSDNPIC,"Exit to DOS",sc_Q,ui_Normal,USL_CtlDButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlDLSPanels[] =\r
+ {\r
+#define CtlPanel3LSR(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\\r
+ CtlPanel3X+32+(x)*8,CtlPanel3Y+16+(y)*8}\r
+{CtlPanel3LSR(1,0),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,0),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,2),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,2),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,4),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,4),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,6),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,6),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,8),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,8),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,10),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,10),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,12),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,12),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlDEPanels[] =\r
+ {\r
+#define CtlPanel3ER(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\\r
+ CtlPanel3X+40+(x)*8,CtlPanel3Y+24+(y)*8}\r
+{CtlPanel3ER(12,5),uii_Button,CTL_D_EXITUPPIC,CTL_D_EXITDNPIC,"Really Exit to DOS",sc_None,ui_Normal,USL_CtlDEButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlCPanels[] =\r
+ {\r
+{CtlPanel2R(8,0),uii_RadioButton,CTL_C_KBDUPPIC,CTL_C_KBDDNPIC,"Use / Configure Keyboard",sc_F3,ui_Normal,USL_CtlCButtonCustom},\r
+{CtlPanel2R(13,0),uii_RadioButton,CTL_C_JOY1UPPIC,CTL_C_JOY1DNPIC,"Use / Configure Joystick 1",sc_None,ui_Normal,USL_CtlCButtonCustom},\r
+{CtlPanel2R(18,0),uii_RadioButton,CTL_C_JOY2UPPIC,CTL_C_JOY2DNPIC,"Use / Configure Joystick 2",sc_None,ui_Normal,USL_CtlCButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+#define CtlPanelKC3R(x,y) { CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\\r
+ CtlPanel3X+56+(x)*8,CtlPanel3Y+32+(y)*8}\r
+ CtlCKbdPanels[] =\r
+ {\r
+{CtlPanelKC3R(1,2),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key for Jumping",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(1,6),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key for Throwing",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(8,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up & Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(15,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(22,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up & Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(8,4),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(22,4),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(8,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down & Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(15,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(22,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down & Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlCJoyPanels[] =\r
+ {\r
+{CtlPanel3R(13,5),uii_Button,CTL_C_CALIBRATEUPPIC,CTL_C_CALIBRATEDNPIC,"Configure Joystick",sc_None,ui_Normal,USL_CtlCJoyButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlSPanels[] =\r
+ {\r
+{CtlPanel2R(3,0),uii_RadioButton,CTL_S_NOSNDUPPIC,CTL_S_NOSNDDNPIC,"Turn Sound Off",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{CtlPanel2R(8,0),uii_RadioButton,CTL_S_PCSNDUPPIC,CTL_S_PCSNDDNPIC,"Use PC Speaker",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{CtlPanel2R(13,0),uii_RadioButton,CTL_S_ADLIBUPPIC,CTL_S_ADLIBDNPIC,"Use AdLib Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{CtlPanel2R(18,0),uii_RadioButton,CTL_S_SNDBLUPPIC,CTL_S_SNDBLDNPIC,"Use SoundBlaster Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{CtlPanel2R(23,0),uii_RadioButton,CTL_S_SNDSRCUPPIC,CTL_S_SNDSRCDNPIC,"Use Sound Source Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlSSSPanels[] =\r
+ {\r
+{CtlPanel3R(7,2),uii_CheckBox,CTL_CHECKUPPIC,CTL_CHECKDNPIC,"Turn Tandy Mode On / Off",sc_None,ui_Normal,0,"Tandy Mode"},\r
+{CtlPanel3R(7,6),uii_CheckBox,CTL_CHECKUPPIC,CTL_CHECKDNPIC,"Switch between LPT1 & LPT2",sc_None,ui_Normal,0,"Use LPT2"},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ CtlMPanels[] =\r
+ {\r
+{CtlPanel2R(9,0),uii_RadioButton,CTL_M_NOMUSUPPIC,CTL_M_NOMUSDNPIC,"Background Music Off"},\r
+{CtlPanel2R(15,0),uii_RadioButton,CTL_M_ADLIBUPPIC,CTL_M_ADLIBDNPIC,"Use AdLib/SoundBlaster Music"},\r
+{-1,-1,-1,-1,uii_Bad}\r
+ },\r
+ *CtlPanels2[] =\r
+ {\r
+ CtlPPanels, // Start\r
+ CtlHPanels, // Help\r
+ CtlDPanels, // Disk\r
+ CtlCPanels, // Controls\r
+ CtlSPanels, // Sound\r
+ CtlMPanels // Music\r
+ },\r
+ *TheItems[4] = {CtlPanels};\r
+static int CtlPanelButton;\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_TurnOff() - Goes through a list of UserItems and sets them all to\r
+// the normal state\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_TurnOff(UserItem *ip)\r
+{\r
+ while (ip->type != uii_Bad)\r
+ {\r
+ ip->sel = ui_Normal;\r
+ ip++;\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_FindDown() - Finds which UserItem, if any, is selected in the given\r
+// list\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static int\r
+USL_FindDown(UserItem *ip)\r
+{\r
+ int i;\r
+\r
+ for (i = 0;ip->type != uii_Bad;i++,ip++)\r
+ if (ip->sel & ui_Selected)\r
+ return(i);\r
+ return(-1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ShowHelp() - Shows the specified string in the help window\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ShowHelp(char *s)\r
+{\r
+ WindowRec wr;\r
+\r
+ if (!s)\r
+ return;\r
+\r
+ US_SaveWindow(&wr);\r
+ US_RestoreWindow(&HelpWindow);\r
+\r
+ US_ClearWindow();\r
+ US_PrintCentered(s);\r
+\r
+ US_RestoreWindow(&wr);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_HandleError() - Handles telling the user that there's been an error\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_HandleError(int num)\r
+{\r
+ char buf[64];\r
+\r
+ strcpy(buf,"Error: ");\r
+ if (num < 0)\r
+ strcat(buf,"Unknown");\r
+ else if (num == ENOMEM)\r
+ strcat(buf,"Disk is Full");\r
+ else if (num == EINVFMT)\r
+ strcat(buf,"File is Incomplete");\r
+ else\r
+ strcat(buf,sys_errlist[num]);\r
+\r
+ VW_HideCursor();\r
+\r
+ fontcolor = F_SECONDCOLOR;\r
+ USL_ShowHelp(buf);\r
+ fontcolor = F_BLACK;\r
+ VW_UpdateScreen();\r
+\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ VW_ShowCursor();\r
+ VW_UpdateScreen();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_DrawItem() - Draws a UserItem. If there's a custom routine, this will\r
+// call it with a uic_Draw command. If the custom routine returns true,\r
+// then the routine handled all of the drawing. If it returns false,\r
+// then this routine does the default drawing.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_DrawItem(word hiti,word hitn)\r
+{\r
+ boolean handled,centered;\r
+ char *text;\r
+ word w,h;\r
+ int picup,picdown;\r
+ Rect r;\r
+ UserItem *ip;\r
+\r
+ ip = &TheItems[hiti][hitn];\r
+ if (ip->custom)\r
+ handled = ip->custom(uic_Draw,hiti,hitn);\r
+ else\r
+ handled = false;\r
+\r
+ if (!handled)\r
+ {\r
+ picup = ip->picup;\r
+ picdown = ip->picdown;\r
+ switch (ip->type)\r
+ {\r
+ case uii_CheckBox:\r
+ px = ip->r.lr.x + 8;\r
+ py = ip->r.ul.y + 8;\r
+ text = ip->text;\r
+ centered = false;\r
+ break;\r
+ case uii_KeyCap:\r
+ if (!(ip->sel & ui_Selected))\r
+ {\r
+ text = ip->text;\r
+ if (text)\r
+ {\r
+ r = ip->r;\r
+ centered = true;\r
+ }\r
+ }\r
+ else\r
+ text = nil;\r
+ break;\r
+ default:\r
+ text = nil;\r
+ break;\r
+ }\r
+\r
+ VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,\r
+ (ip->sel & ui_Selected)? picdown : picup);\r
+ if (text)\r
+ {\r
+ if (centered)\r
+ USL_PrintInCenter(text,r);\r
+ else\r
+ {\r
+ USL_MeasureString(text,&w,&h);\r
+ VWB_Bar(px,py,w + 7,h,WHITE);\r
+ USL_DrawString(text);\r
+ }\r
+ }\r
+ if (ip->sel & ui_Disabled)\r
+ {\r
+ if ((picup == CTL_D_LOADUPPIC) || (picup == CTL_D_SAVEUPPIC))\r
+ VWB_DrawMPic(ip->r.ul.x,ip->r.ul.y,CTL_LSMASKPICM);\r
+ else\r
+ VWB_DrawMPic(ip->r.ul.x,ip->r.ul.y,CTL_LITTLEMASKPICM);\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_DoHit() - Handles a hit on a UserItem. If there's a custom routine,\r
+// it will be called. If it returns true, then don't do anything\r
+// more. If it returns false, then use the standard behaviour\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_DoHit(word hiti,word hitn)\r
+{\r
+ boolean handled;\r
+ word i;\r
+ UserItem *ip;\r
+\r
+ ip = &TheItems[hiti][hitn];\r
+ if (ip->custom)\r
+ handled = ip->custom(uic_Hit,hiti,hitn);\r
+ else\r
+ handled = false;\r
+\r
+ if (!handled)\r
+ {\r
+ if (TheItems[hiti][hitn].sel & ui_Disabled)\r
+ {\r
+ fontcolor = F_SECONDCOLOR;\r
+ USL_ShowHelp("This Item is Disabled");\r
+ fontcolor = F_BLACK;\r
+ return;\r
+ }\r
+\r
+ FlushHelp = true;\r
+\r
+ switch (ip->type)\r
+ {\r
+ case uii_Button:\r
+ // Must have a custom routine to handle hits - this just redraws\r
+ ip->sel ^= ui_Selected;\r
+ USL_DrawItem(hiti,hitn);\r
+ case uii_CheckBox:\r
+ ip->sel ^= ui_Selected;\r
+ USL_DrawItem(hiti,hitn);\r
+ break;\r
+ case uii_RadioButton:\r
+ for (i = 0,ip = TheItems[hiti];ip->type != uii_Bad;i++,ip++)\r
+ {\r
+ if\r
+ (\r
+ (i != hitn)\r
+ && (ip->type == uii_RadioButton)\r
+ && (ip->sel & ui_Selected)\r
+ )\r
+ {\r
+ ip->sel &= ~ui_Selected;\r
+ USL_DrawItem(hiti,i);\r
+ }\r
+ }\r
+ TheItems[hiti][hitn].sel |= ui_Selected;\r
+ USL_DrawItem(hiti,hitn);\r
+ break;\r
+ case uii_KeyCap:\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_IsInRect() - Checks to see if the coordinates given are within any\r
+// of the Rects in the UserItem list. If so, returns true & sets the\r
+// index & number for lookup. If not, returns false.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_IsInRect(word x,word y,word *index,word *number)\r
+{\r
+ UserItem *item,**items;\r
+\r
+ items = TheItems;\r
+ *index = 0;\r
+ while (*items)\r
+ {\r
+ item = *items;\r
+ *number = 0;\r
+ while (item->type != uii_Bad)\r
+ {\r
+ if\r
+ (\r
+ (x >= item->r.ul.x)\r
+ && (x < item->r.lr.x)\r
+ && (y >= item->r.ul.y)\r
+ && (y < item->r.lr.y)\r
+ )\r
+ return(true);\r
+ (*number)++;\r
+ item++;\r
+ }\r
+\r
+ (*index)++;\r
+ items++;\r
+ }\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_TrackItem() - Tracks the given item. If the cursor is inside of the\r
+// item, it's redrawn as down. If the cursor is outside, the item is\r
+// drawn in its original state. Returns true if the button was released\r
+// while the cursor was inside the item, or false if it wasn't.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_TrackItem(word hiti,word hitn)\r
+{\r
+ boolean inside,last;\r
+ word ini,inn,\r
+ on,\r
+ sel,othersel;\r
+ UserItem *ip,*op;\r
+\r
+ ip = &TheItems[hiti][hitn];\r
+ sel = ip->sel;\r
+ if (ip->type == uii_RadioButton)\r
+ {\r
+ inside = false;\r
+ for (op = TheItems[hiti],on = 0;op->type != uii_Bad;op++,on++)\r
+ {\r
+ if (op->sel & ui_Selected)\r
+ {\r
+ inside = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!inside)\r
+ op = ip;\r
+ othersel = op->sel;\r
+ }\r
+ else\r
+ op = nil;\r
+\r
+ if (ip->sel & ui_Disabled)\r
+ {\r
+ fontcolor = F_SECONDCOLOR;\r
+ USL_ShowHelp("This item is disabled");\r
+ fontcolor = F_BLACK;\r
+\r
+ while (US_UpdateCursor())\r
+ VW_UpdateScreen();\r
+\r
+ FlushHelp = true;\r
+ return(false);\r
+ }\r
+\r
+ last = false;\r
+ do\r
+ {\r
+ USL_IsInRect(CursorX,CursorY,&ini,&inn);\r
+ inside = (ini == hiti) && (inn == hitn);\r
+ if (inside != last)\r
+ {\r
+ if (inside)\r
+ {\r
+ if (op)\r
+ {\r
+ op->sel &= ~ui_Selected;\r
+ ip->sel |= ui_Selected;\r
+ }\r
+ else\r
+ ip->sel = sel ^ ui_Selected;\r
+ }\r
+ else\r
+ {\r
+ if (op && (op != ip))\r
+ {\r
+ op->sel |= ui_Selected;\r
+ ip->sel &= ~ui_Selected;\r
+ }\r
+ else\r
+ ip->sel = sel;\r
+ }\r
+\r
+ USL_DrawItem(hiti,hitn);\r
+ if (op && (op != ip))\r
+ USL_DrawItem(hiti,on);\r
+\r
+ last = inside;\r
+ }\r
+ VW_UpdateScreen();\r
+ } while (US_UpdateCursor());\r
+\r
+ if (op)\r
+ op->sel = othersel;\r
+ ip->sel = sel;\r
+ if (!inside)\r
+ {\r
+ if (op && (op != ip))\r
+ USL_DrawItem(hiti,on);\r
+ USL_DrawItem(hiti,hitn);\r
+ VW_UpdateScreen();\r
+ }\r
+\r
+ return(inside);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_GlideCursor() - Smoothly moves the cursor to the given location\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_GlideCursor(long newx,long newy)\r
+{\r
+ word steps;\r
+ long x,y,\r
+ dx,dy;\r
+\r
+ if (grmode == CGAGR)\r
+ steps = 1;\r
+ else\r
+ steps = 8;\r
+\r
+ x = (long)CursorX << 16;\r
+ dx = ((newx << 16) - x) / steps;\r
+ y = (long)CursorY << 16;\r
+ dy = ((newy << 16) - y) / steps;\r
+\r
+ while ((CursorX != newx) || (CursorY != newy))\r
+ {\r
+ x += dx;\r
+ y += dy;\r
+\r
+ CursorX = x >> 16;\r
+ CursorY = y >> 16;\r
+ VW_MoveCursor(CursorX,CursorY);\r
+ VW_UpdateScreen();\r
+ }\r
+ CursorBad = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_FindRect() - Code so ugly you'll puke! Given a Rect and direction,\r
+// this routine will try to find a UserItem to move the cursor to\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_FindRect(Rect r,Motion xd,Motion yd)\r
+{\r
+ word i,i1,i2,i3;\r
+ Motion m1,m2;\r
+ Point diffs[9],diff,*dp;\r
+ Rect *rp,*good,*goods[9];\r
+ UserItem *ip,**items;\r
+\r
+ for (m1 = motion_Up,dp = diffs;m1 <= motion_Down;m1++)\r
+ {\r
+ for (m2 = motion_Left;m2 <= motion_Right;m2++,dp++)\r
+ {\r
+ dp->x = m2 * 1024;\r
+ dp->y = m1 * 1024;\r
+ }\r
+ }\r
+ for (i = 0;i < 9;i++)\r
+ goods[i] = nil;\r
+\r
+ // Find out which octants all of the rects (except r) are in\r
+ for (items = TheItems;*items;items++)\r
+ {\r
+ for (ip = *items;ip->type != uii_Bad;ip++)\r
+ {\r
+ rp = &ip->r;\r
+ diff.x = rp->ul.x - r.ul.x;\r
+ diff.y = rp->ul.y - r.ul.y;\r
+ if (!(diff.x || diff.y))\r
+ continue;\r
+\r
+ if // 1,4,7\r
+ (\r
+ ((rp->ul.x >= r.ul.x) && (rp->ul.x < r.lr.x))\r
+ || ((rp->lr.x > r.ul.x) && (rp->lr.x <= r.lr.x))\r
+ )\r
+ {\r
+ if (rp->lr.y <= r.ul.y)\r
+ {\r
+ if (!(goods[1] && (diff.y < diffs[1].y)))\r
+ {\r
+ goods[1] = rp;\r
+ diffs[1] = diff;\r
+ }\r
+ }\r
+ else if (rp->ul.y >= r.lr.y)\r
+ {\r
+ if (!(goods[7] && (diff.y > diffs[7].y)))\r
+ {\r
+ goods[7] = rp;\r
+ diffs[7] = diff;\r
+ }\r
+ }\r
+ }\r
+\r
+ if // 3,4,5\r
+ (\r
+ ((rp->ul.y >= r.ul.y) && (rp->ul.y < r.lr.y))\r
+ || ((rp->lr.y > r.ul.y) && (rp->lr.y <= r.lr.y))\r
+ )\r
+ {\r
+ if (rp->lr.x <= r.ul.x)\r
+ {\r
+ if (!(goods[3] && (diff.x < diffs[3].x)))\r
+ {\r
+ goods[3] = rp;\r
+ diffs[3] = diff;\r
+ }\r
+ }\r
+ else if (rp->ul.x >= r.lr.x)\r
+ {\r
+ if (!(goods[5] && (diff.x > diffs[5].x)))\r
+ {\r
+ goods[5] = rp;\r
+ diffs[5] = diff;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (rp->ul.x < r.ul.x) // 0,6\r
+ {\r
+ if (rp->lr.y <= r.ul.y)\r
+ {\r
+ if\r
+ (\r
+ (!goods[0])\r
+ || (diff.y > diffs[0].y)\r
+ || (diff.x > diffs[6].x)\r
+ )\r
+ {\r
+ goods[0] = rp;\r
+ diffs[0] = diff;\r
+ }\r
+ }\r
+ else if (rp->ul.y >= r.lr.y)\r
+ {\r
+ if\r
+ (\r
+ (!goods[6])\r
+ || (diff.y < diffs[6].y)\r
+ || (diff.x > diffs[6].x)\r
+ )\r
+ {\r
+ goods[6] = rp;\r
+ diffs[6] = diff;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (rp->lr.x > r.lr.x) // 2,8\r
+ {\r
+ if (rp->lr.y <= r.ul.y)\r
+ {\r
+ if\r
+ (\r
+ (!goods[2])\r
+ || (diff.y > diffs[2].y)\r
+ || (diff.x < diffs[2].x)\r
+ )\r
+ {\r
+ goods[2] = rp;\r
+ diffs[2] = diff;\r
+ }\r
+ }\r
+ else if (rp->ul.y >= r.lr.y)\r
+ {\r
+ if\r
+ (\r
+ (!goods[8])\r
+ || (diff.y < diffs[8].y)\r
+ || (diff.x < diffs[8].x)\r
+ )\r
+ {\r
+ goods[8] = rp;\r
+ diffs[8] = diff;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ switch (yd)\r
+ {\r
+ case motion_Up:\r
+ i1 = 1,i2 = 0,i3 = 2;\r
+ break;\r
+ case motion_None:\r
+ switch (xd)\r
+ {\r
+ case motion_Left:\r
+ i1 = 3,i2 = 0,i3 = 6;\r
+ break;\r
+ case motion_Right:\r
+ i1 = 5,i2 = 8,i3 = 2;\r
+ break;\r
+ }\r
+ break;\r
+ case motion_Down:\r
+ i1 = 7,i2 = 8,i3 = 6;\r
+ break;\r
+ }\r
+\r
+ (\r
+ (good = goods[i1])\r
+ || (good = goods[i2])\r
+ || (good = goods[i3])\r
+ || (good = &r)\r
+ );\r
+#if 0\r
+ CursorX = good->lr.x - 8;\r
+ CursorY = good->lr.y - 8;\r
+ CursorBad = true;\r
+ US_UpdateCursor();\r
+#endif\r
+ USL_GlideCursor(good->lr.x - 8,good->lr.y - 8);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlButtonCustom() - The custom routine for all of the Control Panel\r
+// (leftmost) buttons. Clears all of the other item lists, clears the\r
+// large area, and draws the line dividing the top and bottom areas.\r
+// Then it sets up and draws the appropriate top row of icons.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlButtonCustom(UserCall call,word i,word n)\r
+{\r
+ word j;\r
+ UserItem *ip;\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ if (n == CtlPanelButton)\r
+ return(true);\r
+\r
+ US_ClearWindow();\r
+ for (j = 8;j < 38;j++)\r
+ {\r
+ VWB_DrawTile8M(j * 8,6 * 8,10);\r
+ VWB_DrawTile8M(j * 8,21 * 8,10);\r
+ }\r
+ VWB_DrawTile8M(7 * 8,6 * 8,9);\r
+ VWB_DrawTile8M(38 * 8,6 * 8,11);\r
+ VWB_DrawTile8M(7 * 8,21 * 8,9);\r
+ VWB_DrawTile8M(38 * 8,21 * 8,11);\r
+\r
+ for (j = 1;j < 4;j++)\r
+ TheItems[j] = nil;\r
+\r
+ // Set to new button\r
+ CtlPanelButton = n;\r
+\r
+ // Draw new items\r
+ TheItems[1] = ip = CtlPanels2[CtlPanelButton];\r
+ j = 0;\r
+ while (ip && (ip->type != uii_Bad))\r
+ {\r
+ USL_DrawItem(i + 1,j);\r
+ if (ip->sel & ui_Selected)\r
+ USL_DoHit(i + 1,j);\r
+ j++;\r
+ ip++;\r
+ }\r
+\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlCKbdButtonCustom() - The custom routine for the keyboard keycaps.\r
+// This routine gets a scancode and puts it in the appropriate\r
+// KbdDefs[0] member.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlCKbdButtonCustom(UserCall call,word i,word n)\r
+{\r
+ boolean state;\r
+ word j;\r
+ ScanCode scan;\r
+ longword time;\r
+ UserItem *ip;\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ ip = &TheItems[i][n];\r
+\r
+ fontcolor = F_SECONDCOLOR;\r
+ USL_ShowHelp(ip->help);\r
+ fontcolor = F_BLACK;\r
+ VW_HideCursor();\r
+ VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,ip->picdown);\r
+ VW_UpdateScreen();\r
+\r
+ LastScan = sc_None;\r
+ time = TimeCount;\r
+ state = true;\r
+ do\r
+ {\r
+ if (TimeCount - time > 35) // Half-second delays\r
+ {\r
+ state ^= true;\r
+ VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,state? ip->picdown : ip->picup);\r
+ VW_UpdateScreen();\r
+ time = TimeCount;\r
+ }\r
+ if (US_UpdateCursor())\r
+ {\r
+ while (US_UpdateCursor())\r
+ ;\r
+ scan = sc_Escape;\r
+ break;\r
+ }\r
+\r
+ asm pushf\r
+ asm cli\r
+ if (LastScan == sc_LShift)\r
+ LastScan = sc_None;\r
+ asm popf\r
+ } while (!(scan = LastScan));\r
+ IN_ClearKey(scan);\r
+ if (scan != sc_Escape)\r
+ {\r
+ for (j = 0,state = false;j < 10;j++)\r
+ {\r
+ if (j == n)\r
+ continue;\r
+ if (*(KeyMaps[j]) == scan)\r
+ {\r
+ state = true;\r
+ break;\r
+ }\r
+ }\r
+ if (state)\r
+ {\r
+ fontcolor = F_SECONDCOLOR;\r
+ USL_ShowHelp("That Key is Already Used!");\r
+ fontcolor = F_BLACK;\r
+ }\r
+ else\r
+ {\r
+ ip->text = IN_GetScanName(scan);\r
+ *(KeyMaps[n]) = scan;\r
+ FlushHelp = true;\r
+ }\r
+ }\r
+\r
+ USL_DrawItem(i,n);\r
+ VW_ShowCursor();\r
+ VW_UpdateScreen();\r
+\r
+ return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlCJoyButtonCustom() - The custom button routine for joystick\r
+// calibration\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlCJoyButtonCustom(UserCall call,word i,word n)\r
+{\r
+ word joy,\r
+ minx,maxx,\r
+ miny,maxy;\r
+\r
+ i++,n++; // Shut the compiler up\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ IN_ClearKeysDown();\r
+ joy = USL_FindDown(CtlCPanels) - 1;\r
+\r
+ VW_HideCursor();\r
+ FlushHelp = true;\r
+ fontcolor = F_SECONDCOLOR;\r
+\r
+ USL_ShowHelp("Move Joystick to the Upper-Left");\r
+ VW_UpdateScreen();\r
+ while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joy))\r
+ ;\r
+ if (LastScan != sc_Escape)\r
+ {\r
+ IN_GetJoyAbs(joy,&minx,&miny);\r
+ while (IN_GetJoyButtonsDB(joy))\r
+ ;\r
+\r
+ USL_ShowHelp("Move Joystick to the Lower-Right");\r
+ VW_UpdateScreen();\r
+ while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joy))\r
+ ;\r
+ if (LastScan != sc_Escape)\r
+ {\r
+ IN_GetJoyAbs(0,&maxx,&maxy);\r
+ IN_SetupJoy(joy,minx,maxx,miny,maxy);\r
+ }\r
+ }\r
+\r
+ if (LastScan != sc_Escape)\r
+ while (IN_GetJoyButtonsDB(joy))\r
+ ;\r
+\r
+ if (LastScan)\r
+ IN_ClearKeysDown();\r
+\r
+ fontcolor = F_BLACK;\r
+ VW_ShowCursor();\r
+\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ClearBottom() - Clears the bottom part of the window\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ClearBottom(void)\r
+{\r
+ WindowRec wr;\r
+\r
+ US_SaveWindow(&wr);\r
+ US_RestoreWindow(&BottomWindow);\r
+\r
+ US_ClearWindow();\r
+\r
+ US_RestoreWindow(&wr);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_FormatHelp() - Formats helptext. Runs through and calculates the\r
+// number of lines, and the offset for the start of each line. Stops\r
+// after len bytes or when it hits a tilde ('~'). Munges the text.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+USL_FormatHelp(char far *text,long len)\r
+{\r
+ word line,\r
+ w,h,\r
+ far *off;\r
+ char c,\r
+ far *s,far *l,far *le;\r
+\r
+ WindowX += 4;\r
+ WindowW -= 4;\r
+\r
+ MM_GetPtr(&LineOffsets,MaxHelpLines * sizeof(word));\r
+ off = (word far *)LineOffsets;\r
+ for (line = 0,le = l = s = text;(s - text < len) && (*s != '~');s++)\r
+ {\r
+ if ((c = *s) == '\n')\r
+ {\r
+ *s = '\0';\r
+ *off++ = l - text; // Save offset of start of line\r
+ line++; // Bump line number\r
+ le = l = s + 1; // Set start of line ptr\r
+ }\r
+\r
+ if (c == '\r')\r
+ c = *s = ' ';\r
+ if // Strip orphaned spaces\r
+ (\r
+ (c == ' ')\r
+ && (s == l)\r
+ && (*(s - 1) == '\0')\r
+ && (*(s + 1) != ' ')\r
+ && (s > text)\r
+ )\r
+ le = l = s + 1;\r
+ else if (c == ' ')\r
+ {\r
+ *s = '\0';\r
+ USL_MeasureString(l,&w,&h);\r
+ if (w >= WindowW) // If string width exceeds window,\r
+ {\r
+ *s = c; // Replace null char with proper char\r
+ *le = '\0'; // Go back to last line end\r
+ *off++ = l - text; // Save offset of start of line\r
+ line++; // Bump line number\r
+ l = s = le + 1; // Start next time through after last line end\r
+ }\r
+ else\r
+ {\r
+ *s = c; // Width still ok - put char back\r
+ le = s; // And save ptr to last ok end of word\r
+ }\r
+ }\r
+ }\r
+\r
+ WindowX -= 4;\r
+ WindowW += 4;\r
+\r
+ return(line);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_DrawHelp() - Draws helptext in the current window\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_DrawHelp(char far *text,word start,word end,word line,word h,word far *lp)\r
+{\r
+ px = WindowX + 4;\r
+ py = WindowY + (line * h);\r
+ for (lp += start;start < end;start++,px = WindowX + 4,py += h)\r
+ USL_DrawString(text + *lp++);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_DoHelp() - Formats and displays the specified help\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_DoHelp(memptr text,long len)\r
+{\r
+ boolean done,\r
+ moved;\r
+ int scroll;\r
+ word i,\r
+ pixdiv,\r
+ w,h,\r
+ lines,cur,page,\r
+ top,num,loc,\r
+ far *lp,\r
+ base,srcbase,destbase;\r
+ ScanCode waitkey;\r
+ longword lasttime;\r
+ WindowRec wr;\r
+ CursorInfo info;\r
+\r
+ USL_ShowHelp("Arrow Keys Move / Escape Exits");\r
+ fontcolor = F_BLACK;\r
+\r
+ US_SaveWindow(&wr);\r
+ US_RestoreWindow(&BottomWindow);\r
+ US_ClearWindow();\r
+\r
+ VW_HideCursor();\r
+ VW_UpdateScreen();\r
+\r
+ lines = USL_FormatHelp((char far *)text,len);\r
+ USL_MeasureString("",&w,&h);\r
+ page = WindowH / h;\r
+ cur = 0;\r
+ lp = LineOffsets;\r
+\r
+ IN_ClearKeysDown();\r
+ moved = true;\r
+ lasttime = 0;\r
+ scroll = 0;\r
+ done = false;\r
+ waitkey = sc_None;\r
+ while (!done)\r
+ {\r
+ if (moved)\r
+ {\r
+ while (TimeCount - lasttime < 5)\r
+ ;\r
+ lasttime = TimeCount;\r
+\r
+ if (scroll == -1)\r
+ {\r
+ top = cur;\r
+ num = 1;\r
+ loc = 0;\r
+ }\r
+ else if (scroll == +1)\r
+ {\r
+ num = 1;\r
+ loc = page - 1;\r
+ top = cur + loc;\r
+ }\r
+ else\r
+ {\r
+ top = cur;\r
+ num = (page < lines)? page : lines;\r
+ loc = 0;\r
+ }\r
+ if (scroll)\r
+ {\r
+ if (grmode == CGAGR)\r
+ {\r
+ pixdiv = 4;\r
+ base = bufferofs + panadjust + (WindowX / pixdiv);\r
+ }\r
+ else if (grmode == EGAGR)\r
+ {\r
+ VWB_Bar(WindowX,WindowY + (loc * h),WindowW,num * h,WHITE);\r
+ USL_DrawHelp((char far *)text,top,top + num,loc,h,lp);\r
+\r
+ pixdiv = 8;\r
+ base = displayofs + panadjust + (WindowX / pixdiv);\r
+ }\r
+ else if (grmode == VGAGR)\r
+ pixdiv = 1;\r
+\r
+ if (scroll == 1)\r
+ {\r
+ srcbase = base + ylookup[WindowY + h];\r
+ destbase = base + ylookup[WindowY];\r
+ if (grmode == EGAGR)\r
+ {\r
+ EGAWRITEMODE(1);\r
+ VW_WaitVBL(1);\r
+ }\r
+ VW_ScreenToScreen(srcbase,destbase,WindowW / pixdiv,\r
+ WindowH - h);\r
+ }\r
+ else\r
+ {\r
+ i = WindowY + (h * (page - 1));\r
+ srcbase = base + ylookup[i - h];\r
+ destbase = base + ylookup[i];\r
+ base = ylookup[h];\r
+ for (i = page - 1;i;i--,srcbase -= base,destbase -= base)\r
+ VW_ScreenToScreen(srcbase,destbase,WindowW / pixdiv,h);\r
+ }\r
+ if (grmode == CGAGR)\r
+ {\r
+ VWB_Bar(WindowX,WindowY + (loc * h),WindowW,num * h,WHITE);\r
+ USL_DrawHelp((char far *)text,top,top + num,loc,h,lp);\r
+ VW_UpdateScreen();\r
+ }\r
+ else if (grmode == EGAGR)\r
+ {\r
+ base = panadjust + (WindowX / pixdiv) +\r
+ ylookup[WindowY + (loc * h)];\r
+ VW_ScreenToScreen(base + bufferofs,base + displayofs,\r
+ WindowW / pixdiv,h);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ US_ClearWindow();\r
+ USL_DrawHelp((char far *)text,top,top + num,loc,h,lp);\r
+ VW_UpdateScreen();\r
+ }\r
+\r
+ moved = false;\r
+ scroll = 0;\r
+ }\r
+\r
+ if (waitkey)\r
+ while (IN_KeyDown(waitkey))\r
+ ;\r
+ waitkey = sc_None;\r
+\r
+ IN_ReadCursor(&info);\r
+ if (info.y < 0)\r
+ {\r
+ if (cur > 0)\r
+ {\r
+ scroll = -1;\r
+ cur--;\r
+ moved = true;\r
+ }\r
+ }\r
+ else if (info.y > 0)\r
+ {\r
+ if (cur + page < lines)\r
+ {\r
+ scroll = +1;\r
+ cur++;\r
+ moved = true;\r
+ }\r
+ }\r
+ else if (info.button0 || info.button1)\r
+ done = true;\r
+ else if (IN_KeyDown(LastScan))\r
+ {\r
+ switch (LastScan)\r
+ {\r
+ case sc_Escape:\r
+ done = true;\r
+ break;\r
+ case sc_UpArrow:\r
+ if (cur > 0)\r
+ {\r
+ scroll = -1;\r
+ cur--;\r
+ moved = true;\r
+ }\r
+ break;\r
+ case sc_DownArrow:\r
+ if (cur + page < lines)\r
+ {\r
+ scroll = +1;\r
+ cur++;\r
+ moved = true;\r
+ }\r
+ break;\r
+ case sc_PgUp:\r
+ if (cur > page)\r
+ cur -= page;\r
+ else\r
+ cur = 0;\r
+ moved = true;\r
+ waitkey = sc_PgUp;\r
+ break;\r
+ case sc_PgDn:\r
+ if (cur + page < lines)\r
+ {\r
+ cur += page;\r
+ if (cur + page >= lines)\r
+ cur = lines - page;\r
+ moved = true;\r
+ }\r
+ waitkey = sc_PgDn;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ IN_ClearKeysDown();\r
+ do\r
+ {\r
+ IN_ReadCursor(&info);\r
+ } while (info.button0 || info.button1);\r
+\r
+ VW_ShowCursor();\r
+ US_ClearWindow();\r
+ VW_UpdateScreen();\r
+ US_RestoreWindow(&wr);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlHButtonCustom() - The custom routine for all of the help buttons\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlHButtonCustom(UserCall call,word i,word n)\r
+{\r
+ word j;\r
+ UserItem *ip;\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ ip = &TheItems[i][n];\r
+ if (ip->sel & ui_Disabled)\r
+ return(false);\r
+\r
+ ip->sel |= ui_Selected;\r
+ USL_DrawItem(i,n);\r
+\r
+ USL_ClearBottom();\r
+\r
+ fontcolor = F_SECONDCOLOR;\r
+ USL_ShowHelp("Loading & Formatting Text...");\r
+ VW_UpdateScreen();\r
+\r
+#ifdef HELPTEXTLINKED // Ugly hack because of lack of disk space...\r
+ {\r
+extern char far gametext,far context,far story;\r
+ char far *buf;\r
+ memptr dupe;\r
+\r
+ switch (n)\r
+ {\r
+ case 0:\r
+ buf = &gametext;\r
+ break;\r
+ case 1:\r
+ buf = &context;\r
+ break;\r
+ case 2:\r
+ buf = &story;\r
+ break;\r
+ }\r
+\r
+ MM_GetPtr(&dupe,5000);\r
+ _fmemcpy((char far *)dupe,buf,5000);\r
+\r
+ USL_DoHelp(dupe,5000);\r
+\r
+ MM_FreePtr(&dupe);\r
+ if (LineOffsets)\r
+ MM_FreePtr(&LineOffsets);\r
+ }\r
+#else\r
+ {\r
+ char *name;\r
+ int file;\r
+ long len;\r
+ memptr buf;\r
+\r
+ switch (n)\r
+ {\r
+ case 0:\r
+ name = "GAMETEXT."EXTENSION;\r
+ break;\r
+ case 1:\r
+ name = "CONTEXT."EXTENSION;\r
+ break;\r
+ case 2:\r
+ name = "STORY."EXTENSION;\r
+ break;\r
+ default:\r
+ Quit("Bad help button number");\r
+ }\r
+\r
+ if ((file = open(name,O_RDONLY | O_TEXT)) == -1)\r
+ USL_HandleError(errno);\r
+ else\r
+ {\r
+ len = filelength(file);\r
+ MM_GetPtr(&buf,len);\r
+\r
+ if (CA_FarRead(file,(byte far *)buf,len))\r
+ USL_DoHelp(buf,len);\r
+ else\r
+ USL_HandleError(errno);\r
+\r
+ close(file);\r
+ MM_FreePtr(&buf);\r
+ }\r
+\r
+ if (LineOffsets)\r
+ MM_FreePtr(&LineOffsets);\r
+ }\r
+#endif\r
+\r
+ fontcolor = F_BLACK;\r
+\r
+ ip->sel &= ~ui_Selected;\r
+ USL_DrawItem(i,n);\r
+\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlDButtonCustom() - The custom routine for all of the disk buttons.\r
+// Sets up the bottom area of the window with the appropriate buttons\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlDButtonCustom(UserCall call,word i,word n)\r
+{\r
+ word j;\r
+ UserItem *ip;\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ ip = &TheItems[i][n];\r
+ if (ip->sel & ui_Disabled)\r
+ return(false);\r
+\r
+ USL_ClearBottom();\r
+\r
+ j = 0;\r
+ TheItems[i + 1] = ip = n? CtlDEPanels : CtlDLSPanels;\r
+ while (ip && (ip->type != uii_Bad))\r
+ {\r
+ USL_DrawItem(i + 1,j++);\r
+ ip++;\r
+ }\r
+\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_DLSRect() - Draw the rectangle for the save game names\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static Rect\r
+USL_DLSRect(UserItem *ip)\r
+{\r
+ Rect r;\r
+\r
+ r.ul.x = ip->r.lr.x + 40 + 2;\r
+ r.ul.y = ip->r.ul.y + 2;\r
+ r.lr.x = WindowX + WindowW - 8 - 2;\r
+ r.lr.y = ip->r.lr.y - 2;\r
+\r
+ VWB_Bar(r.ul.x,r.ul.y,r.lr.x - r.ul.x,r.lr.y - r.ul.y,WHITE);\r
+\r
+ VWB_Hlin(r.ul.x,r.lr.x,r.ul.y,BLACK);\r
+ VWB_Hlin(r.ul.x,r.lr.x,r.lr.y,BLACK);\r
+ VWB_Vlin(r.ul.y,r.lr.y,r.ul.x,BLACK);\r
+ VWB_Vlin(r.ul.y,r.lr.y,r.lr.x,BLACK);\r
+\r
+ px = r.ul.x + 2;\r
+ py = r.ul.y + 2;\r
+\r
+ return(r);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlDLButtonCustom() - The load game custom routine\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlDLButtonCustom(UserCall call,word i,word n)\r
+{\r
+ char *filename,\r
+ msg[MaxGameName + 12];\r
+ word err;\r
+ int file;\r
+ UserItem *ip;\r
+ SaveGame *game;\r
+ WindowRec wr;\r
+\r
+ // DEBUG - deal with warning user about loading a game causing abort\r
+\r
+ game = &Games[n / 2];\r
+ ip = &TheItems[i][n];\r
+\r
+ switch (call)\r
+ {\r
+ case uic_Draw:\r
+ if (!loadedgame)\r
+ {\r
+ USL_DLSRect(ip);\r
+ fontcolor = game->present? F_BLACK : F_FIRSTCOLOR;\r
+ USL_DrawString(game->present? game->name : "Empty");\r
+ fontcolor = F_BLACK;\r
+ }\r
+ break;\r
+ case uic_Hit:\r
+ if (ip->sel & ui_Disabled)\r
+ return(false);\r
+\r
+ LeaveDriveOn++;\r
+ filename = USL_GiveSaveName(n / 2);\r
+\r
+ US_SaveWindow(&wr);\r
+ US_CenterWindow(30,3);\r
+ strcpy(msg,"Loading `");\r
+ strcat(msg,game->name);\r
+ strcat(msg,"\'");\r
+ US_PrintCentered(msg);\r
+ VW_HideCursor();\r
+ VW_UpdateScreen();\r
+\r
+ err = 0;\r
+ if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+ {\r
+ if (read(file,game,sizeof(*game)) == sizeof(*game))\r
+ {\r
+ if (USL_LoadGame)\r
+ if (!USL_LoadGame(file))\r
+ USL_HandleError(err = errno);\r
+ }\r
+ else\r
+ USL_HandleError(err = errno);\r
+ close(file);\r
+ }\r
+ else\r
+ USL_HandleError(err = errno);\r
+ if (err)\r
+ abortgame = true;\r
+ else\r
+ loadedgame = true;\r
+ game->present = true;\r
+\r
+ if (loadedgame)\r
+ Paused = true;\r
+\r
+ VW_ShowCursor();\r
+ US_RestoreWindow(&wr);\r
+\r
+ LeaveDriveOn--;\r
+ break;\r
+ }\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlDSButtonCustom() - The save game custom routine\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlDSButtonCustom(UserCall call,word i,word n)\r
+{\r
+ boolean ok;\r
+ char *filename;\r
+ word err;\r
+ int file;\r
+ Rect r;\r
+ UserItem *ip;\r
+ SaveGame *game;\r
+ WindowRec wr;\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ game = &Games[n / 2];\r
+ ip = &TheItems[i][n];\r
+ if (ip->sel & ui_Disabled)\r
+ return(false);\r
+\r
+ FlushHelp = true;\r
+ fontcolor = F_SECONDCOLOR;\r
+ USL_ShowHelp("Enter Game Name / Escape Aborts");\r
+ fontcolor = F_BLACK;\r
+\r
+ r = USL_DLSRect(ip - 1);\r
+ ok = US_LineInput(px,py,game->name,game->present? game->name : nil,true,\r
+ MaxGameName,r.lr.x - r.ul.x - 8);\r
+ if (!strlen(game->name))\r
+ strcpy(game->name,"Untitled");\r
+ if (ok)\r
+ {\r
+ US_SaveWindow(&wr);\r
+ US_CenterWindow(10,3);\r
+ US_PrintCentered("Saving");\r
+ VW_HideCursor();\r
+ VW_UpdateScreen();\r
+\r
+ LeaveDriveOn++;\r
+ filename = USL_GiveSaveName(n / 2);\r
+ err = 0;\r
+ file = open(filename,O_CREAT | O_BINARY | O_WRONLY,\r
+ S_IREAD | S_IWRITE | S_IFREG);\r
+ if (file != -1)\r
+ {\r
+ if (write(file,game,sizeof(*game)) == sizeof(*game))\r
+ {\r
+ if (USL_SaveGame)\r
+ ok = USL_SaveGame(file);\r
+ if (!ok)\r
+ USL_HandleError(err = errno);\r
+ }\r
+ else\r
+ USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+ close(file);\r
+ }\r
+ else\r
+ USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+ if (err)\r
+ {\r
+ remove(filename);\r
+ ok = false;\r
+ }\r
+ LeaveDriveOn--;\r
+\r
+ VW_ShowCursor();\r
+ US_RestoreWindow(&wr);\r
+ USL_DoHit(i - 1,0);\r
+ VW_UpdateScreen();\r
+ }\r
+\r
+ if (!game->present)\r
+ game->present = ok;\r
+\r
+ if (ok)\r
+ {\r
+ GameIsDirty = false;\r
+ (ip - 1)->sel &= ~ui_Disabled;\r
+ }\r
+\r
+ USL_DrawItem(i,n - 1);\r
+// USL_CtlDLButtonCustom(uic_Draw,i,n - 1);\r
+\r
+ return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlSButtonCustom() - The custom routine for all of the sound buttons\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlSButtonCustom(UserCall call,word i,word n)\r
+{\r
+ word j;\r
+ UserItem *ip;\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ ip = &TheItems[i][n];\r
+ if (ip->sel & ui_Disabled)\r
+ return(false);\r
+\r
+ USL_ClearBottom();\r
+\r
+ if (n == sdm_SoundSource)\r
+ {\r
+ j = 0;\r
+ TheItems[i + 1] = ip = CtlSSSPanels;\r
+ while (ip && (ip->type != uii_Bad))\r
+ {\r
+ USL_DrawItem(i + 1,j++);\r
+ ip++;\r
+ }\r
+ }\r
+ else\r
+ TheItems[i + 1] = nil;\r
+\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlPButtonCustom() - The custom routine for all of the start game btns\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlPButtonCustom(UserCall call,word i,word n)\r
+{\r
+ word j;\r
+ UserItem *ip;\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ ip = &TheItems[i][n];\r
+ if (ip->sel & ui_Disabled)\r
+ return(false);\r
+\r
+ USL_ClearBottom();\r
+\r
+ j = 0;\r
+ TheItems[i + 1] = ip = n? CtlPRPanels : CtlPSPanels;\r
+ while (ip && (ip->type != uii_Bad))\r
+ {\r
+ USL_DrawItem(i + 1,j++);\r
+ ip++;\r
+ }\r
+\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_GiveAbortWarning() - Draws a string that warns the user that an\r
+// action they're about to take will abort the game in progress\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_GiveAbortWarning(void)\r
+{\r
+ WindowRec wr;\r
+\r
+ if (!GameIsDirty)\r
+ return;\r
+\r
+ US_SaveWindow(&wr);\r
+ US_RestoreWindow(&BottomWindow);\r
+ US_HomeWindow();\r
+ PrintY += 5;\r
+\r
+ VWB_Bar(WindowX,WindowY,WindowW,30,WHITE);\r
+ fontcolor = F_SECONDCOLOR;\r
+ US_CPrint("Warning! If you do this, you'll");\r
+ US_CPrint("abort the current game.");\r
+ fontcolor = F_BLACK;\r
+\r
+ US_RestoreWindow(&wr);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlPSButtonCustom() - The custom routine for the start game button\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlPSButtonCustom(UserCall call,word i,word n)\r
+{\r
+ boolean result;\r
+ UserItem *ip;\r
+\r
+ i++; // Shut the compiler up\r
+\r
+ switch (call)\r
+ {\r
+ case uic_Hit:\r
+ switch (n)\r
+ {\r
+ case 0:\r
+ restartgame = gd_Normal;\r
+ break;\r
+ case 1:\r
+ restartgame = gd_Easy;\r
+ break;\r
+ case 2:\r
+ restartgame = gd_Hard;\r
+ break;\r
+ }\r
+ if (restartgame && ingame && USL_ResetGame)\r
+ USL_ResetGame();\r
+ result = false;\r
+ break;\r
+ case uic_Draw:\r
+ USL_GiveAbortWarning();\r
+ result = false;\r
+ break;\r
+ default:\r
+ result = false;\r
+ break;\r
+ }\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlPRButtonCustom() - The custom routine for the resume game button\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlPRButtonCustom(UserCall call,word i,word n)\r
+{\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ i++,n++; // Shut the compiler up\r
+ ResumeGame = true;\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlDEButtonCustom() - The custom routine for the exit to DOS button\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlDEButtonCustom(UserCall call,word i,word n)\r
+{\r
+ boolean result;\r
+ UserItem *ip;\r
+\r
+ i++,n++; // Shut the compiler up\r
+\r
+ switch (call)\r
+ {\r
+ case uic_Hit:\r
+ QuitToDos = true;\r
+ break;\r
+ case uic_Draw:\r
+ USL_GiveAbortWarning();\r
+ default:\r
+ result = false;\r
+ break;\r
+ }\r
+ return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CtlCButtonCustom() - The custom routine for all of the control\r
+// buttons\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlCButtonCustom(UserCall call,word i,word n)\r
+{\r
+ word j;\r
+ Point p;\r
+ UserItem *ip;\r
+\r
+ if (call != uic_Hit)\r
+ return(false);\r
+\r
+ ip = &TheItems[i][n];\r
+ if (ip->sel & ui_Disabled)\r
+ return(false);\r
+\r
+ USL_ClearBottom();\r
+ if (n == 0) // Keyboard\r
+ {\r
+ TheItems[i + 1] = ip = CtlCKbdPanels;\r
+ p = CtlCKbdPanels[2].r.lr;\r
+ VWB_DrawPic(p.x,p.y,CTL_DIRSPIC);\r
+ }\r
+ else\r
+ TheItems[i + 1] = ip = CtlCJoyPanels;\r
+\r
+ j = 0;\r
+ while (ip && (ip->type != uii_Bad))\r
+ {\r
+ USL_DrawItem(i + 1,j++);\r
+ ip++;\r
+ }\r
+\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_HitHotKey() - After a hotkey was hit, move the cursor to the first\r
+// selected item in the group after the group containing the item\r
+// holding the hotkey\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_HitHotKey(int i,int n)\r
+{\r
+ UserItem *ip;\r
+\r
+ if (ip = TheItems[++i])\r
+ {\r
+ if ((n = USL_FindDown(TheItems[i])) == -1)\r
+ n = 0;\r
+ ip += n;\r
+ CursorX = ip->r.lr.x - 8;\r
+ CursorY = ip->r.lr.y - 8;\r
+ CursorBad = true;\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CheckScan() - Checks to see if the scancode in LastScan corresponds\r
+// to anything in the list of useritems. If so, selects the item.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CheckScan(word *ci,word *cn)\r
+{\r
+ word i,n;\r
+ UserItem *ip;\r
+\r
+ if (!LastScan)\r
+ return(false);\r
+\r
+#if 1 // DEBUG - probably kill this code\r
+ // Use 1..? for the items across the top row\r
+ if (TheItems[1] && !IN_KeyDown(sc_RShift))\r
+ {\r
+ for (i = 0,ip = TheItems[1];(ip->type != uii_Bad) && (i < 9);i++,ip++)\r
+ ;\r
+ for (n = 0;n < i;n++)\r
+ {\r
+ if (LastScan == 2 + n) // Numbers from 1..9\r
+ {\r
+ if (!(TheItems[1][n].sel & ui_Disabled))\r
+ {\r
+ LastScan = sc_None;\r
+ USL_DoHit(1,n);\r
+ return(true);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // Use Alt-1..6 for the items in the leftmost column\r
+ if (IN_KeyDown(sc_RShift))\r
+ {\r
+ n = LastScan - 2;\r
+ if (n < 6) // Numbers from 1..6\r
+ {\r
+ USL_DoHit(0,n);\r
+ LastScan = sc_None;\r
+ return(true);\r
+ }\r
+ }\r
+#endif\r
+\r
+ // Check normal hotkeys for the leftmost column\r
+ for (i = 0;CtlPanels[i].type != uii_Bad;i++)\r
+ {\r
+ if (CtlPanels[i].key == LastScan)\r
+ {\r
+ LastScan = sc_None;\r
+ USL_DoHit(0,i);\r
+ *ci = 0;\r
+ *cn = i;\r
+ USL_HitHotKey(0,i);\r
+ return(true);\r
+ }\r
+ }\r
+\r
+ // Check normal hotkeys for the top row\r
+ for (i = 0;i < 6;i++)\r
+ {\r
+ for (n = 0,ip = CtlPanels2[i];ip && ip->type != uii_Bad;n++,ip++)\r
+ {\r
+ if ((ip->key == LastScan) && !(ip->sel & ui_Disabled))\r
+ {\r
+ LastScan = sc_None;\r
+ USL_DoHit(0,i);\r
+ USL_DoHit(1,n);\r
+ *ci = 1;\r
+ *cn = n;\r
+ USL_HitHotKey(1,n);\r
+ return(true);\r
+ }\r
+ }\r
+ }\r
+ return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the\r
+// values of all the appropriate variables\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_SetUpCtlPanel(void)\r
+{\r
+ word i,j;\r
+\r
+ GameIsDirty = ingame;\r
+\r
+ // Set up restart game\r
+ USL_TurnOff(CtlPPanels);\r
+ CtlPPanels[0].sel = ingame? ui_Normal : ui_Selected;\r
+ CtlPPanels[1].sel = ingame? ui_Selected : ui_Disabled;\r
+\r
+ // Set up disk stuff - default to load/save game\r
+ USL_TurnOff(CtlDPanels);\r
+ CtlDPanels[0].sel = ui_Selected;\r
+\r
+ // Set up load/save buttons\r
+ USL_TurnOff(CtlDLSPanels);\r
+ for (i = 0;i < MaxSaveGames;i++)\r
+ {\r
+ if (!Games[i].present)\r
+ CtlDLSPanels[i * 2].sel = ui_Disabled;\r
+ if (!ingame)\r
+ CtlDLSPanels[(i * 2) + 1].sel = ui_Disabled;\r
+ }\r
+\r
+ // Set up Controls\r
+ USL_TurnOff(CtlCPanels);\r
+ CtlCPanels[1].sel = JoysPresent[0]? ui_Normal : ui_Disabled;\r
+ CtlCPanels[2].sel = JoysPresent[1]? ui_Normal : ui_Disabled;\r
+ if (Controls[0] == ctrl_Keyboard)\r
+ i = 0;\r
+ else\r
+ i = (Controls[0] == ctrl_Joystick1)? 1 : 2;\r
+ CtlCPanels[i].sel |= ui_Selected;\r
+ if (JoysPresent[1] && !JoysPresent[0])\r
+ CtlCPanels[2].key = sc_F4;\r
+ else\r
+ CtlCPanels[1].key = sc_F4;\r
+\r
+ // Set up Keyboard\r
+ for (i = 0;i < 10;i++)\r
+ CtlCKbdPanels[i].text = IN_GetScanName(*(KeyMaps[i]));\r
+\r
+ // Set up Sounds\r
+ USL_TurnOff(CtlSPanels);\r
+ CtlSPanels[sdm_AdLib].sel = AdLibPresent? ui_Normal : ui_Disabled;\r
+#if 0 // DEBUG - hack because no space for digitized sounds on Keen Dreams\r
+ CtlSPanels[sdm_SoundBlaster].sel =\r
+ SoundBlasterPresent? ui_Normal : ui_Disabled;\r
+ CtlSPanels[sdm_SoundSource].sel =\r
+ SoundSourcePresent? ui_Normal : ui_Disabled;\r
+#else\r
+ CtlSPanels[sdm_SoundBlaster].sel = ui_Disabled;\r
+ CtlSPanels[sdm_SoundSource].sel = ui_Disabled;\r
+#endif\r
+ CtlSPanels[SoundMode].sel |= ui_Selected;\r
+\r
+ // Set up SoundSource\r
+ USL_TurnOff(CtlSSSPanels);\r
+ CtlSSSPanels[0].sel = ssIsTandy? ui_Selected : ui_Normal;\r
+ CtlSSSPanels[1].sel = (ssPort == 2)? ui_Selected : ui_Normal;\r
+\r
+ // Set up Music\r
+ USL_TurnOff(CtlMPanels);\r
+ CtlMPanels[smm_AdLib].sel = AdLibPresent? ui_Normal : ui_Disabled;\r
+ CtlMPanels[MusicMode].sel |= ui_Selected;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_TearDownCtlPanel() - Given the state of the control panel, sets the\r
+// modes and values as appropriate\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_TearDownCtlPanel(void)\r
+{\r
+ int i;\r
+\r
+ i = USL_FindDown(CtlCPanels);\r
+ if (i != -1)\r
+ {\r
+ i = i? (i == 1? ctrl_Joystick1 : ctrl_Joystick2) : ctrl_Keyboard;\r
+ IN_SetControlType(0,i);\r
+ }\r
+\r
+ CtlCPanels[1].key = CtlCPanels[2].key = sc_None;\r
+\r
+ i = USL_FindDown(CtlSPanels);\r
+ if (i != -1)\r
+ SD_SetSoundMode(i);\r
+\r
+ ssIsTandy = CtlSSSPanels[0].sel & ui_Selected;\r
+ ssPort = (CtlSSSPanels[1].sel & ui_Selected)? 2 : 1;\r
+\r
+ i = USL_FindDown(CtlMPanels);\r
+ if (i != -1)\r
+ {\r
+ SD_SetMusicMode(i);\r
+\r
+ if (!QuitToDos)\r
+ {\r
+ US_CenterWindow(20,8);\r
+ US_CPrint("Loading");\r
+#if 0\r
+ fontcolor = F_SECONDCOLOR;\r
+ US_CPrint("Sounds");\r
+ fontcolor = F_BLACK;\r
+#endif\r
+ VW_UpdateScreen();\r
+\r
+ CA_LoadAllSounds();\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_ControlPanel() - This is the main routine for the control panel\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ControlPanel(void)\r
+{\r
+ char gamename[MaxGameName + 10 + 1];\r
+ ScanCode c;\r
+ boolean done,\r
+ buttondown,inrect;\r
+ word hiti,hitn,\r
+ i,n,\r
+ lasti,lastn,\r
+ lastx,lasty;\r
+ longword lasttime;\r
+ Point p;\r
+ Rect userect;\r
+ UserItem *ip;\r
+\r
+ c = LastScan;\r
+ if (c == sc_Escape) // Map escape from game to Exit to DOS\r
+ c = sc_Q;\r
+\r
+ CA_UpLevel();\r
+ for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++)\r
+ CA_MarkGrChunk(i);\r
+ CA_MarkGrChunk(CTL_LITTLEMASKPICM);\r
+ CA_MarkGrChunk(CTL_LSMASKPICM);\r
+ CA_CacheMarks("Options Screen");\r
+\r
+ USL_SetUpCtlPanel();\r
+\r
+ US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString);\r
+ fontcolor = F_BLACK;\r
+\r
+ VW_InitDoubleBuffer();\r
+\r
+ VWB_Bar(0,0,MaxX,MaxY,FIRSTCOLOR);\r
+ US_DrawWindow(8,22,30,2);\r
+ US_SaveWindow(&HelpWindow);\r
+ US_DrawWindow(8,7,30,14);\r
+ US_SaveWindow(&BottomWindow);\r
+ US_DrawWindow(8,1,30,20);\r
+\r
+ for (ip = CtlPanels;ip->type != uii_Bad;ip++)\r
+ VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,ip->picup);\r
+\r
+ US_StartCursor();\r
+ CursorX = (8 * 8) + ((MaxX - (8 * 8)) / 2);\r
+ CursorBad = true;\r
+\r
+ CtlPanelButton = -1;\r
+ LastScan = c;\r
+ USL_CheckScan(&i,&n);\r
+ if (CtlPanelButton == -1)\r
+ USL_DoHit(0,0);\r
+\r
+ ResumeGame = false;\r
+ done = false;\r
+ FlushHelp = true;\r
+ lastx = lasty = -1;\r
+ while\r
+ (\r
+ (restartgame == gd_Continue)\r
+ && !(done || loadedgame || ResumeGame)\r
+ )\r
+ {\r
+ VW_UpdateScreen();\r
+\r
+ buttondown = US_UpdateCursor();\r
+ inrect = USL_IsInRect(CursorX,CursorY,&i,&n);\r
+\r
+ if (FlushHelp)\r
+ {\r
+ lasti = -2;\r
+ lasttime = TimeCount;\r
+ FlushHelp = false;\r
+ }\r
+ if (inrect)\r
+ {\r
+ if ((lasti != i) || (lastn != n))\r
+ {\r
+ // If over a Load button\r
+ if\r
+ (\r
+ (CtlPanelButton == 2)\r
+ && (i == 2)\r
+ && (TheItems[1][0].sel & ui_Selected)\r
+ && (Games[n / 2].present)\r
+ && !(n & 1)\r
+ )\r
+ {\r
+ strcpy(gamename,"Load `");\r
+ strcat(gamename,Games[n / 2].name);\r
+ strcat(gamename,"'");\r
+ USL_ShowHelp(gamename);\r
+ }\r
+ else\r
+ USL_ShowHelp(TheItems[i][n].help);\r
+ lasti = i;\r
+ lastn = n;\r
+ }\r
+ }\r
+ else if (lasti != (word)-1)\r
+ {\r
+ USL_ShowHelp("Select a Button");\r
+ lasti = -1;\r
+ }\r
+\r
+ hiti = i;\r
+ hitn = n;\r
+\r
+ if (inrect)\r
+ userect = TheItems[i][n].r;\r
+ else\r
+ {\r
+ userect.ul.x = CursorX;\r
+ userect.ul.y = CursorY;\r
+ userect.lr = userect.ul;\r
+ }\r
+\r
+ if (IN_KeyDown(sc_UpArrow))\r
+ {\r
+ USL_FindRect(userect,motion_None,motion_Up);\r
+ buttondown = false;\r
+ IN_ClearKey(sc_UpArrow);\r
+ }\r
+ else if (IN_KeyDown(sc_DownArrow))\r
+ {\r
+ USL_FindRect(userect,motion_None,motion_Down);\r
+ buttondown = false;\r
+ IN_ClearKey(sc_DownArrow);\r
+ }\r
+ else if (IN_KeyDown(sc_LeftArrow))\r
+ {\r
+ USL_FindRect(userect,motion_Left,motion_None);\r
+ buttondown = false;\r
+ IN_ClearKey(sc_LeftArrow);\r
+ }\r
+ else if (IN_KeyDown(sc_RightArrow))\r
+ {\r
+ USL_FindRect(userect,motion_Right,motion_None);\r
+ buttondown = false;\r
+ IN_ClearKey(sc_RightArrow);\r
+ }\r
+ else if\r
+ (\r
+ IN_KeyDown(c = sc_Return)\r
+ || IN_KeyDown(c = KbdDefs[0].button0)\r
+ || IN_KeyDown(c = KbdDefs[0].button1)\r
+ )\r
+ {\r
+ IN_ClearKey(c);\r
+ if (inrect)\r
+ {\r
+ ip = &TheItems[hiti][hitn];\r
+\r
+ if ((ip->type == uii_Button) && !(ip->sel & ui_Disabled))\r
+ {\r
+ lasttime = TimeCount;\r
+\r
+ ip->sel |= ui_Selected;\r
+ USL_DrawItem(hiti,hitn);\r
+ VW_UpdateScreen();\r
+\r
+ while (TimeCount - lasttime < TickBase / 4)\r
+ ;\r
+ lasttime = TimeCount;\r
+\r
+ ip->sel &= ~ui_Selected;\r
+ USL_DrawItem(hiti,hitn);\r
+ VW_UpdateScreen();\r
+\r
+ while (TimeCount - lasttime < TickBase / 4)\r
+ ;\r
+ }\r
+\r
+ USL_DoHit(hiti,hitn);\r
+ }\r
+ }\r
+ else if (USL_CheckScan(&i,&n))\r
+ ;\r
+ else if (buttondown && inrect && USL_TrackItem(hiti,hitn))\r
+ USL_DoHit(hiti,hitn);\r
+\r
+ if (LastScan == sc_Escape)\r
+ {\r
+ IN_ClearKey(sc_Escape);\r
+ done = true;\r
+ }\r
+\r
+ if (QuitToDos)\r
+ done = true;\r
+\r
+ if ((lastx != CursorX) || (lasty != CursorY))\r
+ {\r
+ lastx = CursorX;\r
+ lasty = CursorY;\r
+ lasttime = TimeCount;\r
+ }\r
+ if (TimeCount - lasttime > TickBase * 10)\r
+ {\r
+ if (((TimeCount - lasttime) / TickBase) & 2)\r
+ fontcolor = F_SECONDCOLOR;\r
+ USL_ShowHelp("Press F1 for Help");\r
+ fontcolor = F_BLACK;\r
+ }\r
+ }\r
+\r
+ US_ShutCursor();\r
+\r
+ USL_TearDownCtlPanel();\r
+\r
+ if (restartgame && USL_ResetGame)\r
+ USL_ResetGame();\r
+\r
+ if (QuitToDos)\r
+ {\r
+ if (tedlevel)\r
+ TEDDeath();\r
+ else\r
+ {\r
+ US_CenterWindow(20,3);\r
+ fontcolor = F_SECONDCOLOR;\r
+ US_PrintCentered("Now Exiting to DOS...");\r
+ fontcolor = F_BLACK;\r
+ VW_UpdateScreen();\r
+ Quit(nil);\r
+ }\r
+ }\r
+\r
+ CA_DownLevel();\r
+}\r
+\r
+// High score routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_DisplayHighScores() - Assumes that double buffering has been started.\r
+// If passed a -1 will just display the high scores, but if passed\r
+// a non-negative number will display that entry in red and let the\r
+// user type in a name\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_DisplayHighScores(int which)\r
+{\r
+ char buffer[16],*str;\r
+ word i,\r
+ w,h,\r
+ x,y;\r
+ HighScore *s;\r
+\r
+ US_CenterWindow(30,MaxScores + (MaxScores / 2));\r
+\r
+ x = WindowX + (WindowW / 2);\r
+ US_Print(" Name");\r
+ PrintX = x + 20;\r
+ US_Print("Score");\r
+ PrintX = x + 60;\r
+ US_Print("Done\n\n");\r
+ PrintY -= 3;\r
+\r
+ for (i = WindowX;i < WindowX + WindowW;i += 8)\r
+ VWB_DrawTile8M(i,WindowY + 8,10);\r
+ VWB_DrawTile8M(WindowX - 8,WindowY + 8,9);\r
+ VWB_DrawTile8M(WindowX + WindowW,WindowY + 8,11);\r
+\r
+ for (i = 0,s = Scores;i < MaxScores;i++,s++)\r
+ {\r
+ fontcolor = (i == which)? F_SECONDCOLOR : F_BLACK;\r
+\r
+ if (i != which)\r
+ {\r
+ US_Print(" ");\r
+ if (strlen(s->name))\r
+ US_Print(s->name);\r
+ else\r
+ US_Print("-");\r
+ }\r
+ else\r
+ y = PrintY;\r
+\r
+ PrintX = x + (7 * 8);\r
+ ultoa(s->score,buffer,10);\r
+ for (str = buffer;*str;str++)\r
+ *str = *str + (129 - '0'); // Used fixed-width numbers (129...)\r
+ USL_MeasureString(buffer,&w,&h);\r
+ PrintX -= w;\r
+ US_Print(buffer);\r
+\r
+ PrintX = x + 60;\r
+ if (s->completed)\r
+ US_PrintUnsigned(s->completed);\r
+ else\r
+ US_Print("-");\r
+\r
+ US_Print("\n");\r
+ }\r
+\r
+ if (which != -1)\r
+ {\r
+ fontcolor = F_SECONDCOLOR;\r
+ PrintY = y;\r
+ PrintX = WindowX;\r
+ US_Print(" ");\r
+ strcpy(Scores[which].name,"");\r
+ US_LineInput(PrintX,PrintY,Scores[which].name,nil,true,MaxHighName,\r
+ (WindowW / 2) - 8);\r
+ }\r
+ fontcolor = F_BLACK;\r
+\r
+ VW_UpdateScreen();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CheckHighScore() - Checks gamestate to see if the just-ended game\r
+// should be entered in the high score list. If so, lets the user\r
+// enter their name\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CheckHighScore(long score,word other)\r
+{\r
+ word i,j,\r
+ n;\r
+ HighScore myscore;\r
+\r
+ strcpy(myscore.name,"");\r
+ myscore.score = score;\r
+ myscore.completed = other;\r
+\r
+ for (i = 0,n = -1;i < MaxScores;i++)\r
+ {\r
+ if\r
+ (\r
+ (myscore.score > Scores[i].score)\r
+ || (\r
+ (myscore.score == Scores[i].score)\r
+ && (myscore.completed > Scores[i].completed)\r
+ )\r
+ )\r
+ {\r
+ for (j = MaxScores;--j > i;)\r
+ Scores[j] = Scores[j - 1];\r
+ Scores[i] = myscore;\r
+\r
+ n = i;\r
+ HighScoresDirty = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ VW_InitDoubleBuffer();\r
+ VWB_Bar(0,0,MaxX,MaxY,FIRSTCOLOR);\r
+\r
+ US_DisplayHighScores(n);\r
+ IN_UserInput(5 * TickBase,false);\r
+}\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_US.h - Header file for the User Manager\r
+// v1.0d1\r
+// By Jason Blochowiak\r
+//\r
+\r
+#ifndef __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef __ID_US__\r
+#define __ID_US__\r
+\r
+#ifdef __DEBUG__\r
+#define __DEBUG_UserMgr__\r
+#endif\r
+\r
+//#define HELPTEXTLINKED\r
+\r
+#define MaxHelpLines 500\r
+\r
+#define MaxHighName 57\r
+#define MaxScores 7\r
+typedef struct\r
+ {\r
+ char name[MaxHighName + 1];\r
+ long score;\r
+ word completed;\r
+ } HighScore;\r
+\r
+#define MaxGameName 32\r
+#define MaxSaveGames 6\r
+typedef struct\r
+ {\r
+ char signature[4];\r
+ word *oldtest;\r
+ boolean present;\r
+ char name[MaxGameName + 1];\r
+ } SaveGame;\r
+\r
+#define MaxString 128 // Maximum input string size\r
+\r
+typedef struct\r
+ {\r
+ int x,y,\r
+ w,h,\r
+ px,py;\r
+ } WindowRec; // Record used to save & restore screen windows\r
+\r
+typedef enum\r
+ {\r
+ gd_Continue,\r
+ gd_Easy,\r
+ gd_Normal,\r
+ gd_Hard\r
+ } GameDiff;\r
+\r
+// Hack import for TED launch support\r
+extern boolean tedlevel;\r
+extern word tedlevelnum;\r
+extern void TEDDeath(void);\r
+\r
+extern word MaxX,MaxY; // MDM (GAMERS EDGE)\r
+\r
+extern boolean ingame, // Set by game code if a game is in progress\r
+ abortgame, // Set if a game load failed\r
+ loadedgame, // Set if the current game was loaded\r
+ NoWait,\r
+ HighScoresDirty;\r
+extern char *abortprogram; // Set to error msg if program is dying\r
+extern GameDiff restartgame; // Normally gd_Continue, else starts game\r
+extern word PrintX,PrintY; // Current printing location in the window\r
+extern word WindowX,WindowY,// Current location of window\r
+ WindowW,WindowH;// Current size of window\r
+\r
+extern boolean Button0,Button1,\r
+ CursorBad;\r
+extern int CursorX,CursorY;\r
+\r
+extern void (*USL_MeasureString)(char far *,word *,word *),\r
+ (*USL_DrawString)(char far *);\r
+\r
+extern boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int);\r
+extern void (*USL_ResetGame)(void);\r
+extern SaveGame Games[MaxSaveGames];\r
+extern HighScore Scores[];\r
+\r
+#define US_HomeWindow() {PrintX = WindowX; PrintY = WindowY;}\r
+\r
+extern void US_Startup(void),\r
+ US_Setup(void),\r
+ US_Shutdown(void),\r
+ US_InitRndT(boolean randomize),\r
+ US_SetLoadSaveHooks(boolean (*load)(int),\r
+ boolean (*save)(int),\r
+ void (*reset)(void)),\r
+ US_TextScreen(void),\r
+ US_UpdateTextScreen(void),\r
+ US_FinishTextScreen(void),\r
+ US_ControlPanel(void),\r
+ US_DrawWindow(word x,word y,word w,word h),\r
+ US_CenterWindow(word,word),\r
+ US_SaveWindow(WindowRec *win),\r
+ US_RestoreWindow(WindowRec *win),\r
+ US_ClearWindow(void),\r
+ US_SetPrintRoutines(void (*measure)(char far *,word *,word *),\r
+ void (*print)(char far *)),\r
+ US_PrintCentered(char *s),\r
+ US_CPrint(char *s),\r
+ US_CPrintLine(char *s),\r
+ US_Print(char *s),\r
+ US_PrintUnsigned(longword n),\r
+ US_PrintSigned(long n),\r
+ US_StartCursor(void),\r
+ US_ShutCursor(void),\r
+ US_ControlPanel(void),\r
+ US_CheckHighScore(long score,word other),\r
+ US_DisplayHighScores(int which);\r
+extern boolean US_UpdateCursor(void),\r
+ US_LineInput(int x,int y,char *buf,char *def,boolean escok,\r
+ int maxchars,int maxwidth);\r
+extern int US_CheckParm(char *parm,char **strings),\r
+ US_RndT(void);\r
+\r
+ void USL_PrintInCenter(char *s,Rect r);\r
+ char *USL_GiveSaveName(word game);\r
+#endif\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_US_1.c - User Manager - General routines\r
+// v1.1d1\r
+// By Jason Blochowiak\r
+// Hacked up for Catacomb 3D\r
+//\r
+\r
+//\r
+// This module handles dealing with user input & feedback\r
+//\r
+// Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching,\r
+// and Refresh Mgrs, Memory Mgr for background save/restore\r
+//\r
+// Globals:\r
+// ingame - Flag set by game indicating if a game is in progress\r
+// abortgame - Flag set if the current game should be aborted (if a load\r
+// game fails)\r
+// loadedgame - Flag set if a game was loaded\r
+// abortprogram - Normally nil, this points to a terminal error message\r
+// if the program needs to abort\r
+// restartgame - Normally set to gd_Continue, this is set to one of the\r
+// difficulty levels if a new game should be started\r
+// PrintX, PrintY - Where the User Mgr will print (global coords)\r
+// WindowX,WindowY,WindowW,WindowH - The dimensions of the current\r
+// window\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+\r
+#pragma hdrstop\r
+\r
+#pragma warn -pia\r
+\r
+\r
+// Special imports\r
+extern boolean showscorebox;\r
+#ifdef KEEN\r
+extern boolean oldshooting;\r
+extern ScanCode firescan;\r
+#else\r
+ ScanCode firescan;\r
+#endif\r
+\r
+// Global variables\r
+ char *abortprogram;\r
+ boolean NoWait,\r
+ HighScoresDirty;\r
+ word PrintX,PrintY;\r
+ word WindowX,WindowY,WindowW,WindowH;\r
+\r
+ word MaxX=320,MaxY=200; // MDM (GAMERS EDGE)\r
+\r
+// Internal variables\r
+#define ConfigVersion 1\r
+\r
+static char *ParmStrings[] = {"TEDLEVEL","NOWAIT"},\r
+ *ParmStrings2[] = {"COMP","NOCOMP"};\r
+static boolean US_Started;\r
+\r
+ boolean Button0,Button1,\r
+ CursorBad;\r
+ int CursorX,CursorY;\r
+\r
+ void (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString,\r
+ (*USL_DrawString)(char far *) = VWB_DrawPropString;\r
+\r
+ boolean (*USL_SaveGame)(int),(*USL_LoadGame)(int);\r
+ void (*USL_ResetGame)(void);\r
+ SaveGame Games[MaxSaveGames];\r
+ HighScore Scores[MaxScores] =\r
+ {\r
+ {"Sir Lancelot",500,3},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ {"",0},\r
+ };\r
+\r
+// Internal routines\r
+\r
+// Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed\r
+// from DOS.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma warn -par\r
+#pragma warn -rch\r
+int\r
+USL_HardError(word errval,int ax,int bp,int si)\r
+{\r
+#define IGNORE 0\r
+#define RETRY 1\r
+#define ABORT 2\r
+extern void ShutdownId(void);\r
+\r
+static char buf[32];\r
+static WindowRec wr;\r
+ int di;\r
+ char c,*s,*t;\r
+\r
+\r
+ di = _DI;\r
+\r
+ if (ax < 0)\r
+ s = "Device Error";\r
+ else\r
+ {\r
+ if ((di & 0x00ff) == 0)\r
+ s = "Drive ~ is Write Protected";\r
+ else\r
+ s = "Error on Drive ~";\r
+ for (t = buf;*s;s++,t++) // Can't use sprintf()\r
+ if ((*t = *s) == '~')\r
+ *t = (ax & 0x00ff) + 'A';\r
+ *t = '\0';\r
+ s = buf;\r
+ }\r
+\r
+ c = peekb(0x40,0x49); // Get the current screen mode\r
+ if ((c < 4) || (c == 7))\r
+ goto oh_kill_me;\r
+\r
+ // DEBUG - handle screen cleanup\r
+\r
+ US_SaveWindow(&wr);\r
+ US_CenterWindow(30,3);\r
+ US_CPrint(s);\r
+ US_CPrint("(R)etry or (A)bort?");\r
+ VW_UpdateScreen();\r
+ IN_ClearKeysDown();\r
+\r
+asm sti // Let the keyboard interrupts come through\r
+\r
+ while (true)\r
+ {\r
+ switch (IN_WaitForASCII())\r
+ {\r
+ case key_Escape:\r
+ case 'a':\r
+ case 'A':\r
+ goto oh_kill_me;\r
+ break;\r
+ case key_Return:\r
+ case key_Space:\r
+ case 'r':\r
+ case 'R':\r
+ US_ClearWindow();\r
+ VW_UpdateScreen();\r
+ US_RestoreWindow(&wr);\r
+ return(RETRY);\r
+ break;\r
+ }\r
+ }\r
+\r
+oh_kill_me:\r
+ abortprogram = s;\r
+ ShutdownId();\r
+ fprintf(stderr,"Terminal Error: %s\n",s);\r
+ if (tedlevel)\r
+ fprintf(stderr,"You launched from TED. I suggest that you reboot...\n");\r
+\r
+ return(ABORT);\r
+#undef IGNORE\r
+#undef RETRY\r
+#undef ABORT\r
+}\r
+#pragma warn +par\r
+#pragma warn +rch\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_GiveSaveName() - Returns a pointer to a static buffer that contains\r
+// the filename to use for the specified save game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char *\r
+USL_GiveSaveName(word game)\r
+{\r
+static char name[] = "SAVEGAMx."EXT;\r
+\r
+ name[7] = '0' + game;\r
+ return(name);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after\r
+// reading or writing the save game headers\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void))\r
+{\r
+ USL_LoadGame = load;\r
+ USL_SaveGame = save;\r
+ USL_ResetGame = reset;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ReadConfig() - Reads the configuration file, if present, and sets\r
+// things up accordingly. If it's not present, uses defaults. This file\r
+// includes the high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ReadConfig(void)\r
+{\r
+ boolean gotit;\r
+ char sig[sizeof(EXT)];\r
+ word version;\r
+ int file;\r
+ SDMode sd;\r
+ SMMode sm;\r
+ ControlType ctl;\r
+\r
+ if ((file = open("CONFIG."EXT,O_BINARY | O_RDONLY)) != -1)\r
+ {\r
+ read(file,sig,sizeof(EXT));\r
+ read(file,&version,sizeof(version));\r
+ if (strcmp(sig,EXT) || (version != ConfigVersion))\r
+ {\r
+ close(file);\r
+ goto rcfailed;\r
+ }\r
+ read(file,Scores,sizeof(HighScore) * MaxScores);\r
+ read(file,&sd,sizeof(sd));\r
+ read(file,&sm,sizeof(sm));\r
+ read(file,&ctl,sizeof(ctl));\r
+ read(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+ read(file,&showscorebox,sizeof(showscorebox));\r
+ read(file,&compatability,sizeof(compatability));\r
+#ifdef KEEN\r
+ read(file,&oldshooting,sizeof(oldshooting));\r
+ read(file,&firescan,sizeof(firescan));\r
+#endif\r
+ close(file);\r
+\r
+ HighScoresDirty = false;\r
+ gotit = true;\r
+ }\r
+ else\r
+ {\r
+rcfailed:\r
+ sd = sdm_Off;\r
+ sm = smm_Off;\r
+ ctl = ctrl_Keyboard;\r
+ showscorebox = true;\r
+#ifdef KEEN\r
+ oldshooting = false;\r
+#endif\r
+\r
+ gotit = false;\r
+ HighScoresDirty = true;\r
+ }\r
+\r
+ SD_Default(gotit,sd,sm);\r
+ IN_Default(gotit,ctl);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_WriteConfig() - Writes out the current configuration, including the\r
+// high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_WriteConfig(void)\r
+{\r
+ word version;\r
+ int file;\r
+\r
+ version = ConfigVersion;\r
+ file = open("CONFIG."EXT,O_CREAT | O_BINARY | O_WRONLY,\r
+ S_IREAD | S_IWRITE | S_IFREG);\r
+ if (file != -1)\r
+ {\r
+ write(file,EXT,sizeof(EXT));\r
+ write(file,&version,sizeof(version));\r
+ write(file,Scores,sizeof(HighScore) * MaxScores);\r
+ write(file,&SoundMode,sizeof(SoundMode));\r
+ write(file,&MusicMode,sizeof(MusicMode));\r
+ if // Hack\r
+ (\r
+ (Controls[0] == ctrl_Joystick1)\r
+ || (Controls[0] == ctrl_Joystick2)\r
+ )\r
+ Controls[0] = ctrl_Keyboard;\r
+ write(file,&(Controls[0]),sizeof(Controls[0]));\r
+ write(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+ write(file,&showscorebox,sizeof(showscorebox));\r
+ write(file,&compatability,sizeof(compatability));\r
+#ifdef KEEN\r
+ write(file,&oldshooting,sizeof(oldshooting));\r
+ write(file,&firescan,sizeof(firescan));\r
+#endif\r
+ close(file);\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_CheckSavedGames() - Checks to see which saved games are present\r
+// & valid\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_CheckSavedGames(void)\r
+{\r
+ boolean ok;\r
+ char *filename;\r
+ word i;\r
+ int file;\r
+ SaveGame *game;\r
+\r
+ USL_SaveGame = 0;\r
+ USL_LoadGame = 0;\r
+\r
+ for (i = 0,game = Games;i < MaxSaveGames;i++,game++)\r
+ {\r
+ filename = USL_GiveSaveName(i);\r
+ ok = false;\r
+ if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+ {\r
+ if\r
+ (\r
+ (read(file,game,sizeof(*game)) == sizeof(*game))\r
+ && (!strcmp(game->signature,EXT))\r
+ && (game->oldtest == &PrintX)\r
+ )\r
+ ok = true;\r
+\r
+ close(file);\r
+ }\r
+\r
+ if (ok)\r
+ game->present = true;\r
+ else\r
+ {\r
+ strcpy(game->signature,EXT);\r
+ game->present = false;\r
+ strcpy(game->name,"Empty");\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Startup() - Starts the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Startup(void)\r
+{\r
+ int i;\r
+\r
+ if (US_Started)\r
+ return;\r
+\r
+ harderr(USL_HardError); // Install the fatal error handler\r
+\r
+ US_InitRndT(true); // Initialize the random number generator\r
+\r
+ USL_ReadConfig(); // Read config file\r
+\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ switch (US_CheckParm(_argv[i],ParmStrings2))\r
+ {\r
+ case 0:\r
+ if (grmode == EGAGR)\r
+ compatability = true;\r
+ break;\r
+ case 1:\r
+ compatability = false;\r
+ break;\r
+ }\r
+ }\r
+\r
+ US_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Setup() - Does the disk access part of the User Mgr's startup\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Setup(void)\r
+{\r
+ USL_CheckSavedGames(); // Check which saved games are present\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Shutdown() - Shuts down the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Shutdown(void)\r
+{\r
+ if (!US_Started)\r
+ return;\r
+\r
+ if (!abortprogram)\r
+ USL_WriteConfig();\r
+\r
+ US_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CheckParm() - checks to see if a string matches one of a set of\r
+// strings. The check is case insensitive. The routine returns the\r
+// index of the string that matched, or -1 if no matches were found\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+int\r
+US_CheckParm(char *parm,char **strings)\r
+{\r
+ char cp,cs,\r
+ *p,*s;\r
+ int i;\r
+\r
+ while (!isalpha(*parm)) // Skip non-alphas\r
+ parm++;\r
+\r
+ for (i = 0;*strings && **strings;i++)\r
+ {\r
+ for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)\r
+ {\r
+ cs = *s++;\r
+ if (!cs)\r
+ return(i);\r
+ cp = *p++;\r
+\r
+ if (isupper(cs))\r
+ cs = tolower(cs);\r
+ if (isupper(cp))\r
+ cp = tolower(cp);\r
+ }\r
+ }\r
+ return(-1);\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ScreenDraw() - Draws a chunk of the text screen (called only by\r
+// US_TextScreen())\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ScreenDraw(word x,word y,char *s,byte attr)\r
+{\r
+ byte far *screen,far *oscreen;\r
+\r
+ screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2));\r
+ oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) + 1;\r
+ while (*s)\r
+ {\r
+ *screen++ = *s++;\r
+ if (attr != 0xff)\r
+ {\r
+ *screen++ = (attr & 0x8f) | (*oscreen & 0x70);\r
+ oscreen += 2;\r
+ }\r
+ else\r
+ screen++;\r
+ }\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it,\r
+// and moves the cursor to the leftmost column of the bottom line\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ClearTextScreen(void)\r
+{\r
+ // Set to 80x25 color text mode\r
+ _AL = 3; // Mode 3\r
+ _AH = 0x00;\r
+ geninterrupt(0x10);\r
+\r
+ // Use BIOS to move the cursor to the bottom of the screen\r
+ _AH = 0x0f;\r
+ geninterrupt(0x10); // Get current video mode into _BH\r
+ _DL = 0; // Lefthand side of the screen\r
+ _DH = 24; // Bottom row\r
+ _AH = 0x02;\r
+ geninterrupt(0x10);\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_TextScreen() - Puts up the startup text screen\r
+// Note: These are the only User Manager functions that can be safely called\r
+// before the User Mgr has been started up\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_TextScreen(void)\r
+{\r
+ word i,n;\r
+\r
+ USL_ClearTextScreen();\r
+\r
+ _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2);\r
+\r
+ // Check for TED launching here\r
+ for (i = 1;i < _argc;i++)\r
+ {\r
+ n = US_CheckParm(_argv[i],ParmStrings);\r
+ if (n == 0)\r
+ {\r
+ tedlevelnum = atoi(_argv[i + 1]);\r
+ if (tedlevelnum >= 0)\r
+ {\r
+ tedlevel = true;\r
+ return;\r
+ }\r
+ else\r
+ break;\r
+ }\r
+ else if (n == 1)\r
+ {\r
+ NoWait = true;\r
+ return;\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_Show() - Changes the appearance of one of the fields on the text\r
+// screen. Possibly adds a checkmark in front of it and highlights it\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_Show(word x,word y,word w,boolean show,boolean hilight)\r
+{\r
+ byte far *screen,far *oscreen;\r
+\r
+ screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2));\r
+ oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) - 1;\r
+ *screen++ = show? 251 : ' '; // Checkmark char or space\r
+// *screen = 0x48;\r
+// *screen = (*oscreen & 0xf0) | 8;\r
+ oscreen += 2;\r
+ if (show && hilight)\r
+ {\r
+ for (w++;w--;screen += 2,oscreen += 2)\r
+ *screen = (*oscreen & 0xf0) | 0x0f;\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_ShowMem() - Right justifies a longword in one of the memory fields on\r
+// the text screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ShowMem(word x,word y,long mem)\r
+{\r
+ char buf[16];\r
+ word i;\r
+\r
+ for (i = strlen(ltoa(mem,buf,10));i < 5;i++)\r
+ USL_ScreenDraw(x++,y," ",0xff);\r
+ USL_ScreenDraw(x,y,buf,0xff);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_UpdateTextScreen() - Called after the ID libraries are started up.\r
+// Displays what hardware is present.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_UpdateTextScreen(void)\r
+{\r
+ boolean b;\r
+ longword totalmem;\r
+\r
+ // Show video card info\r
+ b = (grmode == CGAGR);\r
+ USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b);\r
+ b = (grmode == EGAGR);\r
+ USL_Show(21,8,4,(videocard >= EGAcard) && (videocard <= VGAcard),b);\r
+ b = (grmode == VGAGR);\r
+ USL_Show(21,9,4,videocard == VGAcard,b);\r
+ if (compatability)\r
+ USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f);\r
+\r
+ // Show input device info\r
+ USL_Show(60,7,8,true,true);\r
+ USL_Show(60,8,11,JoysPresent[0],true);\r
+ USL_Show(60,9,11,JoysPresent[1],true);\r
+ USL_Show(60,10,5,MousePresent,true);\r
+\r
+ // Show sound hardware info\r
+ USL_Show(21,14,11,true,SoundMode == sdm_PC);\r
+ b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib);\r
+ USL_Show(21,15,14,AdLibPresent,b);\r
+ if (b && AdLibPresent) // Hack because of two lines\r
+ {\r
+ byte far *screen,far *oscreen;\r
+ word x,y,w;\r
+\r
+ x = 21;\r
+ y = 16;\r
+ w = 14;\r
+ screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2) - 1);\r
+ oscreen = (&introscn + 7) + (x * 2) + (y * 80 * 2) - 1;\r
+ oscreen += 2;\r
+ for (w++;w--;screen += 2,oscreen += 2)\r
+ *screen = (*oscreen & 0xf0) | 0x0f;\r
+ }\r
+\r
+ // Show memory available/used\r
+ USL_ShowMem(63,15,mminfo.mainmem / 1024);\r
+ USL_Show(53,15,23,true,true);\r
+ USL_ShowMem(63,16,mminfo.EMSmem / 1024);\r
+ USL_Show(53,16,23,mminfo.EMSmem? true : false,true);\r
+ USL_ShowMem(63,17,mminfo.XMSmem / 1024);\r
+ USL_Show(53,17,23,mminfo.XMSmem? true : false,true);\r
+ totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem;\r
+ USL_ShowMem(63,18,totalmem / 1024);\r
+ USL_Show(53,18,23,true,true); // DEBUG\r
+ USL_ScreenDraw(52,18," ",0xff);\r
+\r
+ // Change Initializing... to Loading...\r
+ USL_ScreenDraw(27,22," Loading... ",0x9c);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_FinishTextScreen() - After the main program has finished its initial\r
+// loading, this routine waits for a keypress and then clears the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_FinishTextScreen(void)\r
+{\r
+static byte colors[] = {4,6,13,15,15,15,15,15,15};\r
+ boolean up;\r
+ int i,c;\r
+\r
+ // Change Loading... to Press a Key\r
+\r
+ if (!(tedlevel || NoWait))\r
+ {\r
+ IN_ClearKeysDown();\r
+ for (i = 0,up = true;!IN_UserInput(4,true);)\r
+ {\r
+ c = colors[i];\r
+ if (up)\r
+ {\r
+ if (++i == 9)\r
+ i = 8,up = false;\r
+ }\r
+ else\r
+ {\r
+ if (--i < 0)\r
+ i = 1,up = true;\r
+ }\r
+\r
+ USL_ScreenDraw(29,22," Ready - Press a Key ",0x00 + c);\r
+ }\r
+ }\r
+ else\r
+ USL_ScreenDraw(29,22," Ready - Press a Key ",0x9a);\r
+ IN_ClearKeysDown();\r
+\r
+ USL_ClearTextScreen();\r
+}\r
+#endif\r
+\r
+// Window/Printing routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_SetPrintRoutines() - Sets the routines used to measure and print\r
+// from within the User Mgr. Primarily provided to allow switching\r
+// between masked and non-masked fonts\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *))\r
+{\r
+ USL_MeasureString = measure;\r
+ USL_DrawString = print;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Print() - Prints a string in the current window. Newlines are\r
+// supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Print(char *s)\r
+{\r
+ char c,*se;\r
+ word w,h;\r
+\r
+ while (*s)\r
+ {\r
+ se = s;\r
+ while ((c = *se) && (c != '\n'))\r
+ se++;\r
+ *se = '\0';\r
+\r
+ USL_MeasureString(s,&w,&h);\r
+ px = PrintX;\r
+ py = PrintY;\r
+ USL_DrawString(s);\r
+\r
+ s = se;\r
+ if (c)\r
+ {\r
+ *se = c;\r
+ s++;\r
+\r
+ PrintX = WindowX;\r
+ PrintY += h;\r
+ }\r
+ else\r
+ PrintX += w;\r
+ }\r
+}\r
+\r
+// MDM - (GAMERS EDGE) begin\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Printxy()\r
+//\r
+void US_Printxy(word x, word y, char *text)\r
+{\r
+ word orgx, orgy;\r
+\r
+ orgx = PrintX;\r
+ orgy = PrintY;\r
+\r
+// PrintX = WindowX+x;\r
+// PrintY = WindowY+y;\r
+ PrintX = x;\r
+ PrintY = y;\r
+ US_Print(text);\r
+\r
+ PrintX = orgx;\r
+ PrintY = orgy;\r
+}\r
+\r
+// MDM - (GAMERS EDGE) end\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_PrintUnsigned() - Prints an unsigned long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintUnsigned(longword n)\r
+{\r
+ char buffer[32];\r
+\r
+ US_Print(ultoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_PrintSigned() - Prints a signed long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintSigned(long n)\r
+{\r
+ char buffer[32];\r
+\r
+ US_Print(ltoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_PrintInCenter() - Prints a string in the center of the given rect\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+USL_PrintInCenter(char *s,Rect r)\r
+{\r
+ word w,h,\r
+ rw,rh;\r
+\r
+ USL_MeasureString(s,&w,&h);\r
+ rw = r.lr.x - r.ul.x;\r
+ rh = r.lr.y - r.ul.y;\r
+\r
+ px = r.ul.x + ((rw - w) / 2);\r
+ py = r.ul.y + ((rh - h) / 2);\r
+ USL_DrawString(s);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_PrintCentered() - Prints a string centered in the current window.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintCentered(char *s)\r
+{\r
+ Rect r;\r
+\r
+ r.ul.x = WindowX;\r
+ r.ul.y = WindowY;\r
+ r.lr.x = r.ul.x + WindowW;\r
+ r.lr.y = r.ul.y + WindowH;\r
+\r
+ USL_PrintInCenter(s,r);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CPrintLine() - Prints a string centered on the current line and\r
+// advances to the next line. Newlines are not supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrintLine(char *s)\r
+{\r
+ word w,h;\r
+\r
+ USL_MeasureString(s,&w,&h);\r
+\r
+ if (w > WindowW)\r
+ Quit("US_CPrintLine() - String exceeds width");\r
+ px = WindowX + ((WindowW - w) / 2);\r
+ py = PrintY;\r
+ USL_DrawString(s);\r
+ PrintY += h;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CPrint() - Prints a string in the current window. Newlines are\r
+// supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrint(char *s)\r
+{\r
+ char c,*se;\r
+\r
+ while (*s)\r
+ {\r
+ se = s;\r
+ while ((c = *se) && (c != '\n'))\r
+ se++;\r
+ *se = '\0';\r
+\r
+ US_CPrintLine(s);\r
+\r
+ s = se;\r
+ if (c)\r
+ {\r
+ *se = c;\r
+ s++;\r
+ }\r
+ }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_ClearWindow() - Clears the current window to white and homes the\r
+// cursor\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ClearWindow(void)\r
+{\r
+ VWB_Bar(WindowX,WindowY,WindowW,WindowH,LT_GREY);\r
+ PrintX = WindowX;\r
+ PrintY = WindowY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_DrawWindow() - Draws a frame and sets the current window parms\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_DrawWindow(word x,word y,word w,word h)\r
+{\r
+ word i,\r
+ sx,sy,sw,sh;\r
+\r
+ WindowX = x * 8;\r
+ WindowY = y * 8;\r
+ WindowW = w * 8;\r
+ WindowH = h * 8;\r
+\r
+ PrintX = WindowX;\r
+ PrintY = WindowY;\r
+\r
+ sx = (x - 1) * 8;\r
+ sy = (y - 1) * 8;\r
+ sw = (w + 1) * 8;\r
+ sh = (h + 1) * 8;\r
+\r
+ US_ClearWindow();\r
+\r
+ VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6);\r
+ for (i = sx + 8;i <= sx + sw - 8;i += 8)\r
+ VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7);\r
+ VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8);\r
+\r
+ for (i = sy + 8;i <= sy + sh - 8;i += 8)\r
+ VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CenterWindow() - Generates a window of a given width & height in the\r
+// middle of the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterWindow(word w,word h)\r
+{\r
+ US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_CenterSaveWindow() - Generates a window of a given width & height in\r
+// the middle of the screen, saving the background\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterSaveWindow(word w,word h,memptr *save)\r
+{\r
+ word x,y,\r
+ screen;\r
+\r
+ x = ((MaxX / 8) - w) / 2;\r
+ y = ((MaxY / 8) - h) / 2;\r
+ MM_GetPtr(save,(w * h) * CHARWIDTH);\r
+ screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH);\r
+ VW_ScreenToMem(screen,*save,w * CHARWIDTH,h);\r
+ US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_RestoreSaveWindow() - Restores the background of the size of the\r
+// current window from the memory specified by save\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreSaveWindow(memptr *save)\r
+{\r
+ word screen;\r
+\r
+ screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH);\r
+ VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH);\r
+ MM_FreePtr(save);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_SaveWindow() - Saves the current window parms into a record for\r
+// later restoration\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SaveWindow(WindowRec *win)\r
+{\r
+ win->x = WindowX;\r
+ win->y = WindowY;\r
+ win->w = WindowW;\r
+ win->h = WindowH;\r
+\r
+ win->px = PrintX;\r
+ win->py = PrintY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_RestoreWindow() - Sets the current window parms to those held in the\r
+// record\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreWindow(WindowRec *win)\r
+{\r
+ WindowX = win->x;\r
+ WindowY = win->y;\r
+ WindowW = win->w;\r
+ WindowH = win->h;\r
+\r
+ PrintX = win->px;\r
+ PrintY = win->py;\r
+}\r
+\r
+// Cursor routines\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_StartCursor() - Sets up the cursor for User Mgr use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_StartCursor(void)\r
+{\r
+ CursorInfo info;\r
+\r
+ VW_SetCursor(CURSORARROWSPR);\r
+ CursorX = MaxX / 2;\r
+ CursorY = MaxY / 2;\r
+ VW_MoveCursor(CursorX,CursorY);\r
+ VW_ShowCursor();\r
+\r
+ IN_ReadCursor(&info); // Dispose of any accumulated movement\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_ShutCursor() - Cleans up after US_StartCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ShutCursor(void)\r
+{\r
+ VW_HideCursor();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_UpdateCursor() - Gets the new cursor position & button states from\r
+// the Input Mgr and tells the View Mgr where the cursor is\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_UpdateCursor(void)\r
+{\r
+ CursorInfo info;\r
+\r
+ IN_ReadCursor(&info);\r
+ if (info.x || info.y || CursorBad)\r
+ {\r
+ CursorX += info.x;\r
+ if (CursorX >= MaxX)\r
+ CursorX = MaxX - 1;\r
+ else if (CursorX < 0)\r
+ CursorX = 0;\r
+\r
+ CursorY += info.y;\r
+ if (CursorY >= MaxY)\r
+ CursorY = MaxY - 1;\r
+ else if (CursorY < 0)\r
+ CursorY = 0;\r
+\r
+ VW_MoveCursor(CursorX,CursorY);\r
+ CursorBad = false;\r
+ }\r
+ Button0 = info.button0;\r
+ Button1 = info.button1;\r
+ return(Button0 || Button1);\r
+}\r
+#endif\r
+\r
+// Input routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_XORICursor(int x,int y,char *s,word cursor)\r
+{\r
+ char buf[MaxString];\r
+ word w,h;\r
+\r
+ strcpy(buf,s);\r
+ buf[cursor] = '\0';\r
+ USL_MeasureString(buf,&w,&h);\r
+\r
+ px = x + w - 1;\r
+ py = y;\r
+ USL_DrawString("\x80");\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_LineInput() - Gets a line of user input at (x,y), the string defaults\r
+// to whatever is pointed at by def. Input is restricted to maxchars\r
+// chars or maxwidth pixels wide. If the user hits escape (and escok is\r
+// true), nothing is copied into buf, and false is returned. If the\r
+// user hits return, the current string is copied into buf, and true is\r
+// returned\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_LineInput(int x,int y,char *buf,char *def,boolean escok,\r
+ int maxchars,int maxwidth)\r
+{\r
+ boolean redraw,\r
+ cursorvis,cursormoved,\r
+ done,result;\r
+ ScanCode sc;\r
+ char c,\r
+ s[MaxString],olds[MaxString];\r
+ word i,\r
+ cursor,\r
+ w,h,\r
+ len;\r
+ longword lasttime;\r
+\r
+ VW_HideCursor();\r
+\r
+ if (def)\r
+ strcpy(s,def);\r
+ else\r
+ *s = '\0';\r
+ *olds = '\0';\r
+ cursor = strlen(s);\r
+ cursormoved = redraw = true;\r
+\r
+ cursorvis = done = false;\r
+ lasttime = TimeCount;\r
+ LastASCII = key_None;\r
+ LastScan = sc_None;\r
+\r
+ while (!done)\r
+ {\r
+ if (cursorvis)\r
+ USL_XORICursor(x,y,s,cursor);\r
+\r
+ asm pushf\r
+ asm cli\r
+\r
+ sc = LastScan;\r
+ LastScan = sc_None;\r
+ c = LastASCII;\r
+ LastASCII = key_None;\r
+\r
+ asm popf\r
+\r
+ switch (sc)\r
+ {\r
+ case sc_LeftArrow:\r
+ if (cursor)\r
+ cursor--;\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+ case sc_RightArrow:\r
+ if (s[cursor])\r
+ cursor++;\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+ case sc_Home:\r
+ cursor = 0;\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+ case sc_End:\r
+ cursor = strlen(s);\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+\r
+ case sc_Return:\r
+ strcpy(buf,s);\r
+ done = true;\r
+ result = true;\r
+ c = key_None;\r
+ break;\r
+ case sc_Escape:\r
+ if (escok)\r
+ {\r
+ done = true;\r
+ result = false;\r
+ }\r
+ c = key_None;\r
+ break;\r
+\r
+ case sc_BackSpace:\r
+ if (cursor)\r
+ {\r
+ strcpy(s + cursor - 1,s + cursor);\r
+ cursor--;\r
+ redraw = true;\r
+ }\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+ case sc_Delete:\r
+ if (s[cursor])\r
+ {\r
+ strcpy(s + cursor,s + cursor + 1);\r
+ redraw = true;\r
+ }\r
+ c = key_None;\r
+ cursormoved = true;\r
+ break;\r
+\r
+ case 0x4c: // Keypad 5\r
+ case sc_UpArrow:\r
+ case sc_DownArrow:\r
+ case sc_PgUp:\r
+ case sc_PgDn:\r
+ case sc_Insert:\r
+ c = key_None;\r
+ break;\r
+ }\r
+\r
+ if (c)\r
+ {\r
+ len = strlen(s);\r
+ USL_MeasureString(s,&w,&h);\r
+\r
+ if\r
+ (\r
+ isprint(c)\r
+ && (len < MaxString - 1)\r
+ && ((!maxchars) || (len < maxchars))\r
+ && ((!maxwidth) || (w < maxwidth))\r
+ )\r
+ {\r
+ for (i = len + 1;i > cursor;i--)\r
+ s[i] = s[i - 1];\r
+ s[cursor++] = c;\r
+ redraw = true;\r
+ }\r
+ }\r
+\r
+ if (redraw)\r
+ {\r
+ px = x;\r
+ py = y;\r
+ USL_DrawString(olds);\r
+ strcpy(olds,s);\r
+\r
+ px = x;\r
+ py = y;\r
+ USL_DrawString(s);\r
+\r
+ redraw = false;\r
+ }\r
+\r
+ if (cursormoved)\r
+ {\r
+ cursorvis = false;\r
+ lasttime = TimeCount - TickBase;\r
+\r
+ cursormoved = false;\r
+ }\r
+ if (TimeCount - lasttime > TickBase / 2)\r
+ {\r
+ lasttime = TimeCount;\r
+\r
+ cursorvis ^= true;\r
+ }\r
+ if (cursorvis)\r
+ USL_XORICursor(x,y,s,cursor);\r
+\r
+ VW_UpdateScreen();\r
+ }\r
+\r
+ if (cursorvis)\r
+ USL_XORICursor(x,y,s,cursor);\r
+ if (!result)\r
+ {\r
+ px = x;\r
+ py = y;\r
+ USL_DrawString(olds);\r
+ }\r
+ VW_ShowCursor();\r
+ VW_UpdateScreen();\r
+\r
+ IN_ClearKeysDown();\r
+ return(result);\r
+}\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// ID Engine\r
+// ID_US.c - User Manager - User interface\r
+// v1.1d1\r
+// By Jason Blochowiak\r
+// Hacked up for Catacomb 3D\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+#pragma warn -pia\r
+\r
+// Special imports\r
+extern boolean showscorebox;\r
+#ifdef KEEN\r
+extern boolean oldshooting;\r
+extern ScanCode firescan;\r
+#else\r
+ ScanCode firescan;\r
+#endif\r
+\r
+// Global variables\r
+ boolean ingame,abortgame,loadedgame;\r
+ GameDiff restartgame = gd_Continue;\r
+\r
+// Internal variables\r
+static boolean GameIsDirty,\r
+ QuitToDos,\r
+ CtlPanelDone;\r
+\r
+// Forward reference prototypes\r
+static void USL_SetupCard(void);\r
+\r
+// Control panel data\r
+\r
+#define CtlPanelSX 74\r
+#define CtlPanelSY 48\r
+#define CtlPanelEX 234\r
+#define CtlPanelEY 150\r
+#define CtlPanelW (CtlPanelEX - CtlPanelSX)\r
+#define CtlPanelH (CtlPanelEY - CtlPanelSY)\r
+\r
+#define TileBase 92\r
+\r
+// DEBUG - CGA\r
+#define BackColor 0\r
+#define HiliteColor (BackColor ^ 12)\r
+#define NohiliteColor (BackColor ^ 4)\r
+\r
+typedef enum\r
+ {\r
+ uc_None,\r
+ uc_Return,\r
+ uc_Abort,\r
+ uc_Quit,\r
+ uc_Loaded,\r
+ uc_SEasy,\r
+ uc_SNormal,\r
+ uc_SHard,\r
+ } UComm;\r
+typedef enum\r
+ {\r
+ uii_Bad,\r
+ uii_Button,uii_RadioButton,uii_Folder\r
+ } UIType;\r
+typedef enum\r
+ {\r
+ ui_Normal = 0,\r
+ ui_Pushed = 1,\r
+ ui_Selected = 2,\r
+ ui_Disabled = 4,\r
+ ui_Separated = 8\r
+ } UIFlags;\r
+#define UISelectFlags (ui_Pushed | ui_Selected | ui_Disabled)\r
+\r
+typedef enum\r
+ {\r
+ uic_SetupCard,uic_DrawCard,uic_TouchupCard,\r
+ uic_DrawIcon,uic_Draw,uic_Hit\r
+ } UserCall;\r
+\r
+typedef struct UserItem\r
+ {\r
+ UIType type;\r
+ UIFlags flags;\r
+ ScanCode hotkey;\r
+ char *text;\r
+ UComm comm;\r
+ void far *child; // Should be (UserItemGroup *)\r
+\r
+ word x,y;\r
+ } UserItem;\r
+typedef struct UserItemGroup\r
+ {\r
+ word x,y;\r
+ graphicnums title;\r
+ ScanCode hotkey;\r
+ UserItem far *items;\r
+ boolean (*custom)(UserCall,struct UserItem far *); // Custom routine\r
+\r
+ word cursor;\r
+ struct UserItemGroup far *parent;\r
+ } UserItemGroup;\r
+\r
+static char *BottomS1,*BottomS2,*BottomS3;\r
+static UComm Communication;\r
+static ScanCode *KeyMaps[] =\r
+ {\r
+ &KbdDefs[0].button0,\r
+ &KbdDefs[0].button1,\r
+ &firescan,\r
+ &KbdDefs[0].upleft,\r
+ &KbdDefs[0].up,\r
+ &KbdDefs[0].upright,\r
+ &KbdDefs[0].right,\r
+ &KbdDefs[0].downright,\r
+ &KbdDefs[0].down,\r
+ &KbdDefs[0].downleft,\r
+ &KbdDefs[0].left\r
+ };\r
+\r
+// Custom routine prototypes\r
+static boolean USL_ConfigCustom(UserCall call,struct UserItem far *item),\r
+ USL_KeyCustom(UserCall call,struct UserItem far *item),\r
+ USL_KeySCustom(UserCall call,struct UserItem far *item),\r
+ USL_Joy1Custom(UserCall call,struct UserItem far *item),\r
+ USL_Joy2Custom(UserCall call,struct UserItem far *item),\r
+ USL_LoadCustom(UserCall call,struct UserItem far *item),\r
+ USL_SaveCustom(UserCall call,struct UserItem far *item),\r
+ USL_ScoreCustom(UserCall call,struct UserItem far *item),\r
+ USL_CompCustom(UserCall call,struct UserItem far *item);\r
+#ifdef KEEN\r
+ USL_TwoCustom(UserCall call,struct UserItem far *item),\r
+#endif\r
+// USL_PongCustom(UserCall call,struct UserItem far *item);\r
+\r
+#define DefButton(key,text) uii_Button,ui_Normal,key,text\r
+#define DefRButton(key,text) uii_RadioButton,ui_Normal,key,text\r
+#define DefFolder(key,text,child) uii_Folder,ui_Normal,key,text,uc_None,child\r
+#define CustomGroup(title,key,custom) 0,0,title,key,0,custom\r
+ UserItem far holder[] =\r
+ {\r
+ {DefButton(sc_None,"DEBUG")},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far holdergroup = {0,0,CP_MAINMENUPIC,sc_None,holder};\r
+\r
+ // Sound menu\r
+ UserItem far soundi[] =\r
+ {\r
+ {DefRButton(sc_N,"NO SOUND EFFECTS")},\r
+ {DefRButton(sc_P,"PC SPEAKER")},\r
+ {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far soundgroup = {8,0,CP_SOUNDMENUPIC,sc_None,soundi};\r
+\r
+ // Music menu\r
+ UserItem far musici[] =\r
+ {\r
+ {DefRButton(sc_N,"NO MUSIC")},\r
+ {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far musicgroup = {8,0,CP_MUSICMENUPIC,sc_None,musici};\r
+\r
+ // New game menu\r
+ UserItem far newgamei[] =\r
+ {\r
+ {DefButton(sc_E,"BEGIN EASY GAME"),uc_SEasy},\r
+ {DefButton(sc_N,"BEGIN NORMAL GAME"),uc_SNormal},\r
+ {DefButton(sc_H,"BEGIN HARD GAME"),uc_SHard},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far newgamegroup = {8,0,CP_NEWGAMEMENUPIC,sc_None,newgamei,0,1};\r
+\r
+ // Load/Save game menu\r
+ UserItem far loadsavegamei[] =\r
+ {\r
+ {uii_Button,ui_Normal,sc_None},\r
+ {uii_Button,ui_Normal,sc_None},\r
+ {uii_Button,ui_Normal,sc_None},\r
+ {uii_Button,ui_Normal,sc_None},\r
+ {uii_Button,ui_Normal,sc_None},\r
+ {uii_Button,ui_Normal,sc_None},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far loadgamegroup = {4,3,CP_LOADMENUPIC,sc_None,loadsavegamei,USL_LoadCustom};\r
+ UserItemGroup far savegamegroup = {4,3,CP_SAVEMENUPIC,sc_None,loadsavegamei,USL_SaveCustom};\r
+\r
+ // Options menu\r
+ UserItemGroup far scoregroup = {0,0,0,sc_None,0,USL_ScoreCustom};\r
+ UserItemGroup far compgroup = {0,0,0,sc_None,0,USL_CompCustom};\r
+#ifdef KEEN\r
+ UserItemGroup far twogroup = {0,0,0,sc_None,0,USL_TwoCustom};\r
+#endif\r
+ UserItem far optionsi[] =\r
+ {\r
+ {DefFolder(sc_S,"",&scoregroup)},\r
+ {DefFolder(sc_C,"",&compgroup)},\r
+#ifdef KEEN\r
+ {DefFolder(sc_T,"",&twogroup)},\r
+#endif\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far optionsgroup = {8,0,CP_OPTIONSMENUPIC,sc_None,optionsi};\r
+\r
+ // Keyboard menu\r
+ UserItem far keyi[] =\r
+ {\r
+ {DefButton(sc_None,"UP & LEFT")},\r
+ {DefButton(sc_None,"UP")},\r
+ {DefButton(sc_None,"UP & RIGHT")},\r
+ {DefButton(sc_None,"RIGHT")},\r
+ {DefButton(sc_None,"DOWN & RIGHT")},\r
+ {DefButton(sc_None,"DOWN")},\r
+ {DefButton(sc_None,"DOWN & LEFT")},\r
+ {DefButton(sc_None,"LEFT")},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far keygroup = {0,0,CP_KEYMOVEMENTPIC,sc_None,keyi,USL_KeyCustom};\r
+ UserItem far keybi[] =\r
+ {\r
+#ifdef KEEN\r
+ {DefButton(sc_J,"JUMP")},\r
+ {DefButton(sc_P,"POGO")},\r
+ {DefButton(sc_F,"FIRE")},\r
+#endif\r
+#ifdef CAT3D\r
+ {DefButton(sc_J,"FIRE")},\r
+ {DefButton(sc_P,"STRAFE")},\r
+#endif\r
+#ifdef CPD\r
+ {DefButton(sc_J,"SHOOT")},\r
+ {DefButton(sc_P,"BOMB")},\r
+#endif\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far keybgroup = {0,0,CP_KEYBUTTONPIC,sc_None,keybi,USL_KeyCustom};\r
+ UserItem far keysi[] =\r
+ {\r
+ {DefFolder(sc_M,"MOVEMENT",&keygroup)},\r
+ {DefFolder(sc_B,"BUTTONS",&keybgroup)},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far keysgroup = {8,0,CP_KEYBOARDMENUPIC,sc_None,keysi,USL_KeySCustom};\r
+\r
+ // Joystick #1 & #2\r
+ UserItemGroup far joy1group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy1Custom)};\r
+ UserItemGroup far joy2group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy2Custom)};\r
+\r
+ // Config menu\r
+ UserItem far configi[] =\r
+ {\r
+ {DefFolder(sc_S,"SOUND",&soundgroup)},\r
+ {DefFolder(sc_M,"MUSIC",&musicgroup)},\r
+ {uii_Folder,ui_Separated,sc_K,"USE KEYBOARD",uc_None,&keysgroup},\r
+ {DefFolder(sc_None,"USE JOYSTICK #1",&joy1group)},\r
+ {DefFolder(sc_None,"USE JOYSTICK #2",&joy2group)},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far configgroup = {8,0,CP_CONFIGMENUPIC,sc_None,configi,USL_ConfigCustom};\r
+\r
+ // Main menu\r
+// UserItemGroup far ponggroup = {0,0,0,sc_None,0,USL_PongCustom};\r
+ UserItem far rooti[] =\r
+ {\r
+ {DefFolder(sc_N,"NEW GAME",&newgamegroup)},\r
+ {DefFolder(sc_L,"LOAD GAME",&loadgamegroup)},\r
+ {DefFolder(sc_S,"SAVE GAME",&savegamegroup)},\r
+ {DefFolder(sc_C,"CONFIGURE",&configgroup)},\r
+ {DefButton(sc_R,nil),uc_Return}, // Return to Game/Demo\r
+ {DefButton(sc_E,"END GAME"),uc_Abort},\r
+// {DefFolder(sc_B,"SKULL 'N' BONES",&ponggroup)},\r
+ {DefButton(sc_Q,"QUIT"),uc_Quit},\r
+ {uii_Bad}\r
+ };\r
+ UserItemGroup far rootgroup = {32,4,CP_MAINMENUPIC,sc_None,rooti};\r
+#undef DefButton\r
+#undef DefFolder\r
+\r
+#define MaxCards 7\r
+ word cstackptr;\r
+ UserItemGroup far *cardstack[MaxCards],\r
+ far *topcard;\r
+\r
+// Card stack code\r
+static void\r
+USL_SetupStack(void)\r
+{\r
+ cstackptr = 0;\r
+ cardstack[0] = topcard = &rootgroup;\r
+}\r
+\r
+static void\r
+USL_PopCard(void)\r
+{\r
+ if (!cstackptr)\r
+ return;\r
+\r
+ topcard = cardstack[--cstackptr];\r
+}\r
+\r
+static void\r
+USL_PushCard(UserItemGroup far *card)\r
+{\r
+ if (cstackptr == MaxCards - 1)\r
+ return;\r
+\r
+ topcard = cardstack[++cstackptr] = card;\r
+}\r
+\r
+static void\r
+USL_DrawItemIcon(UserItem far *item)\r
+{\r
+ word flags,tile;\r
+\r
+ if (topcard->custom && topcard->custom(uic_DrawIcon,item))\r
+ return;\r
+\r
+ flags = item->flags;\r
+ if (flags & ui_Disabled)\r
+ tile = TileBase + ((flags & ui_Selected)? 5 : 4);\r
+ else if ((item->type == uii_RadioButton) && (!(flags & ui_Pushed)))\r
+ tile = TileBase + ((flags & ui_Selected)? 3 : 2);\r
+ else\r
+ tile = TileBase + ((flags & ui_Selected)? 1 : 0);\r
+ VWB_DrawTile8(item->x,item->y,tile);\r
+}\r
+\r
+static void\r
+USL_DrawItem(UserItem far *item)\r
+{\r
+ if (topcard->custom && topcard->custom(uic_Draw,item))\r
+ return;\r
+\r
+ VWB_Bar(CtlPanelSX + 1,item->y,\r
+ CtlPanelEX - CtlPanelSX - 1,8,BackColor); // Clear out background\r
+ USL_DrawItemIcon(item);\r
+ if ((item->flags & ui_Selected) && !(item->flags & ui_Disabled))\r
+ fontcolor = HiliteColor;\r
+ else\r
+ fontcolor = NohiliteColor;\r
+ px = item->x + 8;\r
+ py = item->y + 1;\r
+ USL_DrawString(item->text);\r
+ fontcolor = F_BLACK;\r
+}\r
+\r
+#define MyLine(y) VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,y,12);\r
+\r
+static void\r
+USL_DrawBottom(void)\r
+{\r
+ word w,h;\r
+\r
+ fontcolor = NohiliteColor;\r
+\r
+ px = CtlPanelSX + 4;\r
+ py = CtlPanelEY - 15;\r
+ USL_DrawString(BottomS1);\r
+\r
+ USL_MeasureString(BottomS2,&w,&h);\r
+ px = CtlPanelEX - 4 - w;\r
+ USL_DrawString(BottomS2);\r
+\r
+ USL_MeasureString(BottomS3,&w,&h);\r
+ px = CtlPanelSX + ((CtlPanelEX - CtlPanelSX - w) / 2);\r
+ py += h + 1;\r
+ USL_DrawString(BottomS3);\r
+\r
+ fontcolor = F_WHITE;\r
+ MyLine(CtlPanelEY - 17);\r
+}\r
+\r
+static void\r
+USL_DrawCtlPanelContents(void)\r
+{\r
+ int x,y;\r
+ UserItem far *item;\r
+\r
+ if (topcard->custom && topcard->custom(uic_DrawCard,nil))\r
+ return;\r
+\r
+ if (topcard->title)\r
+ {\r
+ // Draw the title\r
+ MyLine(CtlPanelSY + 7);\r
+ VWB_DrawPic(CtlPanelSX + 6,CtlPanelSY,topcard->title);\r
+ }\r
+\r
+ USL_DrawBottom();\r
+\r
+ if (!topcard->items)\r
+ return;\r
+\r
+ x = topcard->x + CtlPanelSX;\r
+ if (x % 8)\r
+ x += 8 - (x % 8);\r
+ y = topcard->y + CtlPanelSY + 12;\r
+ for (item = topcard->items;item->type != uii_Bad;item++)\r
+ {\r
+ if (item->flags & ui_Separated)\r
+ y += 8;\r
+\r
+ item->x = x;\r
+ item->y = y;\r
+ USL_DrawItem(item);\r
+ y += 8;\r
+ }\r
+ if (topcard->custom)\r
+ topcard->custom(uic_TouchupCard,nil);\r
+}\r
+\r
+static void\r
+USL_DrawCtlPanel(void)\r
+{\r
+ if (topcard->items || topcard->title)\r
+ {\r
+ // Draw the backdrop\r
+ VWB_DrawPic(0,0,CP_MENUSCREENPIC);\r
+\r
+ // Draw the contents\r
+ USL_DrawCtlPanelContents();\r
+ }\r
+\r
+ // Refresh the screen\r
+ VW_UpdateScreen();\r
+}\r
+\r
+static void\r
+USL_DialogSetup(word w,word h,word *x,word *y)\r
+{\r
+ VWB_DrawMPic(CtlPanelSX,CtlPanelSY,CP_MENUMASKPICM);\r
+\r
+ *x = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+ *y = CtlPanelSY + ((CtlPanelH - h) / 2);\r
+ VWB_Bar(*x,*y,w + 1,h + 1,BackColor);\r
+ VWB_Hlin(*x - 1,*x + w + 1,*y - 1,NohiliteColor);\r
+ VWB_Hlin(*x - 1,*x + w + 1,*y + h + 1,NohiliteColor);\r
+ VWB_Vlin(*y - 1,*y + h + 1,*x - 1,NohiliteColor);\r
+ VWB_Vlin(*y - 1,*y + h + 1,*x + w + 1,NohiliteColor);\r
+}\r
+\r
+static void\r
+USL_ShowLoadSave(char *s,char *name)\r
+{\r
+ word x,y,\r
+ w,h,\r
+ tw,sw;\r
+ char msg[MaxGameName + 4];\r
+\r
+ strcpy(msg,"'");\r
+ strcat(msg,name);\r
+ strcat(msg,"'");\r
+ USL_MeasureString(s,&sw,&h);\r
+ USL_MeasureString(msg,&w,&h);\r
+ tw = ((sw > w)? sw : w) + 6;\r
+ USL_DialogSetup(tw,(h * 2) + 2,&x,&y);\r
+ py = y + 2;\r
+ px = x + ((tw - sw) / 2);\r
+ USL_DrawString(s);\r
+ py += h;\r
+ px = x + ((tw - w) / 2);\r
+ USL_DrawString(msg);\r
+\r
+ VW_UpdateScreen();\r
+\r
+ IN_UserInput(100, true);\r
+}\r
+\r
+static boolean\r
+USL_CtlDialog(char *s1,char *s2,char *s3)\r
+{\r
+ word w,h,sh,\r
+ w1,w2,w3,\r
+ x,y;\r
+ ScanCode c;\r
+ CursorInfo cursorinfo;\r
+\r
+ USL_MeasureString(s1,&w1,&h);\r
+ USL_MeasureString(s2,&w2,&h);\r
+ if (s3)\r
+ USL_MeasureString(s3,&w3,&h);\r
+ else\r
+ w3 = 0;\r
+ w = (w1 > w2)? ((w1 > w3)? w1 : w3) : ((w2 > w3)? w2 : w3);\r
+ w += 7;\r
+ sh = h;\r
+ h *= s3? 5 : 4;\r
+\r
+ USL_DialogSetup(w,h,&x,&y);\r
+\r
+ fontcolor = HiliteColor;\r
+ px = x + ((w - w1) / 2);\r
+ py = y + sh + 1;\r
+ USL_DrawString(s1);\r
+ py += (sh * 2) - 1;\r
+\r
+ VWB_Hlin(x + 3,x + w - 3,py,NohiliteColor);\r
+ py += 2;\r
+\r
+ fontcolor = NohiliteColor;\r
+ px = x + ((w - w2) / 2);\r
+ USL_DrawString(s2);\r
+ py += sh;\r
+\r
+ if (s3)\r
+ {\r
+ px = x + ((w - w3) / 2);\r
+ USL_DrawString(s3);\r
+ }\r
+\r
+ VW_UpdateScreen();\r
+\r
+ IN_ClearKeysDown();\r
+ do\r
+ {\r
+ IN_ReadCursor(&cursorinfo);\r
+ if (cursorinfo.button0)\r
+ c = sc_Y;\r
+ else if (cursorinfo.button1)\r
+ c = sc_Escape;\r
+ else\r
+ c = LastScan;\r
+ } while (c == sc_None);\r
+ do\r
+ {\r
+ IN_ReadCursor(&cursorinfo);\r
+ } while (cursorinfo.button0 || cursorinfo.button1);\r
+\r
+ IN_ClearKeysDown();\r
+ USL_DrawCtlPanel();\r
+ return(c == sc_Y);\r
+}\r
+\r
+static boolean\r
+USL_ConfirmComm(UComm comm)\r
+{\r
+ boolean confirm,dialog;\r
+ char *s1,*s2,*s3;\r
+\r
+ if (!comm)\r
+ Quit("USL_ConfirmComm() - empty comm");\r
+\r
+ confirm = true;\r
+ dialog = false;\r
+ s3 = "ESC TO BACK OUT";\r
+ switch (comm)\r
+ {\r
+ case uc_Abort:\r
+ s1 = "REALLY END CURRENT GAME?";\r
+ s2 = "PRESS Y TO END IT";\r
+ if (ingame && GameIsDirty)\r
+ dialog = true;\r
+ break;\r
+ case uc_Quit:\r
+ s1 = "REALLY QUIT?";\r
+ s2 = "PRESS Y TO QUIT";\r
+ dialog = true;\r
+ break;\r
+ case uc_Loaded:\r
+ s1 = "YOU'RE IN A GAME";\r
+ s2 = "PRESS Y TO LOAD GAME";\r
+ if (ingame && GameIsDirty)\r
+ dialog = true;\r
+ break;\r
+ case uc_SEasy:\r
+ case uc_SNormal:\r
+ case uc_SHard:\r
+ s1 = "YOU'RE IN A GAME";\r
+ s2 = "PRESS Y FOR NEW GAME";\r
+ if (ingame && GameIsDirty)\r
+ dialog = true;\r
+ break;\r
+ }\r
+\r
+ confirm = dialog? USL_CtlDialog(s1,s2,s3) : true;\r
+ if (confirm)\r
+ {\r
+ Communication = comm;\r
+ CtlPanelDone = true;\r
+ }\r
+ return(confirm);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_HandleError() - Handles telling the user that there's been an error\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_HandleError(int num)\r
+{\r
+ char buf[64];\r
+\r
+ strcpy(buf,"Error: ");\r
+ if (num < 0)\r
+ strcat(buf,"Unknown");\r
+ else if (num == ENOMEM)\r
+ strcat(buf,"Disk is Full");\r
+ else if (num == EINVFMT)\r
+ strcat(buf,"File is Incomplete");\r
+ else\r
+ strcat(buf,sys_errlist[num]);\r
+\r
+ VW_HideCursor();\r
+\r
+ USL_CtlDialog(buf,"PRESS ANY KEY",nil);\r
+ VW_UpdateScreen();\r
+\r
+ IN_ClearKeysDown();\r
+ IN_Ack();\r
+\r
+ VW_ShowCursor();\r
+ VW_UpdateScreen();\r
+}\r
+\r
+// Custom routines\r
+#if 0\r
+static boolean\r
+USL_GenericCustom(UserCall call,UserItem far *item)\r
+{\r
+ boolean result;\r
+\r
+ result = false;\r
+ switch (call)\r
+ {\r
+ }\r
+ return(result);\r
+}\r
+#endif\r
+\r
+static void\r
+USL_SetOptionsText(void)\r
+{\r
+ optionsi[0].text = showscorebox? "SCORE BOX (ON)" : "SCORE BOX (OFF)";\r
+ optionsi[1].text = compatability? "SVGA COMPATIBILITY (ON)" : "SVGA COMPATIBILITY (OFF)";\r
+#ifdef KEEN\r
+ optionsi[2].text = oldshooting? "TWO-BUTTON FIRING (ON)" : "TWO-BUTTON FIRING (OFF)";\r
+\r
+ keybi[2].flags &= ~ui_Disabled;\r
+ if (oldshooting)\r
+ keybi[2].flags |= ui_Disabled;\r
+#endif\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_ScoreCustom(UserCall call,UserItem far *item)\r
+{\r
+ if (call != uic_SetupCard)\r
+ return(false);\r
+\r
+ showscorebox ^= true;\r
+ USL_CtlDialog(showscorebox? "Score box now on" : "Score box now off",\r
+ "Press any key",nil);\r
+ USL_SetOptionsText();\r
+ return(true);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_CompCustom(UserCall call,UserItem far *item)\r
+{\r
+ if (call != uic_SetupCard)\r
+ return(false);\r
+\r
+ compatability ^= true;\r
+ USL_CtlDialog(compatability? "SVGA compatibility now on" : "SVGA compatibility now off",\r
+ "Press any key",nil);\r
+ USL_SetOptionsText();\r
+ return(true);\r
+}\r
+\r
+#ifdef KEEN\r
+#pragma argsused\r
+static boolean\r
+USL_TwoCustom(UserCall call,UserItem far *item)\r
+{\r
+ if (call != uic_SetupCard)\r
+ return(false);\r
+\r
+ oldshooting ^= true;\r
+ USL_CtlDialog(oldshooting? "Two-button firing now on" : "Two-button firing now off",\r
+ "Press any key",nil);\r
+ USL_SetOptionsText();\r
+ return(true);\r
+}\r
+#endif\r
+\r
+static boolean\r
+USL_ConfigCustom(UserCall call,UserItem far *item)\r
+{\r
+static char *CtlNames[] = {"KEYBOARD","KEYBOARD","JOYSTICK #1","JOYSTICK #2","MOUSE"};\r
+ char *s;\r
+ word w,h,\r
+ tw;\r
+\r
+ if (call == uic_TouchupCard)\r
+ {\r
+ s = "CONTROL: ";\r
+ USL_MeasureString(s,&w,&h);\r
+ tw = w;\r
+ USL_MeasureString(CtlNames[Controls[0]],&w,&h);\r
+ tw += w;\r
+ py = CtlPanelEY - 18 - h;\r
+ px = CtlPanelSX + ((CtlPanelW - tw) / 2);\r
+ fontcolor = NohiliteColor;\r
+ USL_DrawString(s);\r
+ USL_DrawString(CtlNames[Controls[0]]);\r
+ }\r
+ item++; // Shut the compiler up\r
+ return(false);\r
+}\r
+\r
+static void\r
+USL_CKSetKey(UserItem far *item,word i)\r
+{\r
+ boolean on;\r
+ word j;\r
+ ScanCode scan;\r
+ longword time;\r
+ CursorInfo cursorinfo;\r
+\r
+ on = false;\r
+ time = 0;\r
+ LastScan = sc_None;\r
+ fontcolor = HiliteColor;\r
+ do\r
+ {\r
+ if (TimeCount >= time)\r
+ {\r
+ on ^= true;\r
+ VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor);\r
+ VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor);\r
+ if (on)\r
+ VWB_DrawTile8(item->x + 90 + 16,item->y,TileBase + 8);\r
+ VW_UpdateScreen();\r
+\r
+ time = TimeCount + (TickBase / 2);\r
+ }\r
+\r
+ IN_ReadCursor(&cursorinfo);\r
+ while (cursorinfo.button0 || cursorinfo.button1)\r
+ {\r
+ IN_ReadCursor(&cursorinfo);\r
+ LastScan = sc_Escape;\r
+ }\r
+\r
+ asm pushf\r
+ asm cli\r
+ if (LastScan == sc_LShift)\r
+ LastScan = sc_None;\r
+ asm popf\r
+ } while (!(scan = LastScan));\r
+\r
+ if (scan != sc_Escape)\r
+ {\r
+ for (j = 0,on = false;j < 11;j++)\r
+ {\r
+ if (j == i)\r
+ continue;\r
+ if (*(KeyMaps[j]) == scan)\r
+ {\r
+ on = true;\r
+ break;\r
+ }\r
+ }\r
+ if (on)\r
+ USL_CtlDialog("Key already used","Press a key",nil);\r
+ else\r
+ *(KeyMaps[i]) = scan;\r
+ }\r
+ IN_ClearKeysDown();\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_KeySCustom(UserCall call,UserItem far *item)\r
+{\r
+ if (call == uic_SetupCard)\r
+ Controls[0] = ctrl_Keyboard;\r
+ return(false);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_KeyCustom(UserCall call,UserItem far *item)\r
+{\r
+ boolean result;\r
+ word i;\r
+\r
+ result = false;\r
+ i = (topcard == &keygroup)? (3 + (item - keyi)) : (item - keybi);\r
+ switch (call)\r
+ {\r
+ case uic_SetupCard:\r
+ Controls[0] = ctrl_Keyboard;\r
+ break;\r
+ case uic_Draw:\r
+ VWB_Bar(CtlPanelSX + 1,item->y,\r
+ CtlPanelEX - CtlPanelSX - 1,8,BackColor); // Clear out background\r
+ USL_DrawItemIcon(item);\r
+ fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor;\r
+ px = item->x + 8;\r
+ py = item->y + 1;\r
+ USL_DrawString(item->text);\r
+ VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor);\r
+ VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor);\r
+ px = item->x + 90 + 6;\r
+ py = item->y + 1;\r
+ USL_DrawString(IN_GetScanName(*KeyMaps[i]));\r
+ result = true;\r
+ break;\r
+ case uic_Hit:\r
+ USL_KeyCustom(uic_Draw,item);\r
+ USL_CKSetKey(item,i);\r
+ USL_DrawCtlPanel();\r
+ result = true;\r
+ break;\r
+ }\r
+ return(result);\r
+}\r
+\r
+static void\r
+USL_CJDraw(char *s1,char *s2)\r
+{\r
+ word w,h;\r
+\r
+ USL_MeasureString(s1,&w,&h);\r
+ px = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+ py = CtlPanelEY - 34;\r
+ VWB_Bar(CtlPanelSX + 1,py,CtlPanelW - 2,h * 2,BackColor);\r
+ fontcolor = HiliteColor;\r
+ USL_DrawString(s1);\r
+ py += h;\r
+ USL_MeasureString(s2,&w,&h);\r
+ px = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+ USL_DrawString(s2);\r
+}\r
+\r
+static boolean\r
+USL_CJGet(word joy,word button,word x,word y,word *xaxis,word *yaxis)\r
+{\r
+ boolean on;\r
+ longword time;\r
+\r
+ while (IN_GetJoyButtonsDB(joy))\r
+ if (LastScan == sc_Escape)\r
+ return(false);\r
+\r
+ on = false;\r
+ time = 0;\r
+ while (!(IN_GetJoyButtonsDB(joy) & (1 << button)))\r
+ {\r
+ if (TimeCount >= time)\r
+ {\r
+ on ^= true;\r
+ time = TimeCount + (TickBase / 2);\r
+ VWB_DrawTile8(x,y,TileBase + on);\r
+ VW_UpdateScreen();\r
+ }\r
+\r
+ if (LastScan == sc_Escape)\r
+ return(false);\r
+ }\r
+ IN_GetJoyAbs(joy,xaxis,yaxis);\r
+ return(true);\r
+}\r
+\r
+static boolean\r
+USL_ConfigJoystick(word joy)\r
+{\r
+ word x,y,\r
+ minx,miny,\r
+ maxx,maxy;\r
+\r
+ BottomS1 = BottomS2 = "";\r
+ BottomS3 = "Esc to back out";\r
+ USL_DrawCtlPanel();\r
+ x = CtlPanelSX + 60;\r
+ y = CtlPanelSY + 19;\r
+ VWB_DrawPic(x,y,CP_JOYSTICKPIC);\r
+\r
+ USL_CJDraw("Move Joystick to upper left","and press button #1");\r
+ VWB_DrawTile8(x + 24,y + 8,TileBase + 6);\r
+ VWB_DrawTile8(x + 8,y + 8,TileBase + 1);\r
+ VWB_DrawTile8(x + 8,y + 24,TileBase + 0);\r
+ VW_UpdateScreen();\r
+ if (!USL_CJGet(joy,0,x + 8,y + 8,&minx,&miny))\r
+ return(false);\r
+\r
+ USL_CJDraw("Move Joystick to lower right","and press button #2");\r
+ VWB_DrawTile8(x + 24,y + 8,TileBase - 25);\r
+ VWB_DrawTile8(x + 40,y + 24,TileBase + 7);\r
+ VWB_DrawTile8(x + 8,y + 8,TileBase + 0);\r
+ VWB_DrawTile8(x + 8,y + 24,TileBase + 1);\r
+ VW_UpdateScreen();\r
+ if (!USL_CJGet(joy,1,x + 8,y + 24,&maxx,&maxy))\r
+ return(false);\r
+\r
+ while (IN_GetJoyButtonsDB(joy))\r
+ ;\r
+\r
+ IN_SetupJoy(joy,minx,maxx,miny,maxy);\r
+ return(true);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_Joy1Custom(UserCall call,UserItem far *item)\r
+{\r
+ if (call == uic_SetupCard)\r
+ {\r
+ if (USL_ConfigJoystick(0))\r
+ {\r
+ Controls[0] = ctrl_Joystick1;\r
+ USL_CtlDialog("USING JOYSTICK #1","PRESS ANY KEY",nil);\r
+ }\r
+ return(true);\r
+ }\r
+ else\r
+ return(false);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_Joy2Custom(UserCall call,UserItem far *item)\r
+{\r
+ if (call == uic_SetupCard)\r
+ {\r
+ if (USL_ConfigJoystick(1))\r
+ {\r
+ Controls[0] = ctrl_Joystick2;\r
+ USL_CtlDialog("USING JOYSTICK #2","PRESS ANY KEY",nil);\r
+ }\r
+ return(true);\r
+ }\r
+ else\r
+ return(false);\r
+}\r
+\r
+static void\r
+USL_DrawFileIcon(UserItem far *item)\r
+{\r
+ word color;\r
+\r
+ item->y = topcard->y + CtlPanelSY + 12;\r
+ item->y += (item - loadsavegamei) * 11;\r
+\r
+ fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor;\r
+ color = fontcolor ^ BackColor; // Blech!\r
+ VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y,color);\r
+ VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y + 9,color);\r
+ VWB_Vlin(item->y,item->y + 9,item->x,color);\r
+ VWB_Vlin(item->y,item->y + 9,item->x + (CtlPanelW - 12),color);\r
+}\r
+\r
+static void\r
+USL_DoLoadGame(UserItem far *item)\r
+{\r
+ char *filename;\r
+ word n,\r
+ err;\r
+ int file;\r
+ SaveGame *game;\r
+\r
+ if (!USL_ConfirmComm(uc_Loaded))\r
+ return;\r
+\r
+ n = item - loadsavegamei;\r
+ game = &Games[n];\r
+\r
+ USL_ShowLoadSave("Loading",game->name);\r
+\r
+ err = 0;\r
+ filename = USL_GiveSaveName(n);\r
+ if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+ {\r
+ if (read(file,game,sizeof(*game)) == sizeof(*game))\r
+ {\r
+ if (USL_LoadGame)\r
+ if (!USL_LoadGame(file))\r
+ USL_HandleError(err = errno);\r
+ }\r
+ else\r
+ USL_HandleError(err = errno);\r
+ close(file);\r
+ }\r
+ else\r
+ USL_HandleError(err = errno);\r
+ if (err)\r
+ {\r
+ abortgame = true;\r
+ Communication = uc_None;\r
+ CtlPanelDone = false;\r
+ }\r
+ else\r
+ loadedgame = true;\r
+ game->present = true;\r
+\r
+ if (loadedgame)\r
+ Paused = true;\r
+\r
+ USL_DrawCtlPanel();\r
+}\r
+\r
+static boolean\r
+USL_LoadCustom(UserCall call,UserItem far *item)\r
+{\r
+ boolean result;\r
+ word i;\r
+\r
+ result = false;\r
+ switch (call)\r
+ {\r
+ case uic_SetupCard:\r
+ for (i = 0;i < MaxSaveGames;i++)\r
+ {\r
+ if (Games[i].present)\r
+ loadsavegamei[i].flags &= ~ui_Disabled;\r
+ else\r
+ loadsavegamei[i].flags |= ui_Disabled;\r
+ }\r
+ break;\r
+ case uic_DrawIcon:\r
+ USL_DrawFileIcon(item);\r
+ result = true;\r
+ break;\r
+ case uic_Draw:\r
+ USL_DrawFileIcon(item);\r
+ VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor);\r
+\r
+ i = item - loadsavegamei;\r
+ if (Games[i].present)\r
+ px = item->x + 2;\r
+ else\r
+ px = item->x + 60;\r
+ py = item->y + 2;\r
+ USL_DrawString(Games[i].present? Games[i].name : "Empty");\r
+ result = true;\r
+ break;\r
+ case uic_Hit:\r
+ USL_DoLoadGame(item);\r
+ result = true;\r
+ break;\r
+ }\r
+ return(result);\r
+}\r
+\r
+static void\r
+USL_DoSaveGame(UserItem far *item)\r
+{\r
+ boolean ok;\r
+ char *filename;\r
+ word n,err;\r
+ int file;\r
+ SaveGame *game;\r
+\r
+ BottomS1 = "Type name";\r
+ BottomS2 = "Enter accepts";\r
+ USL_DrawCtlPanel();\r
+\r
+ n = item - loadsavegamei;\r
+ game = &Games[n];\r
+ fontcolor = HiliteColor;\r
+ VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor);\r
+ game->oldtest = &PrintX;\r
+ ok = US_LineInput(item->x + 2,item->y + 2,\r
+ game->name,game->present? game->name : nil,\r
+ true,MaxGameName,\r
+ CtlPanelW - 22);\r
+ if (!strlen(game->name))\r
+ strcpy(game->name,"Untitled");\r
+ if (ok)\r
+ {\r
+ USL_ShowLoadSave("Saving",game->name);\r
+\r
+ filename = USL_GiveSaveName(n);\r
+ err = 0;\r
+ file = open(filename,O_CREAT | O_BINARY | O_WRONLY,\r
+ S_IREAD | S_IWRITE | S_IFREG);\r
+ if (file != -1)\r
+ {\r
+ if (write(file,game,sizeof(*game)) == sizeof(*game))\r
+ {\r
+ if (USL_SaveGame)\r
+ ok = USL_SaveGame(file);\r
+ if (!ok)\r
+ USL_HandleError(err = errno);\r
+ }\r
+ else\r
+ USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+ close(file);\r
+ }\r
+ else\r
+ USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+ if (err)\r
+ {\r
+ remove(filename);\r
+ ok = false;\r
+ }\r
+\r
+ }\r
+\r
+ if (!game->present)\r
+ game->present = ok;\r
+\r
+ if (ok)\r
+ GameIsDirty = false;\r
+ USL_SetupCard();\r
+}\r
+\r
+static boolean\r
+USL_SaveCustom(UserCall call,UserItem far *item)\r
+{\r
+ word i;\r
+\r
+ switch (call)\r
+ {\r
+ case uic_SetupCard:\r
+ for (i = 0;i < MaxSaveGames;i++)\r
+ loadsavegamei[i].flags &= ~ui_Disabled;\r
+ return(false);\r
+ case uic_Hit:\r
+ USL_DoSaveGame(item);\r
+ return(true);\r
+// break;\r
+ }\r
+ return(USL_LoadCustom(call,item));\r
+}\r
+\r
+#if 0\r
+\r
+#define PaddleMinX (CtlPanelSX + 3)\r
+#define PaddleMaxX (CtlPanelEX - 15)\r
+#define BallMinX (CtlPanelSX + 2)\r
+#define BallMinY (CtlPanelSY + 12 + 2)\r
+#define BallMaxX (CtlPanelEX - 6)\r
+#define BallMaxY (CtlPanelEY - 13)\r
+#define CPaddleY (BallMinY + 4)\r
+#define KPaddleY (BallMaxY - 2)\r
+void\r
+USL_DrawPongScore(word k,word c)\r
+{\r
+ fontcolor = HiliteColor;\r
+ PrintY = py = CtlPanelSY + 4;\r
+ px = CtlPanelSX + 6;\r
+ VWB_Bar(px,py,42,6,BackColor);\r
+ USL_DrawString("YOU:");\r
+ PrintX = px;\r
+ US_PrintUnsigned(k);\r
+ px = CtlPanelSX + 108;\r
+ VWB_Bar(px,py,50,6,BackColor);\r
+ USL_DrawString("COMP:");\r
+ PrintX = px;\r
+ US_PrintUnsigned(c);\r
+}\r
+\r
+void\r
+USL_PlayPong(void)\r
+{\r
+ boolean ball,killball,revdir,done,lastscore;\r
+ word cycle,\r
+ x,y,\r
+ kx,cx,\r
+ rx,\r
+ bx,by,\r
+ kscore,cscore,\r
+ speedup;\r
+ int bdx,bdy;\r
+ longword balltime,waittime;\r
+ CursorInfo cursorinfo;\r
+\r
+ kx = cx = PaddleMinX + ((PaddleMaxX - PaddleMinX) / 2);\r
+ bx = by = bdx = bdy = 0;\r
+ kscore = cscore = 0;\r
+ USL_DrawPongScore(0,0);\r
+ cycle = 0;\r
+ revdir = false;\r
+ killball = true;\r
+ done = false;\r
+ lastscore = false;\r
+ do\r
+ {\r
+ waittime = TimeCount;\r
+\r
+ IN_ReadCursor(&cursorinfo);\r
+ if (((cursorinfo.x < 0) || IN_KeyDown(sc_LeftArrow)) && (kx > PaddleMinX))\r
+ kx -= 2;\r
+ else if (((cursorinfo.x > 0) || IN_KeyDown(sc_RightArrow)) && (kx < PaddleMaxX))\r
+ kx += 2;\r
+\r
+ if (killball)\r
+ {\r
+ ball = false;\r
+ balltime = TimeCount + TickBase;\r
+ speedup = 10;\r
+ killball = false;\r
+ }\r
+\r
+ if (ball && (cycle++ % 3))\r
+ {\r
+ x = (bx >> 2);\r
+ if (!(x & 1))\r
+ x += (US_RndT() & 1);\r
+\r
+ if ((cx + 6 < x) && (cx < PaddleMaxX))\r
+ cx += 1;\r
+ else if ((cx + 6 > x) && (cx > PaddleMinX))\r
+ cx -= 1;\r
+ }\r
+\r
+ VWB_Bar(BallMinX,BallMinY - 1,\r
+ BallMaxX - BallMinX + 5,BallMaxY - BallMinY + 7,\r
+ BackColor);\r
+ VWB_DrawSprite(cx,CPaddleY,PADDLESPR);\r
+ VWB_DrawSprite(kx,KPaddleY,PADDLESPR);\r
+ if (ball)\r
+ {\r
+ if\r
+ (\r
+ (((bx + bdx) >> 2) > BallMaxX)\r
+ || (((bx + bdx) >> 2) < BallMinX)\r
+ )\r
+ {\r
+ SD_PlaySound(BALLBOUNCESND);\r
+ bdx = -bdx;\r
+ }\r
+ bx += bdx;\r
+\r
+ if (((by + bdy) >> 2) > BallMaxY)\r
+ {\r
+ killball = true;\r
+ lastscore = false;\r
+ cscore++;\r
+ SD_PlaySound(COMPSCOREDSND);\r
+ USL_DrawPongScore(kscore,cscore);\r
+ if (cscore == 21)\r
+ {\r
+ USL_CtlDialog("You lost!","Press any key",nil);\r
+ done = true;\r
+ continue;\r
+ }\r
+ }\r
+ else if (((by + bdy) >> 2) < BallMinY)\r
+ {\r
+ killball = true;\r
+ lastscore = true;\r
+ kscore++;\r
+ SD_PlaySound(KEENSCOREDSND);\r
+ USL_DrawPongScore(kscore,cscore);\r
+ if (kscore == 21)\r
+ {\r
+ USL_CtlDialog("You won!","Press any key",nil);\r
+ done = true;\r
+ continue;\r
+ }\r
+ }\r
+ by += bdy;\r
+\r
+ x = bx >> 2;\r
+ y = by >> 2;\r
+ if (!killball)\r
+ {\r
+ if\r
+ (\r
+ (bdy < 0)\r
+ && ((y >= CPaddleY) && (y < CPaddleY + 3))\r
+ && ((x >= (cx - 5)) && (x < (cx + 11)))\r
+ )\r
+ {\r
+ rx = cx;\r
+ revdir = true;\r
+ SD_PlaySound(COMPPADDLESND);\r
+ }\r
+ else if\r
+ (\r
+ (bdy > 0)\r
+ && ((y >= (KPaddleY - 3)) && (y < KPaddleY))\r
+ && ((x >= (kx - 5)) && (x < (kx + 11)))\r
+ )\r
+ {\r
+ if (((bdy >> 2) < 3) && !(--speedup))\r
+ {\r
+ bdy++;\r
+ speedup = 10;\r
+ }\r
+ rx = kx;\r
+ revdir = true;\r
+ SD_PlaySound(KEENPADDLESND);\r
+ }\r
+ if (revdir)\r
+ {\r
+ bdy = -bdy;\r
+ bdx = ((x + 5 - rx) >> 1) - (1 << 2);\r
+ if (!bdx)\r
+ bdx--;\r
+ revdir = false;\r
+ }\r
+ }\r
+ VWB_DrawSprite(x,y,(x & 1)? BALL1PIXELTOTHERIGHTSPR : BALLSPR);\r
+ }\r
+ else if (TimeCount >= balltime)\r
+ {\r
+ ball = true;\r
+ bdx = 1 - (US_RndT() % 3);\r
+ bdy = 2;\r
+ if (lastscore)\r
+ bdy = -bdy;\r
+ bx = (BallMinX + ((BallMaxX - BallMinX) / 2)) << 2;\r
+ by = (BallMinY + ((BallMaxY - BallMinY) / 2)) << 2;\r
+ }\r
+ VW_UpdateScreen();\r
+ while (waittime == TimeCount)\r
+ ; // DEBUG - do adaptiveness\r
+ } while ((LastScan != sc_Escape) && !done);\r
+ IN_ClearKeysDown();\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_PongCustom(UserCall call,struct UserItem far *item)\r
+{\r
+ if (call != uic_SetupCard)\r
+ return(false);\r
+\r
+ VWB_DrawPic(0,0,CP_MENUSCREENPIC);\r
+ VWB_DrawPic(CtlPanelSX + 56,CtlPanelSY,CP_PADDLEWARPIC);\r
+ VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelSY + 12,HiliteColor ^ BackColor);\r
+ VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelEY - 7,HiliteColor ^ BackColor);\r
+ USL_PlayPong();\r
+\r
+ return(true);\r
+}\r
+\r
+#endif\r
+\r
+// Flag management stuff\r
+static void\r
+USL_ClearFlags(UserItemGroup far *node)\r
+{\r
+ UserItem far *i;\r
+\r
+ if (!node->items)\r
+ return;\r
+\r
+ for (i = node->items;i->type != uii_Bad;i++)\r
+ {\r
+ i->flags &= ~UISelectFlags;\r
+ if (i->child)\r
+ USL_ClearFlags((UserItemGroup far *)i->child);\r
+ }\r
+}\r
+\r
+static int\r
+USL_FindPushedItem(UserItemGroup far *group)\r
+{\r
+ word i;\r
+ UserItem far *item;\r
+\r
+ for (item = group->items,i = 0;item->type != uii_Bad;item++,i++)\r
+ if (item->flags & ui_Pushed)\r
+ return(i);\r
+ return(-1);\r
+}\r
+\r
+static void\r
+USL_SelectItem(UserItemGroup far *group,word index,boolean draw)\r
+{\r
+ UserItem far *item;\r
+\r
+ if (index != group->cursor)\r
+ {\r
+ item = &group->items[group->cursor];\r
+ item->flags &= ~ui_Selected;\r
+ if (draw)\r
+ USL_DrawItem(item);\r
+ }\r
+\r
+ group->cursor = index;\r
+ item = &group->items[group->cursor];\r
+ group->items[group->cursor].flags |= ui_Selected;\r
+ if (draw)\r
+ USL_DrawItem(item);\r
+}\r
+\r
+static void\r
+USL_PushItem(UserItemGroup far *group,word index,boolean draw)\r
+{\r
+ word i;\r
+ UserItem far *item;\r
+\r
+ USL_SelectItem(group,index,draw);\r
+ for (item = group->items,i = 0;item->type != uii_Bad;item++,i++)\r
+ {\r
+ if (item->type != uii_RadioButton)\r
+ continue;\r
+\r
+ if (i == index)\r
+ {\r
+ item->flags |= ui_Pushed;\r
+ if (draw)\r
+ USL_DrawItem(item);\r
+ }\r
+ else if (item->flags & ui_Pushed)\r
+ {\r
+ item->flags &= ~ui_Pushed;\r
+ if (draw)\r
+ USL_DrawItem(item);\r
+ }\r
+ }\r
+}\r
+\r
+static void\r
+USL_NextItem(void)\r
+{\r
+ if (topcard->items[topcard->cursor + 1].type == uii_Bad)\r
+ return;\r
+ USL_SelectItem(topcard,topcard->cursor + 1,true);\r
+}\r
+\r
+static void\r
+USL_PrevItem(void)\r
+{\r
+ if (!topcard->cursor)\r
+ return;\r
+ USL_SelectItem(topcard,topcard->cursor - 1,true);\r
+}\r
+\r
+static void\r
+USL_SetupCard(void)\r
+{\r
+ BottomS1 = "Arrows move";\r
+ BottomS2 = "Enter selects";\r
+ BottomS3 = cstackptr? "ESC to back out" : "ESC to quit";\r
+\r
+ USL_SelectItem(topcard,topcard->cursor,false);\r
+ USL_DrawCtlPanel(); // Contents?\r
+}\r
+\r
+static void\r
+USL_DownLevel(UserItemGroup far *group)\r
+{\r
+ if (!group)\r
+ Quit("USL_DownLevel() - nil card");\r
+ USL_PushCard(group);\r
+ if (group->custom && group->custom(uic_SetupCard,nil))\r
+ USL_PopCard();\r
+ USL_SetupCard();\r
+}\r
+\r
+static void\r
+USL_UpLevel(void)\r
+{\r
+ if (!cstackptr)\r
+ {\r
+ USL_ConfirmComm(uc_Quit);\r
+ return;\r
+ }\r
+\r
+ if (topcard->items)\r
+ topcard->items[topcard->cursor].flags &= ~ui_Selected;\r
+ USL_PopCard();\r
+ USL_SetupCard();\r
+}\r
+\r
+static void\r
+USL_DoItem(void)\r
+{\r
+ // DEBUG - finish this routine\r
+ UserItem far *item;\r
+\r
+ item = &topcard->items[topcard->cursor];\r
+ if (item->flags & ui_Disabled)\r
+ SD_PlaySound(NOWAYSND);\r
+ else\r
+ {\r
+ switch (item->type)\r
+ {\r
+ case uii_Button:\r
+ if (!(topcard->custom && topcard->custom(uic_Hit,item)))\r
+ USL_ConfirmComm(item->comm);\r
+ break;\r
+ case uii_RadioButton:\r
+ USL_PushItem(topcard,topcard->cursor,true);\r
+ break;\r
+ case uii_Folder:\r
+ USL_DownLevel(item->child);\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+static void\r
+USL_SetControlValues(void)\r
+{\r
+ USL_PushItem(&soundgroup,SoundMode,false);\r
+ USL_PushItem(&musicgroup,MusicMode,false);\r
+ if (!AdLibPresent)\r
+ {\r
+ soundi[2].flags |= ui_Disabled; // AdLib sound effects\r
+ musici[1].flags |= ui_Disabled; // AdLib music\r
+ }\r
+\r
+ if (!JoysPresent[0])\r
+ configi[3].flags |= ui_Disabled;\r
+ if (!JoysPresent[1])\r
+ configi[4].flags |= ui_Disabled;\r
+\r
+ rooti[4].text = ingame? "RETURN TO GAME" : "RETURN TO DEMO";\r
+ if (!ingame)\r
+ {\r
+ rooti[2].flags |= ui_Disabled; // Save Game\r
+ rooti[5].flags |= ui_Disabled; // End Game\r
+ }\r
+ rootgroup.cursor = ingame? 4 : 0;\r
+ USL_SetOptionsText();\r
+ // DEBUG - write the rest of this\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the\r
+// values of all the appropriate variables\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_SetUpCtlPanel(void)\r
+{\r
+ int i;\r
+\r
+ // Cache in all of the stuff for the control panel\r
+ CA_UpLevel();\r
+ for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++)\r
+ CA_MarkGrChunk(i);\r
+// for (i = PADDLE_LUMP_START;i <= PADDLE_LUMP_END;i++)\r
+// CA_MarkGrChunk(i);\r
+ CA_MarkGrChunk(STARTFONT+1); // Little font\r
+ CA_MarkGrChunk(CP_MENUMASKPICM); // Mask for dialogs\r
+ CA_CacheMarks("Control Panel");\r
+ CA_LoadAllSounds();\r
+\r
+ // Do some other setup\r
+ fontnumber = 1;\r
+ US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString);\r
+ fontcolor = F_BLACK;\r
+ VW_Bar (0,0,320,200,3); // CAT3D patch\r
+ RF_FixOfs();\r
+ VW_InitDoubleBuffer();\r
+\r
+ Communication = uc_None;\r
+ USL_ClearFlags(&rootgroup);\r
+ USL_SetControlValues();\r
+ USL_SetupStack();\r
+ USL_SetupCard();\r
+\r
+ if (ingame)\r
+ GameIsDirty = true;\r
+\r
+ IN_ClearKeysDown();\r
+}\r
+\r
+static void\r
+USL_HandleComm(UComm comm)\r
+{\r
+ switch (comm)\r
+ {\r
+ case uc_Loaded:\r
+ case uc_Return:\r
+ break;\r
+ case uc_Abort:\r
+ abortgame = true;\r
+ break;\r
+ case uc_Quit:\r
+ QuitToDos = true;\r
+ break;\r
+ case uc_SEasy:\r
+ restartgame = gd_Easy;\r
+ break;\r
+ case uc_SNormal:\r
+ restartgame = gd_Normal;\r
+ break;\r
+ case uc_SHard:\r
+ restartgame = gd_Hard;\r
+ break;\r
+\r
+ default:\r
+ Quit("USL_HandleComm() - unknown");\r
+ break;\r
+ }\r
+}\r
+\r
+static void\r
+USL_GetControlValues(void)\r
+{\r
+ int i;\r
+\r
+ // DEBUG - write the rest of this\r
+ i = USL_FindPushedItem(&soundgroup);\r
+ if (i != SoundMode)\r
+ SD_SetSoundMode(i);\r
+\r
+ i = USL_FindPushedItem(&musicgroup);\r
+ if (i != MusicMode)\r
+ SD_SetMusicMode(i);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// USL_TearDownCtlPanel() - Given the state of the control panel, sets the\r
+// modes and values as appropriate\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_TearDownCtlPanel(void)\r
+{\r
+ USL_GetControlValues();\r
+ if (Communication)\r
+ USL_HandleComm(Communication);\r
+\r
+ fontnumber = 0; // Normal font\r
+ fontcolor = F_BLACK;\r
+ if (restartgame && USL_ResetGame)\r
+ USL_ResetGame();\r
+ else if (QuitToDos)\r
+ {\r
+ if (tedlevel)\r
+ TEDDeath();\r
+ else\r
+ {\r
+ US_CenterWindow(20,3);\r
+ fontcolor = F_SECONDCOLOR;\r
+ US_PrintCentered("Quitting...");\r
+ fontcolor = F_BLACK;\r
+ VW_UpdateScreen();\r
+ Quit(nil);\r
+ }\r
+ }\r
+\r
+ IN_ClearKeysDown();\r
+ SD_WaitSoundDone();\r
+ VW_Bar (0,0,320,200,3); // CAT3D patch\r
+ CA_DownLevel();\r
+ CA_LoadAllSounds();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_ControlPanel() - This is the main routine for the control panel\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#define MoveMin 40\r
+void\r
+US_ControlPanel(void)\r
+{\r
+extern void HelpScreens(void);\r
+\r
+ boolean resetitem,on;\r
+ word i;\r
+ int ydelta;\r
+ longword flashtime;\r
+ UserItem far *item;\r
+ CursorInfo cursorinfo;\r
+\r
+#if 0\r
+ // DEBUG!!!\r
+ {\r
+ USL_SetUpCtlPanel();\r
+ Communication = uc_Loaded;\r
+ CtlPanelDone = true;\r
+ loadedgame = true;\r
+ USL_TearDownCtlPanel();\r
+ return;\r
+ }\r
+#endif\r
+\r
+ if ((LastScan < sc_F1) || (LastScan > sc_F10))\r
+ IN_ClearKeysDown();\r
+\r
+ USL_SetUpCtlPanel();\r
+ USL_DrawCtlPanel();\r
+\r
+ ydelta = 0;\r
+ for (CtlPanelDone = false,resetitem = on = true;!CtlPanelDone;)\r
+ {\r
+ item = &(topcard->items[topcard->cursor]);\r
+\r
+ if (resetitem)\r
+ {\r
+ flashtime = TimeCount + (TickBase / 2);\r
+ resetitem = false;\r
+ }\r
+\r
+ if (TimeCount >= flashtime)\r
+ {\r
+ on ^= true;\r
+ resetitem = true;\r
+ if (!on)\r
+ item->flags &= ~ui_Selected;\r
+ USL_DrawItemIcon(item);\r
+ item->flags |= ui_Selected;\r
+ }\r
+\r
+ VW_UpdateScreen();\r
+\r
+ if (LastScan)\r
+ {\r
+ switch (LastScan)\r
+ {\r
+ case sc_UpArrow:\r
+ USL_PrevItem();\r
+ resetitem = true;\r
+ break;\r
+ case sc_DownArrow:\r
+ USL_NextItem();\r
+ resetitem = true;\r
+ break;\r
+ case sc_Return:\r
+ USL_DoItem();\r
+ resetitem = true;\r
+ break;\r
+ case sc_Escape:\r
+ USL_UpLevel();\r
+ resetitem = true;\r
+ break;\r
+#ifndef KEEN6\r
+ case sc_F1:\r
+ HelpScreens();\r
+ USL_DrawCtlPanel();\r
+ resetitem = true;\r
+ break;\r
+#endif\r
+ }\r
+\r
+ if\r
+ (\r
+ (!resetitem)\r
+ && (\r
+ (LastScan == KbdDefs[0].button0)\r
+ || (LastScan == KbdDefs[0].button1)\r
+ )\r
+ )\r
+ {\r
+ USL_DoItem();\r
+ resetitem = true;\r
+ }\r
+\r
+ if (!resetitem)\r
+ {\r
+ for (item = topcard->items,i = 0;item->type != uii_Bad;item++,i++)\r
+ {\r
+ if (item->hotkey == LastScan)\r
+ {\r
+ USL_SelectItem(topcard,i,true);\r
+ resetitem = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ IN_ClearKeysDown();\r
+ }\r
+ else\r
+ {\r
+ IN_ReadCursor(&cursorinfo);\r
+ ydelta += cursorinfo.y;\r
+ if (cursorinfo.button0)\r
+ {\r
+ do\r
+ {\r
+ IN_ReadCursor(&cursorinfo);\r
+ } while (cursorinfo.button0);\r
+ USL_DoItem();\r
+ resetitem = true;\r
+ }\r
+ else if (cursorinfo.button1)\r
+ {\r
+ do\r
+ {\r
+ IN_ReadCursor(&cursorinfo);\r
+ } while (cursorinfo.button1);\r
+ USL_UpLevel();\r
+ resetitem = true;\r
+ }\r
+ else if (ydelta < -MoveMin)\r
+ {\r
+ ydelta += MoveMin;\r
+ USL_PrevItem();\r
+ resetitem = true;\r
+ }\r
+ else if (ydelta > MoveMin)\r
+ {\r
+ ydelta -= MoveMin;\r
+ USL_NextItem();\r
+ resetitem = true;\r
+ }\r
+ }\r
+ }\r
+\r
+ USL_TearDownCtlPanel();\r
+}\r
--- /dev/null
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+IDEAL\r
+MODEL MEDIUM,C\r
+\r
+; Assembly portion of the User Mgr. This is just John Carmack's table\r
+; driven pseudo-random number generator, and we put it in the User Mgr\r
+; because we couldn't figure out where it should go\r
+\r
+\r
+;============================================================================\r
+;\r
+; RANDOM ROUTINES\r
+;\r
+;============================================================================\r
+\r
+ FARDATA\r
+\r
+rndindex dw ?\r
+\r
+rndtable db 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66\r
+ db 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36\r
+ db 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188\r
+ db 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224\r
+ db 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242\r
+ db 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0\r
+ db 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235\r
+ db 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113\r
+ db 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75\r
+ db 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196\r
+ db 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113\r
+ db 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241\r
+ db 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224\r
+ db 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95\r
+ db 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226\r
+ db 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36\r
+ db 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106\r
+ db 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136\r
+ db 120, 163, 236, 249\r
+\r
+\r
+ CODESEG\r
+\r
+LastRnd dw ?\r
+\r
+;=================================================\r
+;\r
+; void US_InitRndT (boolean randomize)\r
+; Init table based RND generator\r
+; if randomize is false, the counter is set to 0\r
+;\r
+;=================================================\r
+\r
+PROC US_InitRndT randomize:word\r
+ uses si,di\r
+ public US_InitRndT\r
+\r
+ mov ax,SEG rndtable\r
+ mov es,ax\r
+ mov ax,[randomize]\r
+ or ax,ax\r
+ jne @@timeit ;if randomize is true, really random\r
+\r
+ mov dx,0 ;set to a definite value\r
+ jmp @@setit\r
+\r
+@@timeit:\r
+ mov ah,2ch\r
+ int 21h ;GetSystemTime\r
+ and dx,0ffh\r
+\r
+@@setit:\r
+ mov [es:rndindex],dx\r
+ ret\r
+\r
+ENDP\r
+\r
+;=================================================\r
+;\r
+; int US_RndT (void)\r
+; Return a random # between 0-255\r
+; Exit : AX = value\r
+;\r
+;=================================================\r
+PROC US_RndT\r
+ public US_RndT\r
+\r
+ mov ax,SEG rndtable\r
+ mov es,ax\r
+ mov bx,[es:rndindex]\r
+ inc bx\r
+ and bx,0ffh\r
+ mov [es:rndindex],bx\r
+ mov al,[es:rndtable+BX]\r
+ xor ah,ah\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+END\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_VW.C\r
+\r
+#include "ID_HEADS.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define VIEWWIDTH 40\r
+\r
+#define PIXTOBLOCK 4 // 16 pixels to an update block\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+cardtype videocard; // set by VW_Startup\r
+grtype grmode; // CGAgr, EGAgr, VGAgr\r
+\r
+unsigned bufferofs; // hidden area to draw to before displaying\r
+unsigned displayofs; // origin of the visable screen\r
+unsigned panx,pany; // panning adjustments inside port in pixels\r
+unsigned pansx,pansy; // panning adjustments inside port in screen\r
+ // block limited pixel values (ie 0/8 for ega x)\r
+unsigned panadjust; // panx/pany adjusted by screen resolution\r
+\r
+unsigned screenseg; // normally 0xa000 / 0xb800\r
+unsigned linewidth;\r
+unsigned ylookup[VIRTUALHEIGHT];\r
+\r
+unsigned fontnumber; // 0 based font number for drawing\r
+\r
+boolean screenfaded;\r
+\r
+pictabletype _seg *pictable;\r
+pictabletype _seg *picmtable;\r
+spritetabletype _seg *spritetable;\r
+\r
+int bordercolor;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void VWL_MeasureString (char far *string, word *width, word *height,\r
+ fontstruct _seg *font);\r
+void VWL_DrawCursor (void);\r
+void VWL_EraseCursor (void);\r
+void VWL_DBSetup (void);\r
+void VWL_UpdateScreenBlocks (void);\r
+\r
+\r
+int bordercolor;\r
+int cursorvisible;\r
+int cursornumber,cursorwidth,cursorheight,cursorx,cursory;\r
+memptr cursorsave;\r
+unsigned cursorspot;\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_Startup\r
+=\r
+=======================\r
+*/\r
+\r
+static char *ParmStrings[] = {"HIDDENCARD",""};\r
+\r
+void VW_Startup (void)\r
+{\r
+ int i;\r
+\r
+ asm cld;\r
+\r
+ videocard = 0;\r
+\r
+ for (i = 1;i < _argc;i++)\r
+ if (US_CheckParm(_argv[i],ParmStrings) == 0)\r
+ {\r
+ videocard = EGAcard;\r
+ break;\r
+ }\r
+\r
+ if (!videocard)\r
+ videocard = VW_VideoID ();\r
+\r
+#if GRMODE == EGAGR\r
+ grmode = EGAGR;\r
+ if (videocard != EGAcard && videocard != VGAcard)\r
+Quit ("Improper video card! If you really have an EGA/VGA card that I am not \n"\r
+ "detecting, use the -HIDDENCARD command line parameter!");\r
+ EGAWRITEMODE(0);\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+ grmode = CGAGR;\r
+ if (videocard < CGAcard || videocard > VGAcard)\r
+Quit ("Improper video card! If you really have a CGA card that I am not \n"\r
+ "detecting, use the -HIDDENCARD command line parameter!");\r
+ MM_GetPtr (&(memptr)screenseg,0x10000l); // grab 64k for floating screen\r
+#endif\r
+\r
+ cursorvisible = 0;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_Shutdown\r
+=\r
+=======================\r
+*/\r
+\r
+void VW_Shutdown (void)\r
+{\r
+ VW_SetScreenMode (TEXTGR);\r
+#if GRMODE == EGAGR\r
+ VW_SetLineWidth (80);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+========================\r
+=\r
+= VW_SetScreenMode\r
+= Call BIOS to set TEXT / CGAgr / EGAgr / VGAgr\r
+=\r
+========================\r
+*/\r
+\r
+void VW_SetScreenMode (int grmode)\r
+{\r
+ switch (grmode)\r
+ {\r
+ case TEXTGR: _AX = 3;\r
+ geninterrupt (0x10);\r
+ screenseg=0xb000;\r
+ break;\r
+ case CGAGR: _AX = 4;\r
+ geninterrupt (0x10); // screenseg is actually a main mem buffer\r
+ break;\r
+\r
+ case EGA320GR: // MDM start (GAMERS EDGE)\r
+ MaxX=320;\r
+ MaxY=200;\r
+ _AX = 0xd|128;\r
+ geninterrupt (0x10);\r
+ screenseg=0xa000;\r
+ break;\r
+\r
+ case EGA640GR:\r
+ MaxX=640;\r
+ MaxY=200;\r
+ _AX = 0xe|128;\r
+ geninterrupt (0x10);\r
+ screenseg=0xa000;\r
+ break; // MDM end (GAMERS EDGE)\r
+\r
+ case EGAGR: _AX = 0xd;\r
+ MaxX=320;\r
+ MaxY=200;\r
+ geninterrupt (0x10);\r
+ screenseg=0xa000;\r
+ break;\r
+#ifdef VGAGAME\r
+ case VGAGR:{\r
+ char extern VGAPAL; // deluxepaint vga pallet .OBJ file\r
+ void far *vgapal = &VGAPAL;\r
+ SetCool256 (); // custom 256 color mode\r
+ screenseg=0xa000;\r
+ _ES = FP_SEG(vgapal);\r
+ _DX = FP_OFF(vgapal);\r
+ _BX = 0;\r
+ _CX = 0x100;\r
+ _AX = 0x1012;\r
+ geninterrupt(0x10); // set the deluxepaint pallet\r
+\r
+ break;\r
+#endif\r
+ }\r
+ VW_SetLineWidth(SCREENWIDTH);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ SCREEN FADES\r
+\r
+=============================================================================\r
+*/\r
+\r
+char colors[7][17]=\r
+{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\r
+ {0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,0},\r
+ {0,0,0,0,0,0,0,0,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0},\r
+ {0,1,2,3,4,5,6,7,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0},\r
+ {0,1,2,3,4,5,6,7,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0},\r
+ {0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f}};\r
+\r
+\r
+void VW_ColorBorder (int color)\r
+{\r
+ _AH=0x10;\r
+ _AL=1;\r
+ _BH=color;\r
+ geninterrupt (0x10);\r
+ bordercolor = color;\r
+}\r
+\r
+void VW_SetPalette(byte *palette)\r
+{\r
+ byte p;\r
+ word i;\r
+\r
+ for (i = 0;i < 15;i++)\r
+ {\r
+ p = palette[i];\r
+ colors[0][i] = 0;\r
+ colors[1][i] = (p > 0x10)? (p & 0x0f) : 0;\r
+ colors[2][i] = (p > 0x10)? p : 0;\r
+ colors[3][i] = p;\r
+ colors[4][i] = (p > 0x10)? 0x1f : p;\r
+ colors[5][i] = 0x1f;\r
+ }\r
+}\r
+\r
+void VW_SetDefaultColors(void)\r
+{\r
+#if GRMODE == EGAGR\r
+ colors[3][16] = bordercolor;\r
+ _ES=FP_SEG(&colors[3]);\r
+ _DX=FP_OFF(&colors[3]);\r
+ _AX=0x1002;\r
+ geninterrupt(0x10);\r
+ screenfaded = false;\r
+#endif\r
+}\r
+\r
+\r
+void VW_FadeOut(void)\r
+{\r
+#if GRMODE == EGAGR\r
+ int i;\r
+\r
+ for (i=3;i>=0;i--)\r
+ {\r
+ colors[i][16] = bordercolor;\r
+ _ES=FP_SEG(&colors[i]);\r
+ _DX=FP_OFF(&colors[i]);\r
+ _AX=0x1002;\r
+ geninterrupt(0x10);\r
+ VW_WaitVBL(6);\r
+ }\r
+ screenfaded = true;\r
+#endif\r
+}\r
+\r
+\r
+void VW_FadeIn(void)\r
+{\r
+#if GRMODE == EGAGR\r
+ int i;\r
+\r
+ for (i=0;i<4;i++)\r
+ {\r
+ colors[i][16] = bordercolor;\r
+ _ES=FP_SEG(&colors[i]);\r
+ _DX=FP_OFF(&colors[i]);\r
+ _AX=0x1002;\r
+ geninterrupt(0x10);\r
+ VW_WaitVBL(6);\r
+ }\r
+ screenfaded = false;\r
+#endif\r
+}\r
+\r
+void VW_FadeUp(void)\r
+{\r
+#if GRMODE == EGAGR\r
+ int i;\r
+\r
+ for (i=3;i<6;i++)\r
+ {\r
+ colors[i][16] = bordercolor;\r
+ _ES=FP_SEG(&colors[i]);\r
+ _DX=FP_OFF(&colors[i]);\r
+ _AX=0x1002;\r
+ geninterrupt(0x10);\r
+ VW_WaitVBL(6);\r
+ }\r
+ screenfaded = true;\r
+#endif\r
+}\r
+\r
+void VW_FadeDown(void)\r
+{\r
+#if GRMODE == EGAGR\r
+ int i;\r
+\r
+ for (i=5;i>2;i--)\r
+ {\r
+ colors[i][16] = bordercolor;\r
+ _ES=FP_SEG(&colors[i]);\r
+ _DX=FP_OFF(&colors[i]);\r
+ _AX=0x1002;\r
+ geninterrupt(0x10);\r
+ VW_WaitVBL(6);\r
+ }\r
+ screenfaded = false;\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= VW_SetAtrReg\r
+=\r
+= Sets an attribute (pallete / border) register\r
+= Does NOT vsync!\r
+=\r
+========================\r
+*/\r
+\r
+void VW_SetAtrReg (int reg, int value)\r
+{\r
+ asm cli\r
+ asm mov dx,STATUS_REGISTER_1\r
+ asm in al,dx\r
+ asm mov dx,ATR_INDEX\r
+\r
+ asm mov al,BYTE PTR [reg]\r
+ asm out dx,al\r
+ asm mov al,BYTE PTR [value]\r
+ asm out dx,al\r
+ asm mov dx,0x3da\r
+ asm in al,dx\r
+ asm mov dx,ATR_INDEX\r
+ asm mov al,0x20\r
+ asm out dx,al\r
+ asm sti\r
+}\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetLineWidth\r
+=\r
+= Must be an even number of bytes\r
+=\r
+====================\r
+*/\r
+\r
+void VW_SetLineWidth (int width)\r
+{\r
+ int i,offset;\r
+\r
+#if GRMODE == EGAGR\r
+//\r
+// set wide virtual screen\r
+//\r
+asm mov dx,CRTC_INDEX\r
+asm mov al,CRTC_OFFSET\r
+asm mov ah,[BYTE PTR width]\r
+asm shr ah,1\r
+asm out dx,ax\r
+#endif\r
+\r
+//\r
+// set up lookup tables\r
+//\r
+ linewidth = width;\r
+\r
+ offset = 0;\r
+\r
+ for (i=0;i<VIRTUALHEIGHT;i++)\r
+ {\r
+ ylookup[i]=offset;\r
+ offset += width;\r
+ }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetSplitScreen\r
+=\r
+====================\r
+*/\r
+\r
+void VW_SetSplitScreen (int linenum)\r
+{\r
+ VW_WaitVBL (1);\r
+ if (videocard==VGAcard)\r
+ linenum=linenum*2-1;\r
+ outportb (CRTC_INDEX,CRTC_LINECOMPARE);\r
+ outportb (CRTC_INDEX+1,linenum % 256);\r
+ outportb (CRTC_INDEX,CRTC_OVERFLOW);\r
+ outportb (CRTC_INDEX+1, 1+16*(linenum/256));\r
+ if (videocard==VGAcard)\r
+ {\r
+ outportb (CRTC_INDEX,CRTC_MAXSCANLINE);\r
+ outportb (CRTC_INDEX+1,inportb(CRTC_INDEX+1) & (255-64));\r
+ }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_ClearVideo\r
+=\r
+====================\r
+*/\r
+\r
+void VW_ClearVideo (int color)\r
+{\r
+#if GRMODE == EGAGR\r
+ EGAWRITEMODE(2);\r
+ EGAMAPMASK(15);\r
+#endif\r
+\r
+asm mov es,[screenseg]\r
+asm xor di,di\r
+asm mov cx,0xffff\r
+asm mov al,[BYTE PTR color]\r
+asm rep stosb\r
+asm stosb\r
+\r
+#if GRMODE == EGAGR\r
+ EGAWRITEMODE(0);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if NUMPICS>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawPic\r
+=\r
+= X in bytes, y in pixels, chunknum is the #defined picnum\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum)\r
+{\r
+ int picnum = chunknum - STARTPICS;\r
+ memptr source;\r
+ unsigned dest,width,height;\r
+\r
+ source = grsegs[chunknum];\r
+ dest = ylookup[y]+x+bufferofs;\r
+ width = pictable[picnum].width;\r
+ height = pictable[picnum].height;\r
+\r
+ VW_MemToScreen(source,dest,width,height);\r
+}\r
+\r
+\r
+// MDM (GAMERS EDGE) begin\r
+/*\r
+====================\r
+=\r
+= VW_DrawPic2x - Same as VW_DrawPic, but doubles pixels horizontally\r
+= (Great for drawing 320 graphics on 640 screen!)\r
+=\r
+= X in bytes, y in pixels, chunknum is the #defined picnum\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawPic2x(unsigned x, unsigned y, unsigned chunknum)\r
+{\r
+ int picnum = chunknum - STARTPICS;\r
+ memptr source;\r
+ unsigned dest,width,height;\r
+\r
+ source = grsegs[chunknum];\r
+ dest = ylookup[y]+x+bufferofs;\r
+ width = pictable[picnum].width;\r
+ height = pictable[picnum].height;\r
+\r
+ VW_MemToScreen2x(source,dest,width,height);\r
+}\r
+// MDM (GAMERS EDGE) end\r
+\r
+#endif\r
+\r
+#if NUMPICM>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawMPic\r
+=\r
+= X in bytes, y in pixels, chunknum is the #defined picnum\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum)\r
+{\r
+ int picnum = chunknum - STARTPICM;\r
+ memptr source;\r
+ unsigned dest,width,height;\r
+\r
+ source = grsegs[chunknum];\r
+ dest = ylookup[y]+x+bufferofs;\r
+ width = picmtable[picnum].width;\r
+ height = picmtable[picnum].height;\r
+\r
+ VW_MaskBlock(source,0,dest,width,height,width*height);\r
+}\r
+\r
+void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum)\r
+{\r
+ int picnum = chunknum - STARTPICM;\r
+ memptr source;\r
+ unsigned dest,width,ofs,plane;\r
+ int height;\r
+\r
+ source = grsegs[chunknum];\r
+ width = picmtable[picnum].width;\r
+ height = picmtable[picnum].height;\r
+ plane = width*height;\r
+\r
+ ofs = 0;\r
+ if (y<0)\r
+ {\r
+ ofs= -y*width;\r
+ height+=y;\r
+ y=0;\r
+ }\r
+ else if (y+height>216)\r
+ {\r
+ height-=(y-216);\r
+ }\r
+ dest = ylookup[y]+x+bufferofs;\r
+ if (height<1)\r
+ return;\r
+\r
+ VW_MaskBlock(source,ofs,dest,width,height,plane);\r
+}\r
+\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+#if NUMSPRITES>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawSprite\r
+=\r
+= X and Y in pixels, it will match the closest shift possible\r
+=\r
+= To do:\r
+= Add vertical clipping!\r
+= Make the shifts act as center points, rather than break points\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawSprite(int x, int y, unsigned chunknum)\r
+{\r
+ spritetabletype far *spr;\r
+ spritetype _seg *block;\r
+ unsigned dest,shift;\r
+\r
+ spr = &spritetable[chunknum-STARTSPRITES];\r
+ block = (spritetype _seg *)grsegs[chunknum];\r
+\r
+ y+=spr->orgy>>G_P_SHIFT;\r
+ x+=spr->orgx>>G_P_SHIFT;\r
+\r
+#if GRMODE == EGAGR\r
+ shift = (x&7)/2;\r
+#endif\r
+#if GRMODE == CGAGR\r
+ shift = 0;\r
+#endif\r
+\r
+ dest = bufferofs + ylookup[y];\r
+ if (x>=0)\r
+ dest += x/SCREENXDIV;\r
+ else\r
+ dest += (x+1)/SCREENXDIV;\r
+\r
+ VW_MaskBlock (block,block->sourceoffset[shift],dest,\r
+ block->width[shift],spr->height,block->planesize[shift]);\r
+}\r
+\r
+#endif\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= VW_Hlin\r
+=\r
+==================\r
+*/\r
+\r
+\r
+#if GRMODE == EGAGR\r
+\r
+unsigned char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};\r
+unsigned char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};\r
+\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color)\r
+{\r
+ unsigned dest,xlb,xhb,maskleft,maskright,mid;\r
+\r
+ xlb=xl/8;\r
+ xhb=xh/8;\r
+\r
+ EGAWRITEMODE(2);\r
+ EGAMAPMASK(15);\r
+\r
+ maskleft = leftmask[xl&7];\r
+ maskright = rightmask[xh&7];\r
+\r
+ mid = xhb-xlb-1;\r
+ dest = bufferofs+ylookup[y]+xlb;\r
+\r
+ if (xlb==xhb)\r
+ {\r
+ //\r
+ // entire line is in one byte\r
+ //\r
+\r
+ maskleft&=maskright;\r
+\r
+ asm mov es,[screenseg]\r
+ asm mov di,[dest]\r
+\r
+ asm mov dx,GC_INDEX\r
+ asm mov al,GC_BITMASK\r
+ asm mov ah,[BYTE PTR maskleft]\r
+ asm out dx,ax // mask off pixels\r
+\r
+ asm mov al,[BYTE PTR color]\r
+ asm xchg al,[es:di] // load latches and write pixels\r
+\r
+ goto done;\r
+ }\r
+\r
+asm mov es,[screenseg]\r
+asm mov di,[dest]\r
+asm mov dx,GC_INDEX\r
+asm mov bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm mov al,GC_BITMASK\r
+asm mov ah,[BYTE PTR maskleft]\r
+asm out dx,ax // mask off pixels\r
+\r
+asm mov al,bh\r
+asm mov bl,[es:di] // load latches\r
+asm stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm mov ax,GC_BITMASK + 255*256\r
+asm out dx,ax // no masking\r
+\r
+asm mov al,bh\r
+asm mov cx,[mid]\r
+asm rep stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm mov al,GC_BITMASK\r
+asm mov ah,[BYTE PTR maskright]\r
+asm out dx,ax // mask off pixels\r
+\r
+asm xchg bh,[es:di] // load latches and write pixels\r
+\r
+done:\r
+ EGABITMASK(255);\r
+ EGAWRITEMODE(0);\r
+}\r
+#endif\r
+\r
+\r
+#if GRMODE == CGAGR\r
+\r
+unsigned char pixmask[4] = {0xc0,0x30,0x0c,0x03};\r
+unsigned char leftmask[4] = {0xff,0x3f,0x0f,0x03};\r
+unsigned char rightmask[4] = {0xc0,0xf0,0xfc,0xff};\r
+unsigned char colorbyte[4] = {0,0x55,0xaa,0xff};\r
+\r
+//\r
+// could be optimized for rep stosw\r
+//\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color)\r
+{\r
+ unsigned dest,xlb,xhb,mid;\r
+ byte maskleft,maskright;\r
+\r
+ color = colorbyte[color]; // expand 2 color bits to 8\r
+\r
+ xlb=xl/4;\r
+ xhb=xh/4;\r
+\r
+ maskleft = leftmask[xl&3];\r
+ maskright = rightmask[xh&3];\r
+\r
+ mid = xhb-xlb-1;\r
+ dest = bufferofs+ylookup[y]+xlb;\r
+asm mov es,[screenseg]\r
+\r
+ if (xlb==xhb)\r
+ {\r
+ //\r
+ // entire line is in one byte\r
+ //\r
+ maskleft&=maskright;\r
+\r
+ asm mov ah,[maskleft]\r
+ asm mov bl,[BYTE PTR color]\r
+ asm and bl,[maskleft]\r
+ asm not ah\r
+\r
+ asm mov di,[dest]\r
+\r
+ asm mov al,[es:di]\r
+ asm and al,ah // mask out pixels\r
+ asm or al,bl // or in color\r
+ asm mov [es:di],al\r
+ return;\r
+ }\r
+\r
+asm mov di,[dest]\r
+asm mov bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm mov ah,[maskleft]\r
+asm mov bl,bh\r
+asm and bl,[maskleft]\r
+asm not ah\r
+asm mov al,[es:di]\r
+asm and al,ah // mask out pixels\r
+asm or al,bl // or in color\r
+asm stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm mov al,bh\r
+asm mov cx,[mid]\r
+asm rep stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm mov ah,[maskright]\r
+asm mov bl,bh\r
+asm and bl,[maskright]\r
+asm not ah\r
+asm mov al,[es:di]\r
+asm and al,ah // mask out pixels\r
+asm or al,bl // or in color\r
+asm stosb\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= VW_Bar\r
+=\r
+= Pixel addressable block fill routine\r
+=\r
+==================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+ unsigned color)\r
+{\r
+ unsigned xh = x+width-1;\r
+\r
+ while (height--)\r
+ VW_Hlin (x,xh,y++,color);\r
+}\r
+\r
+#endif\r
+\r
+\r
+#if GRMODE == EGAGR\r
+\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+ unsigned color)\r
+{\r
+ unsigned dest,xh,xlb,xhb,maskleft,maskright,mid;\r
+\r
+ xh = x+width-1;\r
+ xlb=x/8;\r
+ xhb=xh/8;\r
+\r
+ EGAWRITEMODE(2);\r
+ EGAMAPMASK(15);\r
+\r
+ maskleft = leftmask[x&7];\r
+ maskright = rightmask[xh&7];\r
+\r
+ mid = xhb-xlb-1;\r
+ dest = bufferofs+ylookup[y]+xlb;\r
+\r
+ if (xlb==xhb)\r
+ {\r
+ //\r
+ // entire line is in one byte\r
+ //\r
+\r
+ maskleft&=maskright;\r
+\r
+ asm mov es,[screenseg]\r
+ asm mov di,[dest]\r
+\r
+ asm mov dx,GC_INDEX\r
+ asm mov al,GC_BITMASK\r
+ asm mov ah,[BYTE PTR maskleft]\r
+ asm out dx,ax // mask off pixels\r
+\r
+ asm mov ah,[BYTE PTR color]\r
+ asm mov dx,[linewidth]\r
+yloop1:\r
+ asm mov al,ah\r
+ asm xchg al,[es:di] // load latches and write pixels\r
+ asm add di,dx // down to next line\r
+ asm dec [height]\r
+ asm jnz yloop1\r
+\r
+ goto done;\r
+ }\r
+\r
+asm mov es,[screenseg]\r
+asm mov di,[dest]\r
+asm mov bh,[BYTE PTR color]\r
+asm mov dx,GC_INDEX\r
+asm mov si,[linewidth]\r
+asm sub si,[mid] // add to di at end of line to get to next scan\r
+asm dec si\r
+\r
+//\r
+// draw left side\r
+//\r
+yloop2:\r
+asm mov al,GC_BITMASK\r
+asm mov ah,[BYTE PTR maskleft]\r
+asm out dx,ax // mask off pixels\r
+\r
+asm mov al,bh\r
+asm mov bl,[es:di] // load latches\r
+asm stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm mov ax,GC_BITMASK + 255*256\r
+asm out dx,ax // no masking\r
+\r
+asm mov al,bh\r
+asm mov cx,[mid]\r
+asm rep stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm mov al,GC_BITMASK\r
+asm mov ah,[BYTE PTR maskright]\r
+asm out dx,ax // mask off pixels\r
+\r
+asm mov al,bh\r
+asm xchg al,[es:di] // load latches and write pixels\r
+\r
+asm add di,si // move to start of next line\r
+asm dec [height]\r
+asm jnz yloop2\r
+\r
+done:\r
+ EGABITMASK(255);\r
+ EGAWRITEMODE(0);\r
+}\r
+\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= VW_MeasureString\r
+=\r
+==================\r
+*/\r
+\r
+#if NUMFONT+NUMFONTM>0\r
+void\r
+VWL_MeasureString (char far *string, word *width, word *height, fontstruct _seg *font)\r
+{\r
+ *height = font->height-1; // MDM (GAMERS EDGE) - squeeze font vertically...\r
+ for (*width = 0;*string;string++)\r
+ *width += font->width[*((byte far *)string)]; // proportional width\r
+}\r
+\r
+void VW_MeasurePropString (char far *string, word *width, word *height)\r
+{\r
+ VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONT+fontnumber]);\r
+}\r
+\r
+void VW_MeasureMPropString (char far *string, word *width, word *height)\r
+{\r
+ VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONTM+fontnumber]);\r
+}\r
+\r
+\r
+#endif\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ CGA stuff\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define CGACRTCWIDTH 40\r
+\r
+/*\r
+==========================\r
+=\r
+= VW_CGAFullUpdate\r
+=\r
+==========================\r
+*/\r
+\r
+void VW_CGAFullUpdate (void)\r
+{\r
+ byte *update;\r
+ boolean halftile;\r
+ unsigned x,y,middlerows,middlecollumns;\r
+\r
+ displayofs = bufferofs+panadjust;\r
+\r
+asm mov ax,0xb800\r
+asm mov es,ax\r
+\r
+asm mov si,[displayofs]\r
+asm xor di,di\r
+\r
+asm mov bx,100 // pairs of scan lines to copy\r
+asm mov dx,[linewidth]\r
+asm sub dx,80\r
+\r
+asm mov ds,[screenseg]\r
+asm test si,1\r
+asm jz evenblock\r
+\r
+//\r
+// odd source\r
+//\r
+asm mov ax,39 // words accross screen\r
+copytwolineso:\r
+asm movsb\r
+asm mov cx,ax\r
+asm rep movsw\r
+asm movsb\r
+asm add si,dx\r
+asm add di,0x2000-80 // go to the interlaced bank\r
+asm movsb\r
+asm mov cx,ax\r
+asm rep movsw\r
+asm movsb\r
+asm add si,dx\r
+asm sub di,0x2000 // go to the non interlaced bank\r
+\r
+asm dec bx\r
+asm jnz copytwolineso\r
+asm jmp blitdone\r
+\r
+//\r
+// even source\r
+//\r
+evenblock:\r
+asm mov ax,40 // words accross screen\r
+copytwolines:\r
+asm mov cx,ax\r
+asm rep movsw\r
+asm add si,dx\r
+asm add di,0x2000-80 // go to the interlaced bank\r
+asm mov cx,ax\r
+asm rep movsw\r
+asm add si,dx\r
+asm sub di,0x2000 // go to the non interlaced bank\r
+\r
+asm dec bx\r
+asm jnz copytwolines\r
+\r
+blitdone:\r
+asm mov ax,ss\r
+asm mov ds,ax\r
+asm mov es,ax\r
+\r
+asm xor ax,ax // clear out the update matrix\r
+asm mov cx,UPDATEWIDE*UPDATEHIGH/2\r
+\r
+asm mov di,[baseupdateptr]\r
+asm rep stosw\r
+\r
+ updateptr = baseupdateptr;\r
+ *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;\r
+}\r
+\r
+\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+ CURSOR ROUTINES\r
+\r
+These only work in the context of the double buffered update routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+====================\r
+=\r
+= VWL_DrawCursor\r
+=\r
+= Background saves, then draws the cursor at cursorspot\r
+=\r
+====================\r
+*/\r
+\r
+void VWL_DrawCursor (void)\r
+{\r
+ cursorspot = bufferofs + ylookup[cursory+pansy]+(cursorx+pansx)/SCREENXDIV;\r
+ VW_ScreenToMem(cursorspot,cursorsave,cursorwidth,cursorheight);\r
+ VWB_DrawSprite(cursorx,cursory,cursornumber);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VWL_EraseCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VWL_EraseCursor (void)\r
+{\r
+ VW_MemToScreen(cursorsave,cursorspot,cursorwidth,cursorheight);\r
+ VW_MarkUpdateBlock ((cursorx+pansx)&SCREENXMASK,cursory+pansy,\r
+ ( (cursorx+pansx)&SCREENXMASK)+cursorwidth*SCREENXDIV-1,\r
+ cursory+pansy+cursorheight-1);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VW_ShowCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VW_ShowCursor (void)\r
+{\r
+ cursorvisible++;\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_HideCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VW_HideCursor (void)\r
+{\r
+ cursorvisible--;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_MoveCursor\r
+=\r
+====================\r
+*/\r
+#define MAXCURSORX (319-24)\r
+#define MAXCURSORY (199-24)\r
+\r
+void VW_MoveCursor (int x, int y)\r
+{\r
+ if (x>MAXCURSORX)\r
+ x=MAXCURSORX;\r
+ if (y>MAXCURSORY)\r
+ y=MAXCURSORY; // catacombs hack to keep cursor on screen\r
+\r
+ cursorx = x;\r
+ cursory = y;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetCursor\r
+=\r
+= Load in a sprite to be used as a cursor, and allocate background save space\r
+=\r
+====================\r
+*/\r
+\r
+void VW_SetCursor (int spritenum)\r
+{\r
+ VW_FreeCursor ();\r
+\r
+ cursornumber = spritenum;\r
+\r
+ CA_CacheGrChunk (spritenum);\r
+ MM_SetLock (&grsegs[spritenum],true);\r
+\r
+ cursorwidth = spritetable[spritenum-STARTSPRITES].width+1;\r
+ cursorheight = spritetable[spritenum-STARTSPRITES].height;\r
+\r
+ MM_GetPtr (&cursorsave,cursorwidth*cursorheight*5);\r
+ MM_SetLock (&cursorsave,true);\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VW_FreeCursor\r
+=\r
+= Frees the memory used by the cursor and its background save\r
+=\r
+====================\r
+*/\r
+\r
+void VW_FreeCursor (void)\r
+{\r
+ if (cursornumber)\r
+ {\r
+ MM_SetLock (&grsegs[cursornumber],false);\r
+ MM_SetPurge (&grsegs[cursornumber],3);\r
+ MM_SetLock (&cursorsave,false);\r
+ MM_FreePtr (&cursorsave);\r
+ cursornumber = 0;\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+ Double buffer management routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+======================\r
+=\r
+= VW_InitDoubleBuffer\r
+=\r
+======================\r
+*/\r
+\r
+void VW_InitDoubleBuffer (void)\r
+{\r
+#if GRMODE == EGAGR\r
+ VW_SetScreen (displayofs+panadjust,0); // no pel pan\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= VW_FixRefreshBuffer\r
+=\r
+= Copies the view page to the buffer page on page flipped refreshes to\r
+= avoid a one frame shear around pop up windows\r
+=\r
+======================\r
+*/\r
+\r
+void VW_FixRefreshBuffer (void)\r
+{\r
+#if GRMODE == EGAGR\r
+ VW_ScreenToScreen (displayofs,bufferofs,PORTTILESWIDE*4*CHARWIDTH,\r
+ (PORTTILESHIGH-1)*16);\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= VW_QuitDoubleBuffer\r
+=\r
+======================\r
+*/\r
+\r
+void VW_QuitDoubleBuffer (void)\r
+{\r
+}\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_MarkUpdateBlock\r
+=\r
+= Takes a pixel bounded block and marks the tiles in bufferblocks\r
+= Returns 0 if the entire block is off the buffer screen\r
+=\r
+=======================\r
+*/\r
+\r
+int VW_MarkUpdateBlock (int x1, int y1, int x2, int y2)\r
+{\r
+// MDM (GAMERS EDGE) begin - NOT NEEDED FOR 3D ENGINE\r
+#if 0\r
+ int x,y,xt1,yt1,xt2,yt2,nextline;\r
+ byte *mark;\r
+\r
+ xt1 = x1>>PIXTOBLOCK;\r
+ yt1 = y1>>PIXTOBLOCK;\r
+\r
+ xt2 = x2>>PIXTOBLOCK;\r
+ yt2 = y2>>PIXTOBLOCK;\r
+\r
+ if (xt1<0)\r
+ xt1=0;\r
+ else if (xt1>=UPDATEWIDE-1)\r
+ return 0;\r
+\r
+ if (yt1<0)\r
+ yt1=0;\r
+ else if (yt1>UPDATEHIGH)\r
+ return 0;\r
+\r
+ if (xt2<0)\r
+ return 0;\r
+ else if (xt2>=UPDATEWIDE-1)\r
+ xt2 = UPDATEWIDE-2;\r
+\r
+ if (yt2<0)\r
+ return 0;\r
+ else if (yt2>=UPDATEHIGH)\r
+ yt2 = UPDATEHIGH-1;\r
+\r
+ mark = updateptr + uwidthtable[yt1] + xt1;\r
+ nextline = UPDATEWIDE - (xt2-xt1) - 1;\r
+\r
+ for (y=yt1;y<=yt2;y++)\r
+ {\r
+ for (x=xt1;x<=xt2;x++)\r
+ *mark++ = 1; // this tile will need to be updated\r
+\r
+ mark += nextline;\r
+ }\r
+#endif\r
+// MDM (GAMERS EDGE) end\r
+\r
+ return 1;\r
+}\r
+\r
+\r
+/*\r
+===========================\r
+=\r
+= VW_UpdateScreen\r
+=\r
+= Updates any changed areas of the double buffer and displays the cursor\r
+=\r
+===========================\r
+*/\r
+\r
+void VW_UpdateScreen (void)\r
+{\r
+ if (cursorvisible>0)\r
+ VWL_DrawCursor();\r
+\r
+#if GRMODE == EGAGR\r
+ VWL_UpdateScreenBlocks();\r
+\r
+asm mov ax,ds\r
+asm mov es,ax\r
+asm mov di,[updateptr] // cat3d patch\r
+asm xor ax,ax // clear out the update matrix\r
+asm mov cx,UPDATEWIDE*UPDATEHIGH/2\r
+asm rep stosw\r
+ *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;\r
+\r
+asm cli\r
+asm mov cx,[displayofs]\r
+asm add cx,[panadjust]\r
+asm mov dx,CRTC_INDEX\r
+asm mov al,0ch // start address high register\r
+asm out dx,al\r
+asm inc dx\r
+asm mov al,ch\r
+asm out dx,al\r
+asm dec dx\r
+asm mov al,0dh // start address low register\r
+asm out dx,al\r
+asm mov al,cl\r
+asm inc dx\r
+asm out dx,al\r
+asm sti\r
+\r
+#endif\r
+#if GRMODE == CGAGR\r
+ VW_CGAFullUpdate();\r
+#endif\r
+\r
+ if (cursorvisible>0)\r
+ VWL_EraseCursor();\r
+}\r
+\r
+\r
+\r
+void VWB_DrawTile8 (int x, int y, int tile)\r
+{\r
+ x+=pansx;\r
+ y+=pansy;\r
+ if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7))\r
+ VW_DrawTile8 (x/SCREENXDIV,y,tile);\r
+}\r
+\r
+void VWB_DrawTile8M (int x, int y, int tile)\r
+{\r
+ int xb;\r
+\r
+ x+=pansx;\r
+ y+=pansy;\r
+ xb = x/SCREENXDIV; // use intermediate because VW_DT8M is macro\r
+// if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7)) // MDM (GAMER EDGE)\r
+ VW_DrawTile8M (xb,y,tile); // statement prevents drawing chars past 42\r
+}\r
+\r
+void VWB_DrawTile16 (int x, int y, int tile)\r
+{\r
+ x+=pansx;\r
+ y+=pansy;\r
+ if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15))\r
+ VW_DrawTile16 (x/SCREENXDIV,y,tile);\r
+}\r
+\r
+void VWB_DrawTile16M (int x, int y, int tile)\r
+{\r
+ int xb;\r
+\r
+ x+=pansx;\r
+ y+=pansy;\r
+ xb = x/SCREENXDIV; // use intermediate because VW_DT16M is macro\r
+ if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15))\r
+ VW_DrawTile16M (xb,y,tile);\r
+}\r
+\r
+#if NUMPICS\r
+void VWB_DrawPic (int x, int y, int chunknum)\r
+{\r
+// mostly copied from drawpic\r
+ int picnum = chunknum - STARTPICS;\r
+ memptr source;\r
+ unsigned dest,width,height;\r
+\r
+ x+=pansx;\r
+ y+=pansy;\r
+ x/= SCREENXDIV;\r
+\r
+ source = grsegs[chunknum];\r
+ dest = ylookup[y]+x+bufferofs;\r
+ width = pictable[picnum].width;\r
+ height = pictable[picnum].height;\r
+\r
+ if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1))\r
+ VW_MemToScreen(source,dest,width,height);\r
+}\r
+#endif\r
+\r
+#if NUMPICM>0\r
+void VWB_DrawMPic(int x, int y, int chunknum)\r
+{\r
+// mostly copied from drawmpic\r
+ int picnum = chunknum - STARTPICM;\r
+ memptr source;\r
+ unsigned dest,width,height;\r
+\r
+ x+=pansx;\r
+ y+=pansy;\r
+ x/=SCREENXDIV;\r
+\r
+ source = grsegs[chunknum];\r
+ dest = ylookup[y]+x+bufferofs;\r
+ width = picmtable[picnum].width;\r
+ height = picmtable[picnum].height;\r
+\r
+ if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1))\r
+ VW_MaskBlock(source,0,dest,width,height,width*height);\r
+}\r
+#endif\r
+\r
+\r
+void VWB_Bar (int x, int y, int width, int height, int color)\r
+{\r
+ x+=pansx;\r
+ y+=pansy;\r
+ if (VW_MarkUpdateBlock (x,y,x+width,y+height-1) )\r
+ VW_Bar (x,y,width,height,color);\r
+}\r
+\r
+\r
+#if NUMFONT\r
+void VWB_DrawPropString (char far *string)\r
+{\r
+ int x,y;\r
+ x = px+pansx;\r
+ y = py+pansy;\r
+ VW_DrawPropString (string);\r
+ VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1);\r
+}\r
+#endif\r
+\r
+\r
+#if NUMFONTM\r
+void VWB_DrawMPropString (char far *string)\r
+{\r
+ int x,y;\r
+ x = px+pansx;\r
+ y = py+pansy;\r
+ VW_DrawMPropString (string);\r
+ VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1);\r
+}\r
+#endif\r
+\r
+#if NUMSPRITES\r
+void VWB_DrawSprite(int x, int y, int chunknum)\r
+{\r
+ spritetabletype far *spr;\r
+ spritetype _seg *block;\r
+ unsigned dest,shift,width,height;\r
+\r
+ x+=pansx;\r
+ y+=pansy;\r
+\r
+ spr = &spritetable[chunknum-STARTSPRITES];\r
+ block = (spritetype _seg *)grsegs[chunknum];\r
+\r
+ y+=spr->orgy>>G_P_SHIFT;\r
+ x+=spr->orgx>>G_P_SHIFT;\r
+\r
+\r
+#if GRMODE == EGAGR\r
+ shift = (x&7)/2;\r
+#endif\r
+#if GRMODE == CGAGR\r
+ shift = 0;\r
+#endif\r
+\r
+ dest = bufferofs + ylookup[y];\r
+ if (x>=0)\r
+ dest += x/SCREENXDIV;\r
+ else\r
+ dest += (x+1)/SCREENXDIV;\r
+\r
+ width = block->width[shift];\r
+ height = spr->height;\r
+\r
+ if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+width*SCREENXDIV-1\r
+ ,y+height-1))\r
+ VW_MaskBlock (block,block->sourceoffset[shift],dest,\r
+ width,height,block->planesize[shift]);\r
+}\r
+#endif\r
+\r
+void VWB_Plot (int x, int y, int color)\r
+{\r
+ x+=pansx;\r
+ y+=pansy;\r
+ if (VW_MarkUpdateBlock (x,y,x,y))\r
+ VW_Plot(x,y,color);\r
+}\r
+\r
+void VWB_Hlin (int x1, int x2, int y, int color)\r
+{\r
+ x1+=pansx;\r
+ x2+=pansx;\r
+ y+=pansy;\r
+ if (VW_MarkUpdateBlock (x1,y,x2,y))\r
+ VW_Hlin(x1,x2,y,color);\r
+}\r
+\r
+void VWB_Vlin (int y1, int y2, int x, int color)\r
+{\r
+ x+=pansx;\r
+ y1+=pansy;\r
+ y2+=pansy;\r
+ if (VW_MarkUpdateBlock (x,y1,x,y2))\r
+ VW_Vlin(y1,y2,x,color);\r
+}\r
+\r
+\r
+//===========================================================================\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_VW.H\r
+\r
+#ifndef __TYPES__\r
+#include "ID_TYPES.H"\r
+#endif\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+#ifndef __ID_GLOB__\r
+#include "ID_GLOB.H"\r
+#endif\r
+\r
+#define __ID_VW__\r
+\r
+//===========================================================================\r
+\r
+#define G_P_SHIFT 4 // global >> ?? = pixels\r
+\r
+#if GRMODE == EGAGR\r
+#define SCREENWIDTH 40\r
+#define CHARWIDTH 1\r
+#define TILEWIDTH 2\r
+#define GRPLANES 4\r
+#define BYTEPIXELS 8\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define SCREENWIDTH 128\r
+#define CHARWIDTH 2\r
+#define TILEWIDTH 4\r
+#define GRPLANES 1\r
+#define BYTEPIXELS 4\r
+#endif\r
+\r
+#define VIRTUALHEIGHT 300\r
+#define VIRTUALWIDTH 512\r
+\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define MAXSHIFTS 1\r
+\r
+#define WHITE 3 // graphics mode independant colors\r
+#define BLACK 0\r
+#define FIRSTCOLOR 1\r
+#define SECONDCOLOR 2\r
+#define F_WHITE 0 // for XOR font drawing\r
+#define F_BLACK 3\r
+#define F_FIRSTCOLOR 2\r
+#define F_SECONDCOLOR 1\r
+\r
+#endif\r
+\r
+#if GRMODE == EGAGR\r
+\r
+#define MAXSHIFTS 4\r
+\r
+#define WHITE 15 // graphics mode independant colors\r
+#define BLACK 0\r
+#define LT_GREY 7\r
+#define FIRSTCOLOR 1\r
+#define SECONDCOLOR 12\r
+#define F_WHITE 0 // for XOR font drawing\r
+#define F_BLACK 15\r
+#define F_FIRSTCOLOR 14\r
+#define F_SECONDCOLOR 3\r
+\r
+#endif\r
+\r
+#if GRMODE == EGAGR\r
+#define SCREENXMASK (~7)\r
+#define SCREENXPLUS (7)\r
+#define SCREENXDIV (8)\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define SCREENXMASK (~3)\r
+#define SCREENXDIV (4)\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+\r
+#define SC_INDEX 0x3C4\r
+#define SC_RESET 0\r
+#define SC_CLOCK 1\r
+#define SC_MAPMASK 2\r
+#define SC_CHARMAP 3\r
+#define SC_MEMMODE 4\r
+\r
+#define CRTC_INDEX 0x3D4\r
+#define CRTC_H_TOTAL 0\r
+#define CRTC_H_DISPEND 1\r
+#define CRTC_H_BLANK 2\r
+#define CRTC_H_ENDBLANK 3\r
+#define CRTC_H_RETRACE 4\r
+#define CRTC_H_ENDRETRACE 5\r
+#define CRTC_V_TOTAL 6\r
+#define CRTC_OVERFLOW 7\r
+#define CRTC_ROWSCAN 8\r
+#define CRTC_MAXSCANLINE 9\r
+#define CRTC_CURSORSTART 10\r
+#define CRTC_CURSOREND 11\r
+#define CRTC_STARTHIGH 12\r
+#define CRTC_STARTLOW 13\r
+#define CRTC_CURSORHIGH 14\r
+#define CRTC_CURSORLOW 15\r
+#define CRTC_V_RETRACE 16\r
+#define CRTC_V_ENDRETRACE 17\r
+#define CRTC_V_DISPEND 18\r
+#define CRTC_OFFSET 19\r
+#define CRTC_UNDERLINE 20\r
+#define CRTC_V_BLANK 21\r
+#define CRTC_V_ENDBLANK 22\r
+#define CRTC_MODE 23\r
+#define CRTC_LINECOMPARE 24\r
+\r
+\r
+#define GC_INDEX 0x3CE\r
+#define GC_SETRESET 0\r
+#define GC_ENABLESETRESET 1\r
+#define GC_COLORCOMPARE 2\r
+#define GC_DATAROTATE 3\r
+#define GC_READMAP 4\r
+#define GC_MODE 5\r
+#define GC_MISCELLANEOUS 6\r
+#define GC_COLORDONTCARE 7\r
+#define GC_BITMASK 8\r
+\r
+#define ATR_INDEX 0x3c0\r
+#define ATR_MODE 16\r
+#define ATR_OVERSCAN 17\r
+#define ATR_COLORPLANEENABLE 18\r
+#define ATR_PELPAN 19\r
+#define ATR_COLORSELECT 20\r
+\r
+#define STATUS_REGISTER_1 0x3da\r
+\r
+//===========================================================================\r
+\r
+typedef enum {NOcard,MDAcard,CGAcard,EGAcard,MCGAcard,VGAcard,\r
+ HGCcard=0x80,HGCPcard,HICcard} cardtype;\r
+\r
+typedef struct\r
+{\r
+ int width,\r
+ height,\r
+ orgx,orgy,\r
+ xl,yl,xh,yh,\r
+ shifts;\r
+} spritetabletype;\r
+\r
+typedef struct\r
+{\r
+ unsigned sourceoffset[MAXSHIFTS];\r
+ unsigned planesize[MAXSHIFTS];\r
+ unsigned width[MAXSHIFTS];\r
+ byte data[];\r
+} spritetype; // the memptr for each sprite points to this\r
+\r
+typedef struct\r
+{\r
+ int width,height;\r
+} pictabletype;\r
+\r
+\r
+typedef struct\r
+{\r
+ int height;\r
+ int location[256];\r
+ char width[256];\r
+} fontstruct;\r
+\r
+\r
+typedef enum {CGAgr,EGAgr,VGAgr} grtype;\r
+\r
+//===========================================================================\r
+\r
+extern cardtype videocard; // set by VW_Startup\r
+extern grtype grmode; // CGAgr, EGAgr, VGAgr\r
+\r
+extern unsigned bufferofs; // hidden port to draw to before displaying\r
+extern unsigned displayofs; // origin of port on visable screen\r
+extern unsigned panx,pany; // panning adjustments inside port in pixels\r
+extern unsigned pansx,pansy;\r
+extern unsigned panadjust; // panx/pany adjusted by screen resolution\r
+\r
+extern unsigned screenseg; // normally 0xa000 or buffer segment\r
+\r
+extern unsigned linewidth;\r
+extern unsigned ylookup[VIRTUALHEIGHT];\r
+\r
+extern boolean screenfaded;\r
+extern char colors[7][17]; // pallets for fades\r
+\r
+extern pictabletype _seg *pictable;\r
+extern pictabletype _seg *picmtable;\r
+extern spritetabletype _seg *spritetable;\r
+\r
+extern unsigned fontnumber; // 0 based font number for drawing\r
+extern int px,py;\r
+extern byte pdrawmode,fontcolor;\r
+\r
+extern int bordercolor;\r
+\r
+//\r
+// asm globals\r
+//\r
+\r
+extern unsigned *shifttabletable[8];\r
+extern unsigned bufferwidth,bufferheight,screenspot; // used by font drawing stuff\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+void VW_Startup (void);\r
+void VW_Shutdown (void);\r
+\r
+cardtype VW_VideoID (void);\r
+\r
+//\r
+// EGA hardware routines\r
+//\r
+\r
+#define EGAWRITEMODE(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_MODE+256*x;out dx,ax;sti;}\r
+#define EGABITMASK(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_BITMASK+256*x;out dx,ax;sti;}\r
+#define EGAMAPMASK(x) asm{cli;mov dx,SC_INDEX;mov ax,SC_MAPMASK+x*256;out dx,ax;sti;}\r
+#define EGAREADMAP(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_READMAP+x*256;out dx,ax;sti;}\r
+\r
+void VW_SetLineWidth(int width);\r
+void VW_SetSplitScreen(int width);\r
+void VW_SetScreen (unsigned CRTC, unsigned pelpan);\r
+\r
+void VW_SetScreenMode (int grmode);\r
+void VW_ClearVideo (int color);\r
+void VW_WaitVBL (int number);\r
+\r
+void VW_ColorBorder (int color);\r
+void VW_SetPalette(byte *palette);\r
+void VW_SetDefaultColors(void);\r
+void VW_FadeOut(void);\r
+void VW_FadeIn(void);\r
+void VW_FadeUp(void);\r
+void VW_FadeDown(void);\r
+\r
+void VW_SetAtrReg (int reg, int value);\r
+\r
+//\r
+// block primitives\r
+//\r
+\r
+void VW_MaskBlock(memptr segm,unsigned ofs,unsigned dest,\r
+ unsigned wide,unsigned height,unsigned planesize);\r
+void VW_MemToScreen(memptr source,unsigned dest,unsigned width,unsigned height);\r
+void VW_MemToScreen2x(memptr source,unsigned dest,unsigned width,unsigned height);\r
+void VW_ScreenToMem(unsigned source,memptr dest,unsigned width,unsigned height);\r
+void VW_ScreenToScreen(unsigned source,unsigned dest,unsigned width,unsigned height);\r
+\r
+\r
+//\r
+// block addressable routines\r
+//\r
+\r
+void VW_DrawTile8(unsigned x, unsigned y, unsigned tile);\r
+\r
+#if GRMODE == EGAGR\r
+\r
+#define VW_DrawTile8M(x,y,t) \\r
+ VW_MaskBlock(grsegs[STARTTILE8M],(t)*40,bufferofs+ylookup[y]+(x),1,8,8)\r
+#define VW_DrawTile16(x,y,t) \\r
+ VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),2,16)\r
+#define VW_DrawTile16M(x,y,t) \\r
+ VW_MaskBlock(grsegs[STARTTILE16M],(t)*160,bufferofs+ylookup[y]+(x),2,16,32)\r
+\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define VW_DrawTile8M(x,y,t) \\r
+ VW_MaskBlock(grsegs[STARTTILE8M],(t)*32,bufferofs+ylookup[y]+(x),2,8,16)\r
+#define VW_DrawTile16(x,y,t) \\r
+ VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),4,16)\r
+#define VW_DrawTile16M(x,y,t) \\r
+ VW_MaskBlock(grsegs[STARTTILE16M],(t)*128,bufferofs+ylookup[y]+(x),4,16,64)\r
+\r
+#endif\r
+\r
+void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum);\r
+void VW_DrawPic2x(unsigned x, unsigned y, unsigned chunknum);\r
+void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum);\r
+void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum);\r
+\r
+//\r
+// pixel addressable routines\r
+//\r
+void VW_MeasurePropString (char far *string, word *width, word *height);\r
+void VW_MeasureMPropString (char far *string, word *width, word *height);\r
+\r
+void VW_DrawPropString (char far *string);\r
+void VW_DrawMPropString (char far *string);\r
+void VW_DrawSprite(int x, int y, unsigned sprite);\r
+void VW_Plot(unsigned x, unsigned y, unsigned color);\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color);\r
+void VW_Vlin(unsigned yl, unsigned yh, unsigned x, unsigned color);\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+ unsigned color);\r
+\r
+//===========================================================================\r
+\r
+//\r
+// Double buffer management routines\r
+//\r
+\r
+void VW_InitDoubleBuffer (void);\r
+void VW_FixRefreshBuffer (void);\r
+int VW_MarkUpdateBlock (int x1, int y1, int x2, int y2);\r
+void VW_UpdateScreen (void);\r
+void VW_CGAFullUpdate (void);\r
+\r
+//\r
+// cursor\r
+//\r
+\r
+void VW_ShowCursor (void);\r
+void VW_HideCursor (void);\r
+void VW_MoveCursor (int x, int y);\r
+void VW_SetCursor (int spritenum);\r
+void VW_FreeCursor (void);\r
+\r
+//\r
+// mode independant routines\r
+// coordinates in pixels, rounded to best screen res\r
+// regions marked in double buffer\r
+//\r
+\r
+void VWB_DrawTile8 (int x, int y, int tile);\r
+void VWB_DrawTile8M (int x, int y, int tile);\r
+void VWB_DrawTile16 (int x, int y, int tile);\r
+void VWB_DrawTile16M (int x, int y, int tile);\r
+void VWB_DrawPic (int x, int y, int chunknum);\r
+void VWB_DrawMPic(int x, int y, int chunknum);\r
+void VWB_Bar (int x, int y, int width, int height, int color);\r
+\r
+void VWB_DrawPropString (char far *string);\r
+void VWB_DrawMPropString (char far *string);\r
+void VWB_DrawSprite (int x, int y, int chunknum);\r
+void VWB_Plot (int x, int y, int color);\r
+void VWB_Hlin (int x1, int x2, int y, int color);\r
+void VWB_Vlin (int y1, int y2, int x, int color);\r
+\r
+void VWL_MeasureString (char far *string, word *width, word *height, fontstruct _seg *font);\r
+//===========================================================================\r
--- /dev/null
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; ID_VW_A.ASM\r
+\r
+IDEAL\r
+MODEL MEDIUM,C\r
+\r
+INCLUDE "ID_ASM.EQU"\r
+\r
+WAITFORVBL = 1 ; setting to 0 causes setscreen and waitvbl\r
+ ; to skip waiting for VBL (for timing things)\r
+\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+EXTRN screenseg :WORD\r
+EXTRN drawofs :WORD\r
+EXTRN bufferofs :WORD\r
+EXTRN displayofs :WORD\r
+EXTRN drawofs :WORD\r
+EXTRN panadjust :WORD\r
+EXTRN ylookup :WORD\r
+EXTRN linewidth :WORD\r
+EXTRN grsegs :WORD\r
+EXTRN updateptr :WORD\r
+EXTRN blockstarts :WORD ;offsets from drawofs for each update block\r
+EXTRN fontspace :WORD\r
+EXTRN fontnumber :WORD\r
+\r
+\r
+planemask db ?\r
+planenum db ?\r
+screendest dw ?\r
+linedelta dw ?\r
+\r
+LABEL shiftdata0 WORD\r
+; dw 0\r
+\r
+if 1\r
+ dw 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13\r
+ dw 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27\r
+ dw 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41\r
+ dw 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55\r
+ dw 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69\r
+ dw 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83\r
+ dw 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97\r
+ dw 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111\r
+ dw 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125\r
+ dw 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139\r
+ dw 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153\r
+ dw 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167\r
+ dw 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181\r
+ dw 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195\r
+ dw 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209\r
+ dw 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223\r
+ dw 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237\r
+ dw 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251\r
+ dw 252, 253, 254, 255\r
+endif\r
+\r
+LABEL shiftdata1 WORD\r
+; dw 0\r
+\r
+if 1\r
+ dw 0,32768, 1,32769, 2,32770, 3,32771, 4,32772, 5,32773, 6,32774\r
+ dw 7,32775, 8,32776, 9,32777, 10,32778, 11,32779, 12,32780, 13,32781\r
+ dw 14,32782, 15,32783, 16,32784, 17,32785, 18,32786, 19,32787, 20,32788\r
+ dw 21,32789, 22,32790, 23,32791, 24,32792, 25,32793, 26,32794, 27,32795\r
+ dw 28,32796, 29,32797, 30,32798, 31,32799, 32,32800, 33,32801, 34,32802\r
+ dw 35,32803, 36,32804, 37,32805, 38,32806, 39,32807, 40,32808, 41,32809\r
+ dw 42,32810, 43,32811, 44,32812, 45,32813, 46,32814, 47,32815, 48,32816\r
+ dw 49,32817, 50,32818, 51,32819, 52,32820, 53,32821, 54,32822, 55,32823\r
+ dw 56,32824, 57,32825, 58,32826, 59,32827, 60,32828, 61,32829, 62,32830\r
+ dw 63,32831, 64,32832, 65,32833, 66,32834, 67,32835, 68,32836, 69,32837\r
+ dw 70,32838, 71,32839, 72,32840, 73,32841, 74,32842, 75,32843, 76,32844\r
+ dw 77,32845, 78,32846, 79,32847, 80,32848, 81,32849, 82,32850, 83,32851\r
+ dw 84,32852, 85,32853, 86,32854, 87,32855, 88,32856, 89,32857, 90,32858\r
+ dw 91,32859, 92,32860, 93,32861, 94,32862, 95,32863, 96,32864, 97,32865\r
+ dw 98,32866, 99,32867, 100,32868, 101,32869, 102,32870, 103,32871, 104,32872\r
+ dw 105,32873, 106,32874, 107,32875, 108,32876, 109,32877, 110,32878, 111,32879\r
+ dw 112,32880, 113,32881, 114,32882, 115,32883, 116,32884, 117,32885, 118,32886\r
+ dw 119,32887, 120,32888, 121,32889, 122,32890, 123,32891, 124,32892, 125,32893\r
+ dw 126,32894, 127,32895\r
+endif\r
+\r
+LABEL shiftdata2 WORD\r
+ dw 0,16384,32768,49152, 1,16385,32769,49153, 2,16386,32770,49154, 3,16387\r
+ dw 32771,49155, 4,16388,32772,49156, 5,16389,32773,49157, 6,16390,32774,49158\r
+ dw 7,16391,32775,49159, 8,16392,32776,49160, 9,16393,32777,49161, 10,16394\r
+ dw 32778,49162, 11,16395,32779,49163, 12,16396,32780,49164, 13,16397,32781,49165\r
+ dw 14,16398,32782,49166, 15,16399,32783,49167, 16,16400,32784,49168, 17,16401\r
+ dw 32785,49169, 18,16402,32786,49170, 19,16403,32787,49171, 20,16404,32788,49172\r
+ dw 21,16405,32789,49173, 22,16406,32790,49174, 23,16407,32791,49175, 24,16408\r
+ dw 32792,49176, 25,16409,32793,49177, 26,16410,32794,49178, 27,16411,32795,49179\r
+ dw 28,16412,32796,49180, 29,16413,32797,49181, 30,16414,32798,49182, 31,16415\r
+ dw 32799,49183, 32,16416,32800,49184, 33,16417,32801,49185, 34,16418,32802,49186\r
+ dw 35,16419,32803,49187, 36,16420,32804,49188, 37,16421,32805,49189, 38,16422\r
+ dw 32806,49190, 39,16423,32807,49191, 40,16424,32808,49192, 41,16425,32809,49193\r
+ dw 42,16426,32810,49194, 43,16427,32811,49195, 44,16428,32812,49196, 45,16429\r
+ dw 32813,49197, 46,16430,32814,49198, 47,16431,32815,49199, 48,16432,32816,49200\r
+ dw 49,16433,32817,49201, 50,16434,32818,49202, 51,16435,32819,49203, 52,16436\r
+ dw 32820,49204, 53,16437,32821,49205, 54,16438,32822,49206, 55,16439,32823,49207\r
+ dw 56,16440,32824,49208, 57,16441,32825,49209, 58,16442,32826,49210, 59,16443\r
+ dw 32827,49211, 60,16444,32828,49212, 61,16445,32829,49213, 62,16446,32830,49214\r
+ dw 63,16447,32831,49215\r
+\r
+LABEL shiftdata3 WORD\r
+; dw 0\r
+\r
+if 1\r
+ dw 0, 8192,16384,24576,32768,40960,49152,57344, 1, 8193,16385,24577,32769,40961\r
+ dw 49153,57345, 2, 8194,16386,24578,32770,40962,49154,57346, 3, 8195,16387,24579\r
+ dw 32771,40963,49155,57347, 4, 8196,16388,24580,32772,40964,49156,57348, 5, 8197\r
+ dw 16389,24581,32773,40965,49157,57349, 6, 8198,16390,24582,32774,40966,49158,57350\r
+ dw 7, 8199,16391,24583,32775,40967,49159,57351, 8, 8200,16392,24584,32776,40968\r
+ dw 49160,57352, 9, 8201,16393,24585,32777,40969,49161,57353, 10, 8202,16394,24586\r
+ dw 32778,40970,49162,57354, 11, 8203,16395,24587,32779,40971,49163,57355, 12, 8204\r
+ dw 16396,24588,32780,40972,49164,57356, 13, 8205,16397,24589,32781,40973,49165,57357\r
+ dw 14, 8206,16398,24590,32782,40974,49166,57358, 15, 8207,16399,24591,32783,40975\r
+ dw 49167,57359, 16, 8208,16400,24592,32784,40976,49168,57360, 17, 8209,16401,24593\r
+ dw 32785,40977,49169,57361, 18, 8210,16402,24594,32786,40978,49170,57362, 19, 8211\r
+ dw 16403,24595,32787,40979,49171,57363, 20, 8212,16404,24596,32788,40980,49172,57364\r
+ dw 21, 8213,16405,24597,32789,40981,49173,57365, 22, 8214,16406,24598,32790,40982\r
+ dw 49174,57366, 23, 8215,16407,24599,32791,40983,49175,57367, 24, 8216,16408,24600\r
+ dw 32792,40984,49176,57368, 25, 8217,16409,24601,32793,40985,49177,57369, 26, 8218\r
+ dw 16410,24602,32794,40986,49178,57370, 27, 8219,16411,24603,32795,40987,49179,57371\r
+ dw 28, 8220,16412,24604,32796,40988,49180,57372, 29, 8221,16413,24605,32797,40989\r
+ dw 49181,57373, 30, 8222,16414,24606,32798,40990,49182,57374, 31, 8223,16415,24607\r
+ dw 32799,40991,49183,57375\r
+endif\r
+\r
+LABEL shiftdata4 WORD\r
+ dw 0, 4096, 8192,12288,16384,20480,24576,28672,32768,36864,40960,45056,49152,53248\r
+ dw 57344,61440, 1, 4097, 8193,12289,16385,20481,24577,28673,32769,36865,40961,45057\r
+ dw 49153,53249,57345,61441, 2, 4098, 8194,12290,16386,20482,24578,28674,32770,36866\r
+ dw 40962,45058,49154,53250,57346,61442, 3, 4099, 8195,12291,16387,20483,24579,28675\r
+ dw 32771,36867,40963,45059,49155,53251,57347,61443, 4, 4100, 8196,12292,16388,20484\r
+ dw 24580,28676,32772,36868,40964,45060,49156,53252,57348,61444, 5, 4101, 8197,12293\r
+ dw 16389,20485,24581,28677,32773,36869,40965,45061,49157,53253,57349,61445, 6, 4102\r
+ dw 8198,12294,16390,20486,24582,28678,32774,36870,40966,45062,49158,53254,57350,61446\r
+ dw 7, 4103, 8199,12295,16391,20487,24583,28679,32775,36871,40967,45063,49159,53255\r
+ dw 57351,61447, 8, 4104, 8200,12296,16392,20488,24584,28680,32776,36872,40968,45064\r
+ dw 49160,53256,57352,61448, 9, 4105, 8201,12297,16393,20489,24585,28681,32777,36873\r
+ dw 40969,45065,49161,53257,57353,61449, 10, 4106, 8202,12298,16394,20490,24586,28682\r
+ dw 32778,36874,40970,45066,49162,53258,57354,61450, 11, 4107, 8203,12299,16395,20491\r
+ dw 24587,28683,32779,36875,40971,45067,49163,53259,57355,61451, 12, 4108, 8204,12300\r
+ dw 16396,20492,24588,28684,32780,36876,40972,45068,49164,53260,57356,61452, 13, 4109\r
+ dw 8205,12301,16397,20493,24589,28685,32781,36877,40973,45069,49165,53261,57357,61453\r
+ dw 14, 4110, 8206,12302,16398,20494,24590,28686,32782,36878,40974,45070,49166,53262\r
+ dw 57358,61454, 15, 4111, 8207,12303,16399,20495,24591,28687,32783,36879,40975,45071\r
+ dw 49167,53263,57359,61455\r
+\r
+LABEL shiftdata5 WORD\r
+; dw 0\r
+\r
+if 1\r
+ dw 0, 2048, 4096, 6144, 8192,10240,12288,14336,16384,18432,20480,22528,24576,26624\r
+ dw 28672,30720,32768,34816,36864,38912,40960,43008,45056,47104,49152,51200,53248,55296\r
+ dw 57344,59392,61440,63488, 1, 2049, 4097, 6145, 8193,10241,12289,14337,16385,18433\r
+ dw 20481,22529,24577,26625,28673,30721,32769,34817,36865,38913,40961,43009,45057,47105\r
+ dw 49153,51201,53249,55297,57345,59393,61441,63489, 2, 2050, 4098, 6146, 8194,10242\r
+ dw 12290,14338,16386,18434,20482,22530,24578,26626,28674,30722,32770,34818,36866,38914\r
+ dw 40962,43010,45058,47106,49154,51202,53250,55298,57346,59394,61442,63490, 3, 2051\r
+ dw 4099, 6147, 8195,10243,12291,14339,16387,18435,20483,22531,24579,26627,28675,30723\r
+ dw 32771,34819,36867,38915,40963,43011,45059,47107,49155,51203,53251,55299,57347,59395\r
+ dw 61443,63491, 4, 2052, 4100, 6148, 8196,10244,12292,14340,16388,18436,20484,22532\r
+ dw 24580,26628,28676,30724,32772,34820,36868,38916,40964,43012,45060,47108,49156,51204\r
+ dw 53252,55300,57348,59396,61444,63492, 5, 2053, 4101, 6149, 8197,10245,12293,14341\r
+ dw 16389,18437,20485,22533,24581,26629,28677,30725,32773,34821,36869,38917,40965,43013\r
+ dw 45061,47109,49157,51205,53253,55301,57349,59397,61445,63493, 6, 2054, 4102, 6150\r
+ dw 8198,10246,12294,14342,16390,18438,20486,22534,24582,26630,28678,30726,32774,34822\r
+ dw 36870,38918,40966,43014,45062,47110,49158,51206,53254,55302,57350,59398,61446,63494\r
+ dw 7, 2055, 4103, 6151, 8199,10247,12295,14343,16391,18439,20487,22535,24583,26631\r
+ dw 28679,30727,32775,34823,36871,38919,40967,43015,45063,47111,49159,51207,53255,55303\r
+ dw 57351,59399,61447,63495\r
+endif\r
+\r
+LABEL shiftdata6 WORD\r
+ dw 0, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216,10240,11264,12288,13312\r
+ dw 14336,15360,16384,17408,18432,19456,20480,21504,22528,23552,24576,25600,26624,27648\r
+ dw 28672,29696,30720,31744,32768,33792,34816,35840,36864,37888,38912,39936,40960,41984\r
+ dw 43008,44032,45056,46080,47104,48128,49152,50176,51200,52224,53248,54272,55296,56320\r
+ dw 57344,58368,59392,60416,61440,62464,63488,64512, 1, 1025, 2049, 3073, 4097, 5121\r
+ dw 6145, 7169, 8193, 9217,10241,11265,12289,13313,14337,15361,16385,17409,18433,19457\r
+ dw 20481,21505,22529,23553,24577,25601,26625,27649,28673,29697,30721,31745,32769,33793\r
+ dw 34817,35841,36865,37889,38913,39937,40961,41985,43009,44033,45057,46081,47105,48129\r
+ dw 49153,50177,51201,52225,53249,54273,55297,56321,57345,58369,59393,60417,61441,62465\r
+ dw 63489,64513, 2, 1026, 2050, 3074, 4098, 5122, 6146, 7170, 8194, 9218,10242,11266\r
+ dw 12290,13314,14338,15362,16386,17410,18434,19458,20482,21506,22530,23554,24578,25602\r
+ dw 26626,27650,28674,29698,30722,31746,32770,33794,34818,35842,36866,37890,38914,39938\r
+ dw 40962,41986,43010,44034,45058,46082,47106,48130,49154,50178,51202,52226,53250,54274\r
+ dw 55298,56322,57346,58370,59394,60418,61442,62466,63490,64514, 3, 1027, 2051, 3075\r
+ dw 4099, 5123, 6147, 7171, 8195, 9219,10243,11267,12291,13315,14339,15363,16387,17411\r
+ dw 18435,19459,20483,21507,22531,23555,24579,25603,26627,27651,28675,29699,30723,31747\r
+ dw 32771,33795,34819,35843,36867,37891,38915,39939,40963,41987,43011,44035,45059,46083\r
+ dw 47107,48131,49155,50179,51203,52227,53251,54275,55299,56323,57347,58371,59395,60419\r
+ dw 61443,62467,63491,64515\r
+\r
+LABEL shiftdata7 WORD\r
+; dw 0\r
+\r
+if 1\r
+ dw 0, 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144, 6656\r
+ dw 7168, 7680, 8192, 8704, 9216, 9728,10240,10752,11264,11776,12288,12800,13312,13824\r
+ dw 14336,14848,15360,15872,16384,16896,17408,17920,18432,18944,19456,19968,20480,20992\r
+ dw 21504,22016,22528,23040,23552,24064,24576,25088,25600,26112,26624,27136,27648,28160\r
+ dw 28672,29184,29696,30208,30720,31232,31744,32256,32768,33280,33792,34304,34816,35328\r
+ dw 35840,36352,36864,37376,37888,38400,38912,39424,39936,40448,40960,41472,41984,42496\r
+ dw 43008,43520,44032,44544,45056,45568,46080,46592,47104,47616,48128,48640,49152,49664\r
+ dw 50176,50688,51200,51712,52224,52736,53248,53760,54272,54784,55296,55808,56320,56832\r
+ dw 57344,57856,58368,58880,59392,59904,60416,60928,61440,61952,62464,62976,63488,64000\r
+ dw 64512,65024, 1, 513, 1025, 1537, 2049, 2561, 3073, 3585, 4097, 4609, 5121, 5633\r
+ dw 6145, 6657, 7169, 7681, 8193, 8705, 9217, 9729,10241,10753,11265,11777,12289,12801\r
+ dw 13313,13825,14337,14849,15361,15873,16385,16897,17409,17921,18433,18945,19457,19969\r
+ dw 20481,20993,21505,22017,22529,23041,23553,24065,24577,25089,25601,26113,26625,27137\r
+ dw 27649,28161,28673,29185,29697,30209,30721,31233,31745,32257,32769,33281,33793,34305\r
+ dw 34817,35329,35841,36353,36865,37377,37889,38401,38913,39425,39937,40449,40961,41473\r
+ dw 41985,42497,43009,43521,44033,44545,45057,45569,46081,46593,47105,47617,48129,48641\r
+ dw 49153,49665,50177,50689,51201,51713,52225,52737,53249,53761,54273,54785,55297,55809\r
+ dw 56321,56833,57345,57857,58369,58881,59393,59905,60417,60929,61441,61953,62465,62977\r
+ dw 63489,64001,64513,65025\r
+endif\r
+\r
+shifttabletable dw shiftdata0,shiftdata1,shiftdata2,shiftdata3\r
+ dw shiftdata4,shiftdata5,shiftdata6,shiftdata7\r
+\r
+PUBLIC shifttabletable\r
+\r
+\r
+;============================================================================\r
+\r
+CODESEG\r
+\r
+IFE GRMODE-CGAGR\r
+INCLUDE "ID_VW_AC.ASM"\r
+ENDIF\r
+\r
+IFE GRMODE-EGAGR\r
+INCLUDE "ID_VW_AE.ASM"\r
+ENDIF\r
+\r
+IFE GRMODE-VGAGR\r
+INCLUDE "ID_VW_AV.ASM"\r
+ENDIF\r
+\r
+;============================================================================\r
+;\r
+; MISC VIDEO ROUTINES\r
+;\r
+;============================================================================\r
+\r
+;========\r
+;\r
+; VW_WaitVBL (int number)\r
+;\r
+;========\r
+\r
+PROC VW_WaitVBL number:WORD\r
+PUBLIC VW_WaitVBL\r
+\r
+if WAITFORVBL ; skip wait if profiling\r
+\r
+ mov dx,STATUS_REGISTER_1\r
+\r
+ mov cx,[number]\r
+\r
+waitvbl1:\r
+ in al,dx\r
+ test al,00001000b ;look for vbl\r
+ jnz waitvbl1\r
+\r
+waitvbl2:\r
+ in al,dx\r
+ test al,00001000b ;look for vbl\r
+ jz waitvbl2\r
+\r
+ loop waitvbl1\r
+\r
+endif\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+\r
+\r
+ MASM\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Name: VW_VideoID\r
+;\r
+; Function: Detects the presence of various video subsystems\r
+;\r
+; int VideoID;\r
+;\r
+; Subsystem ID values:\r
+; 0 = (none)\r
+; 1 = MDA\r
+; 2 = CGA\r
+; 3 = EGA\r
+; 4 = MCGA\r
+; 5 = VGA\r
+; 80h = HGC\r
+; 81h = HGC+\r
+; 82h = Hercules InColor\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Equates\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+VIDstruct STRUC ; corresponds to C data structure\r
+\r
+Video0Type DB ? ; first subsystem type\r
+Display0Type DB ? ; display attached to first subsystem\r
+\r
+Video1Type DB ? ; second subsystem type\r
+Display1Type DB ? ; display attached to second subsystem\r
+\r
+VIDstruct ENDS\r
+\r
+\r
+Device0 EQU word ptr Video0Type[di]\r
+Device1 EQU word ptr Video1Type[di]\r
+\r
+\r
+MDA EQU 1 ; subsystem types\r
+CGA EQU 2\r
+EGA EQU 3\r
+MCGA EQU 4\r
+VGA EQU 5\r
+HGC EQU 80h\r
+HGCPlus EQU 81h\r
+InColor EQU 82h\r
+\r
+MDADisplay EQU 1 ; display types\r
+CGADisplay EQU 2\r
+EGAColorDisplay EQU 3\r
+PS2MonoDisplay EQU 4\r
+PS2ColorDisplay EQU 5\r
+\r
+TRUE EQU 1\r
+FALSE EQU 0\r
+\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Program\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+\r
+Results VIDstruct <> ;results go here!\r
+\r
+EGADisplays DB CGADisplay ; 0000b, 0001b (EGA switch values)\r
+ DB EGAColorDisplay ; 0010b, 0011b\r
+ DB MDADisplay ; 0100b, 0101b\r
+ DB CGADisplay ; 0110b, 0111b\r
+ DB EGAColorDisplay ; 1000b, 1001b\r
+ DB MDADisplay ; 1010b, 1011b\r
+\r
+DCCtable DB 0,0 ; translate table for INT 10h func 1Ah\r
+ DB MDA,MDADisplay\r
+ DB CGA,CGADisplay\r
+ DB 0,0\r
+ DB EGA,EGAColorDisplay\r
+ DB EGA,MDADisplay\r
+ DB 0,0\r
+ DB VGA,PS2MonoDisplay\r
+ DB VGA,PS2ColorDisplay\r
+ DB 0,0\r
+ DB MCGA,EGAColorDisplay\r
+ DB MCGA,PS2MonoDisplay\r
+ DB MCGA,PS2ColorDisplay\r
+\r
+TestSequence DB TRUE ; this list of flags and addresses\r
+ DW FindPS2 ; determines the order in which this\r
+ ; program looks for the various\r
+EGAflag DB ? ; subsystems\r
+ DW FindEGA\r
+\r
+CGAflag DB ?\r
+ DW FindCGA\r
+\r
+Monoflag DB ?\r
+ DW FindMono\r
+\r
+NumberOfTests EQU ($-TestSequence)/3\r
+\r
+\r
+PUBLIC VW_VideoID\r
+VW_VideoID PROC\r
+\r
+ push bp ; preserve caller registers\r
+ mov bp,sp\r
+ push ds\r
+ push si\r
+ push di\r
+\r
+ push cs\r
+ pop ds\r
+ ASSUME DS:@Code\r
+\r
+; initialize the data structure that will contain the results\r
+\r
+ lea di,Results ; DS:DI -> start of data structure\r
+\r
+ mov Device0,0 ; zero these variables\r
+ mov Device1,0\r
+\r
+; look for the various subsystems using the subroutines whose addresses are\r
+; tabulated in TestSequence; each subroutine sets flags in TestSequence\r
+; to indicate whether subsequent subroutines need to be called\r
+\r
+ mov byte ptr CGAflag,TRUE\r
+ mov byte ptr EGAflag,TRUE\r
+ mov byte ptr Monoflag,TRUE\r
+\r
+ mov cx,NumberOfTests\r
+ mov si,offset TestSequence\r
+\r
+@@L01: lodsb ; AL := flag\r
+ test al,al\r
+ lodsw ; AX := subroutine address\r
+ jz @@L02 ; skip subroutine if flag is false\r
+\r
+ push si\r
+ push cx\r
+ call ax ; call subroutine to detect subsystem\r
+ pop cx\r
+ pop si\r
+\r
+@@L02: loop @@L01\r
+\r
+; determine which subsystem is active\r
+\r
+ call FindActive\r
+\r
+ mov al,Results.Video0Type\r
+ mov ah,0 ; was: Results.Display0Type\r
+\r
+ pop di ; restore caller registers and return\r
+ pop si\r
+ pop ds\r
+ mov sp,bp\r
+ pop bp\r
+ ret\r
+\r
+VW_VideoID ENDP\r
+\r
+\r
+;\r
+; FindPS2\r
+;\r
+; This subroutine uses INT 10H function 1Ah to determine the video BIOS\r
+; Display Combination Code (DCC) for each video subsystem present.\r
+;\r
+\r
+FindPS2 PROC near\r
+\r
+ mov ax,1A00h\r
+ int 10h ; call video BIOS for info\r
+\r
+ cmp al,1Ah\r
+ jne @@L13 ; exit if function not supported (i.e.,\r
+ ; no MCGA or VGA in system)\r
+\r
+; convert BIOS DCCs into specific subsystems & displays\r
+\r
+ mov cx,bx\r
+ xor bh,bh ; BX := DCC for active subsystem\r
+\r
+ or ch,ch\r
+ jz @@L11 ; jump if only one subsystem present\r
+\r
+ mov bl,ch ; BX := inactive DCC\r
+ add bx,bx\r
+ mov ax,[bx+offset DCCtable]\r
+\r
+ mov Device1,ax\r
+\r
+ mov bl,cl\r
+ xor bh,bh ; BX := active DCC\r
+\r
+@@L11: add bx,bx\r
+ mov ax,[bx+offset DCCtable]\r
+\r
+ mov Device0,ax\r
+\r
+; reset flags for subsystems that have been ruled out\r
+\r
+ mov byte ptr CGAflag,FALSE\r
+ mov byte ptr EGAflag,FALSE\r
+ mov byte ptr Monoflag,FALSE\r
+\r
+ lea bx,Video0Type[di] ; if the BIOS reported an MDA ...\r
+ cmp byte ptr [bx],MDA\r
+ je @@L12\r
+\r
+ lea bx,Video1Type[di]\r
+ cmp byte ptr [bx],MDA\r
+ jne @@L13\r
+\r
+@@L12: mov word ptr [bx],0 ; ... Hercules can't be ruled out\r
+ mov byte ptr Monoflag,TRUE\r
+\r
+@@L13: ret\r
+\r
+FindPS2 ENDP\r
+\r
+\r
+;\r
+; FindEGA\r
+;\r
+; Look for an EGA. This is done by making a call to an EGA BIOS function\r
+; which doesn't exist in the default (MDA, CGA) BIOS.\r
+\r
+FindEGA PROC near ; Caller: AH = flags\r
+ ; Returns: AH = flags\r
+ ; Video0Type and\r
+ ; Display0Type updated\r
+\r
+ mov bl,10h ; BL := 10h (return EGA info)\r
+ mov ah,12h ; AH := INT 10H function number\r
+ int 10h ; call EGA BIOS for info\r
+ ; if EGA BIOS is present,\r
+ ; BL <> 10H\r
+ ; CL = switch setting\r
+ cmp bl,10h\r
+ je @@L22 ; jump if EGA BIOS not present\r
+\r
+ mov al,cl\r
+ shr al,1 ; AL := switches/2\r
+ mov bx,offset EGADisplays\r
+ xlat ; determine display type from switches\r
+ mov ah,al ; AH := display type\r
+ mov al,EGA ; AL := subystem type\r
+ call FoundDevice\r
+\r
+ cmp ah,MDADisplay\r
+ je @@L21 ; jump if EGA has a monochrome display\r
+\r
+ mov CGAflag,FALSE ; no CGA if EGA has color display\r
+ jmp short @@L22\r
+\r
+@@L21: mov Monoflag,FALSE ; EGA has a mono display, so MDA and\r
+ ; Hercules are ruled out\r
+@@L22: ret\r
+\r
+FindEGA ENDP\r
+\r
+;\r
+; FindCGA\r
+;\r
+; This is done by looking for the CGA's 6845 CRTC at I/O port 3D4H.\r
+;\r
+FindCGA PROC near ; Returns: VIDstruct updated\r
+\r
+ mov dx,3D4h ; DX := CRTC address port\r
+ call Find6845\r
+ jc @@L31 ; jump if not present\r
+\r
+ mov al,CGA\r
+ mov ah,CGADisplay\r
+ call FoundDevice\r
+\r
+@@L31: ret\r
+\r
+FindCGA ENDP\r
+\r
+;\r
+; FindMono\r
+;\r
+; This is done by looking for the MDA's 6845 CRTC at I/O port 3B4H. If\r
+; a 6845 is found, the subroutine distinguishes between an MDA\r
+; and a Hercules adapter by monitoring bit 7 of the CRT Status byte.\r
+; This bit changes on Hercules adapters but does not change on an MDA.\r
+;\r
+; The various Hercules adapters are identified by bits 4 through 6 of\r
+; the CRT Status value:\r
+;\r
+; 000b = HGC\r
+; 001b = HGC+\r
+; 101b = InColor card\r
+;\r
+\r
+FindMono PROC near ; Returns: VIDstruct updated\r
+\r
+ mov dx,3B4h ; DX := CRTC address port\r
+ call Find6845\r
+ jc @@L44 ; jump if not present\r
+\r
+ mov dl,0BAh ; DX := 3BAh (status port)\r
+ in al,dx\r
+ and al,80h\r
+ mov ah,al ; AH := bit 7 (vertical sync on HGC)\r
+\r
+ mov cx,8000h ; do this 32768 times\r
+@@L41: in al,dx\r
+ and al,80h ; isolate bit 7\r
+ cmp ah,al\r
+ loope @@L41 ; wait for bit 7 to change\r
+ jne @@L42 ; if bit 7 changed, it's a Hercules\r
+\r
+ mov al,MDA ; if bit 7 didn't change, it's an MDA\r
+ mov ah,MDADisplay\r
+ call FoundDevice\r
+ jmp short @@L44\r
+\r
+@@L42: in al,dx\r
+ mov dl,al ; DL := value from status port\r
+ and dl,01110000b ; mask bits 4 thru 6\r
+\r
+ mov ah,MDADisplay ; assume it's a monochrome display\r
+\r
+ mov al,HGCPlus ; look for an HGC+\r
+ cmp dl,00010000b\r
+ je @@L43 ; jump if it's an HGC+\r
+\r
+ mov al,HGC ; look for an InColor card or HGC\r
+ cmp dl,01010000b\r
+ jne @@L43 ; jump if it's not an InColor card\r
+\r
+ mov al,InColor ; it's an InColor card\r
+ mov ah,EGAColorDisplay\r
+\r
+@@L43: call FoundDevice\r
+\r
+@@L44: ret\r
+\r
+FindMono ENDP\r
+\r
+;\r
+; Find6845\r
+;\r
+; This routine detects the presence of the CRTC on a MDA, CGA or HGC.\r
+; The technique is to write and read register 0Fh of the chip (cursor\r
+; low). If the same value is read as written, assume the chip is\r
+; present at the specified port addr.\r
+;\r
+\r
+Find6845 PROC near ; Caller: DX = port addr\r
+ ; Returns: cf set if not present\r
+ mov al,0Fh\r
+ out dx,al ; select 6845 reg 0Fh (Cursor Low)\r
+ inc dx\r
+ in al,dx ; AL := current Cursor Low value\r
+ mov ah,al ; preserve in AH\r
+ mov al,66h ; AL := arbitrary value\r
+ out dx,al ; try to write to 6845\r
+\r
+ mov cx,100h\r
+@@L51: loop @@L51 ; wait for 6845 to respond\r
+\r
+ in al,dx\r
+ xchg ah,al ; AH := returned value\r
+ ; AL := original value\r
+ out dx,al ; restore original value\r
+\r
+ cmp ah,66h ; test whether 6845 responded\r
+ je @@L52 ; jump if it did (cf is reset)\r
+\r
+ stc ; set carry flag if no 6845 present\r
+\r
+@@L52: ret\r
+\r
+Find6845 ENDP\r
+\r
+\r
+;\r
+; FindActive\r
+;\r
+; This subroutine stores the currently active device as Device0. The\r
+; current video mode determines which subsystem is active.\r
+;\r
+\r
+FindActive PROC near\r
+\r
+ cmp word ptr Device1,0\r
+ je @@L63 ; exit if only one subsystem\r
+\r
+ cmp Video0Type[di],4 ; exit if MCGA or VGA present\r
+ jge @@L63 ; (INT 10H function 1AH\r
+ cmp Video1Type[di],4 ; already did the work)\r
+ jge @@L63\r
+\r
+ mov ah,0Fh\r
+ int 10h ; AL := current BIOS video mode\r
+\r
+ and al,7\r
+ cmp al,7 ; jump if monochrome\r
+ je @@L61 ; (mode 7 or 0Fh)\r
+\r
+ cmp Display0Type[di],MDADisplay\r
+ jne @@L63 ; exit if Display0 is color\r
+ jmp short @@L62\r
+\r
+@@L61: cmp Display0Type[di],MDADisplay\r
+ je @@L63 ; exit if Display0 is monochrome\r
+\r
+@@L62: mov ax,Device0 ; make Device0 currently active\r
+ xchg ax,Device1\r
+ mov Device0,ax\r
+\r
+@@L63: ret\r
+\r
+FindActive ENDP\r
+\r
+\r
+;\r
+; FoundDevice\r
+;\r
+; This routine updates the list of subsystems.\r
+;\r
+\r
+FoundDevice PROC near ; Caller: AH = display #\r
+ ; AL = subsystem #\r
+ ; Destroys: BX\r
+ lea bx,Video0Type[di]\r
+ cmp byte ptr [bx],0\r
+ je @@L71 ; jump if 1st subsystem\r
+\r
+ lea bx,Video1Type[di] ; must be 2nd subsystem\r
+\r
+@@L71: mov [bx],ax ; update list entry\r
+ ret\r
+\r
+FoundDevice ENDP\r
+\r
+IDEAL\r
+\r
+\r
+\r
+END\r
--- /dev/null
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+;=================================\r
+;\r
+; CGA view manager routines\r
+;\r
+;=================================\r
+\r
+;============================================================================\r
+;\r
+; All of these routines draw into a floating virtual screen segment in main\r
+; memory. bufferofs points to the origin of the drawing page in screenseg.\r
+; The routines that write out words must take into account buffer wrapping\r
+; and not write a word at 0xffff (which causes an exception on 386s).\r
+;\r
+; The direction flag should be clear\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+plotpixels db 0c0h,030h,0ch,03h\r
+colorbyte db 000000b,01010101b,10101010b,11111111b\r
+colorword dw 0,5555h,0aaaah,0ffffh\r
+\r
+CODESEG\r
+\r
+;============================================================================\r
+;\r
+; VW_Plot (int x,y,color)\r
+;\r
+;============================================================================\r
+\r
+\r
+PROC VW_Plot x:WORD, y:WORD, color:WORD\r
+PUBLIC VW_Plot\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov di,[bufferofs]\r
+ mov bx,[y]\r
+ shl bx,1\r
+ add di,[ylookup+bx]\r
+ mov bx,[x]\r
+ mov ax,bx\r
+ shr ax,1\r
+ shr ax,1\r
+ add di,ax ; di = byte on screen\r
+\r
+ and bx,3\r
+ mov ah,[plotpixels+bx]\r
+ mov bx,[color]\r
+ mov cl,[colorbyte+bx]\r
+ and cl,ah\r
+ not ah\r
+\r
+ mov al,[es:di]\r
+ and al,ah ; mask off other pixels\r
+ or al,cl\r
+ stosb\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Vlin (int yl,yh,x,color)\r
+;\r
+;============================================================================\r
+\r
+PROC VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD\r
+PUBLIC VW_Vlin\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov di,[bufferofs]\r
+ mov bx,[yl]\r
+ shl bx,1\r
+ add di,[ylookup+bx]\r
+ mov bx,[x]\r
+ mov ax,bx\r
+ shr ax,1\r
+ shr ax,1\r
+ add di,ax ; di = byte on screen\r
+\r
+ and bx,3\r
+ mov ah,[plotpixels+bx]\r
+ mov bx,[color]\r
+ mov bl,[colorbyte+bx]\r
+ and bl,ah\r
+ not ah\r
+\r
+ mov cx,[yh]\r
+ sub cx,[yl]\r
+ inc cx ;number of pixels to plot\r
+\r
+ mov dx,[linewidth]\r
+\r
+@@plot:\r
+ mov al,[es:di]\r
+ and al,ah ; mask off other pixels\r
+ or al,bl\r
+ mov [es:di],al\r
+ add di,dx\r
+ loop @@plot\r
+\r
+ ret\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;===================\r
+;\r
+; VW_DrawTile8\r
+;\r
+; xcoord in bytes (8 pixels), ycoord in pixels\r
+; All Tile8s are in one grseg, so an offset is calculated inside it\r
+;\r
+; DONE\r
+;\r
+;===================\r
+\r
+PROC VW_DrawTile8 xcoord:WORD, ycoord:WORD, tile:WORD\r
+PUBLIC VW_DrawTile8\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov di,[bufferofs]\r
+ add di,[xcoord]\r
+ mov bx,[ycoord]\r
+ shl bx,1\r
+ add di,[ylookup+bx]\r
+\r
+ mov bx,[linewidth]\r
+ sub bx,2\r
+\r
+ mov si,[tile]\r
+ shl si,1\r
+ shl si,1\r
+ shl si,1\r
+ shl si,1\r
+\r
+ mov ds,[grsegs+STARTTILE8*2] ; segment for all tile8s\r
+\r
+;\r
+; start drawing\r
+;\r
+\r
+REPT 7\r
+ movsb ;no word moves because of segment wrapping\r
+ movsb\r
+ add di,bx\r
+ENDM\r
+ movsb\r
+ movsb\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MaskBlock\r
+;\r
+; Draws a masked block shape to the screen. bufferofs is NOT accounted for.\r
+; The mask comes first, then the data. Seperate unwound routines are used\r
+; to speed drawing.\r
+;\r
+; Mask blocks will allways be an even width because of the way IGRAB works\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+UNWOUNDMASKS = 18\r
+\r
+\r
+maskroutines dw mask0,mask0,mask2E,mask2O,mask4E,mask4O\r
+ dw mask6E,mask6O,mask8E,mask8O,mask10E,mask10O\r
+ dw mask12E,mask12O,mask14E,mask14O,mask16E,mask16O\r
+ dw mask18E,mask18O\r
+\r
+\r
+routinetouse dw ?\r
+\r
+CODESEG\r
+\r
+PROC VW_MaskBlock segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD\r
+PUBLIC VW_MaskBlock\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov di,[wide]\r
+ mov dx,[linewidth]\r
+ sub dx,di ;dx = delta to start of next line\r
+\r
+ mov bx,[planesize] ; si+bx = data location\r
+\r
+ cmp di,UNWOUNDMASKS\r
+ jbe @@unwoundroutine\r
+\r
+;==============\r
+;\r
+; General purpose masked block drawing. This could be optimised into\r
+; four routines to use words, but few play loop sprites should be this big!\r
+;\r
+;==============\r
+\r
+ mov [ss:linedelta],dx\r
+ mov ds,[segm]\r
+ mov si,[ofs]\r
+ mov di,[dest]\r
+ mov dx,[height] ;scan lines to draw\r
+\r
+@@lineloopgen:\r
+ mov cx,[wide]\r
+@@byteloop:\r
+ mov al,[es:di]\r
+ and al,[si]\r
+ or al,[bx+si]\r
+ inc si\r
+ stosb\r
+ loop @@byteloop\r
+\r
+ add di,[ss:linedelta]\r
+ dec dx\r
+ jnz @@lineloopgen\r
+\r
+mask0:\r
+ mov ax,ss\r
+ mov ds,ax\r
+ ret ;width of 0 = no drawing\r
+\r
+\r
+;=================\r
+;\r
+; use the unwound routines\r
+;\r
+;=================\r
+\r
+@@unwoundroutine:\r
+ shr di,1 ;we only have even width unwound routines\r
+ mov cx,[dest]\r
+ shr cx,1\r
+ rcl di,1 ;shift a 1 in if destination is odd\r
+ shl di,1\r
+ mov ax,[maskroutines+di] ;call the right routine\r
+\r
+ mov ds,[segm]\r
+ mov si,[ofs]\r
+ mov di,[dest]\r
+ mov cx,[height] ;scan lines to draw\r
+\r
+ jmp ax ;draw it\r
+\r
+;=================\r
+;\r
+; Horizontally unwound routines to draw certain masked blocks faster\r
+;\r
+;=================\r
+\r
+MACRO MASKBYTE\r
+ mov al,[es:di]\r
+ and al,[si]\r
+ or al,[bx+si]\r
+ inc si\r
+ stosb\r
+ENDM\r
+\r
+MACRO MASKWORD\r
+ mov ax,[es:di]\r
+ and ax,[si]\r
+ or ax,[bx+si]\r
+ inc si\r
+ inc si\r
+ stosw\r
+ENDM\r
+\r
+MACRO SPRITELOOP addr\r
+ add di,dx\r
+ loop addr\r
+ mov ax,ss\r
+ mov ds,ax\r
+ ret\r
+ENDM\r
+\r
+\r
+EVEN\r
+mask2E:\r
+ MASKWORD\r
+ SPRITELOOP mask2E\r
+\r
+EVEN\r
+mask2O:\r
+ MASKBYTE\r
+ MASKBYTE\r
+ SPRITELOOP mask2O\r
+\r
+EVEN\r
+mask4E:\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask4E\r
+\r
+EVEN\r
+mask4O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask4O\r
+\r
+EVEN\r
+mask6E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask6E\r
+\r
+EVEN\r
+mask6O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask6O\r
+\r
+EVEN\r
+mask8E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask8E\r
+\r
+EVEN\r
+mask8O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask8O\r
+\r
+EVEN\r
+mask10E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask10E\r
+\r
+EVEN\r
+mask10O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask10O\r
+\r
+EVEN\r
+mask12E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask12E\r
+\r
+EVEN\r
+mask12O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask12O\r
+\r
+EVEN\r
+mask14E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask14E\r
+\r
+EVEN\r
+mask14O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask14O\r
+\r
+EVEN\r
+mask16E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask16E\r
+\r
+EVEN\r
+mask16O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask16O\r
+\r
+EVEN\r
+mask18E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask18E\r
+\r
+EVEN\r
+mask18O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask18O\r
+\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_ScreenToScreen\r
+;\r
+; Basic block copy routine. Copies one block of screen memory to another,\r
+; bufferofs is NOT accounted for.\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+PROC VW_ScreenToScreen source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToScreen\r
+USES SI,DI\r
+\r
+ mov bx,[linewidth]\r
+ sub bx,[wide]\r
+\r
+ mov ax,[screenseg]\r
+ mov es,ax\r
+ mov ds,ax\r
+\r
+ mov si,[source]\r
+ mov di,[dest] ;start at same place in all planes\r
+ mov dx,[height] ;scan lines to draw\r
+ mov ax,[wide]\r
+;\r
+; if the width, source, and dest are all even, use word moves\r
+; This is allways the case in the CGA refresh\r
+;\r
+ test ax,1\r
+ jnz @@bytelineloop\r
+ test si,1\r
+ jnz @@bytelineloop\r
+ test di,1\r
+ jnz @@bytelineloop\r
+\r
+ shr ax,1\r
+@@wordlineloop:\r
+ mov cx,ax\r
+ rep movsw\r
+ add si,bx\r
+ add di,bx\r
+\r
+ dec dx\r
+ jnz @@wordlineloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+@@bytelineloop:\r
+ mov cx,ax\r
+ rep movsb\r
+ add si,bx\r
+ add di,bx\r
+\r
+ dec dx\r
+ jnz @@bytelineloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; of width by height data, and draws it to dest in the virtual screen,\r
+; based on linewidth. bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+memtoscreentable dw eventoeven,eventoodd,oddtoeven,oddtoodd\r
+\r
+CODESEG\r
+\r
+\r
+PROC VW_MemToScreen source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov bx,[linewidth]\r
+ sub bx,[wide]\r
+\r
+ mov ds,[source]\r
+\r
+ xor si,si ;block is segment aligned\r
+\r
+ xor di,di\r
+ shr [wide],1 ;change wide to words, and see if carry is set\r
+ rcl di,1 ;1 if wide is odd\r
+ mov ax,[dest]\r
+ shr ax,1\r
+ rcl di,1 ;shift a 1 in if destination is odd\r
+ shl di,1 ;to index into a word width table\r
+ mov dx,[height] ;scan lines to draw\r
+ mov ax,[wide]\r
+ jmp [ss:memtoscreentable+di] ;call the right routine\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an even destination address\r
+;\r
+;==============\r
+\r
+eventoeven:\r
+ mov di,[dest] ;start at same place in all planes\r
+EVEN\r
+@@lineloopEE:\r
+ mov cx,ax\r
+ rep movsw\r
+ add di,bx\r
+ dec dx\r
+ jnz @@lineloopEE\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an even video address\r
+;\r
+;==============\r
+\r
+oddtoeven:\r
+ mov di,[dest] ;start at same place in all planes\r
+EVEN\r
+@@lineloopOE:\r
+ mov cx,ax\r
+ rep movsw\r
+ movsb ;copy the last byte\r
+ add di,bx\r
+ dec dx\r
+ jnz @@lineloopOE\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an odd video address\r
+;\r
+;==============\r
+\r
+eventoodd:\r
+ mov di,[dest] ;start at same place in all planes\r
+ dec ax ;one word has to be handled seperately\r
+EVEN\r
+@@lineloopEO:\r
+ movsb\r
+ mov cx,ax\r
+ rep movsw\r
+ movsb\r
+ add di,bx\r
+ dec dx\r
+ jnz @@lineloopEO\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an odd video address\r
+;\r
+;==============\r
+\r
+oddtoodd:\r
+ mov di,[dest] ;start at same place in all planes\r
+EVEN\r
+@@lineloopOO:\r
+ movsb\r
+ mov cx,ax\r
+ rep movsw\r
+ add di,bx\r
+ dec dx\r
+ jnz @@lineloopOO\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+ ret\r
+\r
+\r
+ENDP\r
+\r
+;===========================================================================\r
+;\r
+; VW_ScreenToMem\r
+;\r
+; Copies a block of video memory to main memory, in order from planes 0-3.\r
+; This could be optimized along the lines of VW_MemToScreen to take advantage\r
+; of word copies, but this is an infrequently called routine.\r
+;\r
+; DONE\r
+;\r
+;===========================================================================\r
+\r
+PROC VW_ScreenToMem source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToMem\r
+USES SI,DI\r
+\r
+ mov es,[dest]\r
+\r
+ mov bx,[linewidth]\r
+ sub bx,[wide]\r
+\r
+ mov ds,[screenseg]\r
+\r
+ xor di,di\r
+\r
+ mov si,[source]\r
+ mov dx,[height] ;scan lines to draw\r
+\r
+@@lineloop:\r
+ mov cx,[wide]\r
+ rep movsb\r
+\r
+ add si,bx\r
+\r
+ dec dx\r
+ jnz @@lineloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+;\r
+; MISC CGA ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+;==============\r
+;\r
+; VW_SetScreen\r
+;\r
+; DONE\r
+;\r
+;==============\r
+\r
+PROC VW_SetScreen crtc:WORD\r
+PUBLIC VW_SetScreen\r
+\r
+;\r
+; for some reason, my XT's EGA card doesn't like word outs to the CRTC\r
+; index...\r
+;\r
+ cli\r
+\r
+ mov cx,[crtc]\r
+ mov dx,CRTC_INDEX\r
+ mov al,0ch ;start address high register\r
+ out dx,al\r
+ inc dx\r
+ mov al,ch\r
+ out dx,al\r
+ dec dx\r
+ mov al,0dh ;start address low register\r
+ out dx,al\r
+ mov al,cl\r
+ inc dx\r
+ out dx,al\r
+\r
+ sti\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+if NUMFONT+NUMFONTM\r
+\r
+;===========================================================================\r
+;\r
+; GENERAL FONT DRAWING ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+DATASEG\r
+\r
+px dw ? ; proportional character drawing coordinates\r
+py dw ?\r
+pdrawmode db 11000b ; 8 = OR, 24 = XOR, put in GC_DATAROTATE\r
+fontcolor db 15 ;0-15 mapmask value\r
+\r
+PUBLIC px,py,pdrawmode,fontcolor\r
+\r
+;\r
+; offsets in font structure\r
+;\r
+pcharheight = 0 ;lines high\r
+charloc = 2 ;pointers to every character\r
+charwidth = 514 ;every character's width in pixels\r
+\r
+\r
+propchar dw ? ; the character number to shift\r
+stringptr dw ?,?\r
+\r
+fontcolormask dw ? ; font color expands into this\r
+\r
+BUFFWIDTH = 100\r
+BUFFHEIGHT = 32 ; must be twice as high as font for masked fonts\r
+\r
+databuffer db BUFFWIDTH*BUFFHEIGHT dup (?)\r
+\r
+bufferwidth dw ? ; bytes with valid info / line\r
+bufferheight dw ? ; number of lines currently used\r
+\r
+bufferbyte dw ?\r
+bufferbit dw ?\r
+PUBLIC bufferwidth,bufferheight,bufferbyte,bufferbit\r
+\r
+screenspot dw ? ; where the buffer is going\r
+\r
+bufferextra dw ? ; add at end of a line copy\r
+screenextra dw ?\r
+\r
+CODESEG\r
+\r
+;======================\r
+;\r
+; Macros to table shift a byte of font\r
+;\r
+;======================\r
+\r
+MACRO SHIFTNOXOR\r
+ mov al,[es:bx] ; source\r
+ xor ah,ah\r
+ shl ax,1\r
+ mov si,ax\r
+ mov ax,[bp+si] ; table shift into two bytes\r
+ or [di],al ; or with first byte\r
+ inc di\r
+ mov [di],ah ; replace next byte\r
+ inc bx ; next source byte\r
+ENDM\r
+\r
+MACRO SHIFTWITHXOR\r
+ mov al,[es:bx] ; source\r
+ xor ah,ah\r
+ shl ax,1\r
+ mov si,ax\r
+ mov ax,[bp+si] ; table shift into two bytes\r
+ not ax\r
+ and [di],al ; and with first byte\r
+ inc di\r
+ mov [di],ah ; replace next byte\r
+ inc bx ; next source byte\r
+ENDM\r
+\r
+\r
+;=======================\r
+;\r
+; VWL_XORBuffer\r
+;\r
+; Pass buffer start in SI (somewhere in databuffer)\r
+; Draws the buffer to the screen buffer\r
+;\r
+;========================\r
+\r
+PROC VWL_XORBuffer NEAR\r
+USES BP\r
+ mov bl,[fontcolor]\r
+ xor bh,bh\r
+ shl bx,1\r
+ mov ax,[colorword+bx]\r
+ mov [fontcolormask],ax\r
+\r
+ mov es,[screenseg]\r
+ mov di,[screenspot]\r
+\r
+ mov bx,[bufferwidth] ;calculate offsets for end of each line\r
+ mov [bufferwidth],bx\r
+\r
+ or bx,bx\r
+ jnz @@isthere\r
+ ret ;nothing to draw\r
+\r
+@@isthere:\r
+ test bx,1\r
+ jnz @@odd\r
+ jmp @@even\r
+;\r
+; clear the last byte so word draws can be used\r
+;\r
+@@odd:\r
+ mov al,0\r
+line = 0\r
+REPT BUFFHEIGHT\r
+ mov [BYTE databuffer+BUFFWIDTH*line+bx],al\r
+line = line+1\r
+ENDM\r
+\r
+ inc bx\r
+@@even:\r
+ mov ax,[linewidth]\r
+ sub ax,bx\r
+ mov [screenextra],ax\r
+ mov ax,BUFFWIDTH\r
+ sub ax,bx\r
+ mov [bufferextra],ax\r
+ mov dx,bx\r
+ shr dx,1 ;word to copy\r
+\r
+ mov bx,[bufferheight] ;lines to copy\r
+ mov bp,[fontcolormask]\r
+@@lineloop:\r
+ mov cx,dx\r
+@@wordloop:\r
+ lodsw ;get a word from the buffer\r
+ and ax,bp\r
+ xor [es:di],ax ;draw it\r
+ add di,2\r
+ loop @@wordloop\r
+\r
+ add si,[bufferextra]\r
+ add di,[screenextra]\r
+\r
+ dec bx\r
+ jnz @@lineloop\r
+\r
+ ret\r
+ENDP\r
+\r
+\r
+DATASEG\r
+\r
+;============================================================================\r
+;\r
+; NON MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfont\r
+\r
+DATASEG\r
+\r
+shiftdrawtable dw 0,shift1wide,shift2wide,shift3wide,shift4wide\r
+ dw shift5wide,shift6wide\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC ShiftPropChar NEAR\r
+\r
+ mov es,[grsegs+STARTFONT*2] ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+ mov si,[es:charwidth+bx]\r
+ and si,0ffh ;SI hold width in pixels\r
+ shl bx,1\r
+ mov bx,[es:charloc+bx] ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+ mov di,[bufferbit]\r
+ shl di,1\r
+ mov bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+ mov di,OFFSET databuffer\r
+ add di,[bufferbyte] ;DI holds pointer to buffer\r
+\r
+ mov cx,[bufferbit]\r
+ add cx,si ;add twice because pixel == two bits\r
+ add cx,si ;new bit position\r
+ mov ax,cx\r
+ and ax,7\r
+ mov [bufferbit],ax ;new bit position\r
+ mov ax,cx\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1\r
+ add [bufferbyte],ax ;new byte position\r
+\r
+ add si,3\r
+ shr si,1\r
+ shr si,1 ;bytes the character is wide\r
+ shl si,1 ;*2 to look up in shiftdrawtable\r
+\r
+ mov cx,[es:pcharheight]\r
+ mov dx,BUFFWIDTH\r
+ jmp [ss:shiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+shift1wide:\r
+ dec dx\r
+EVEN\r
+@@loop1:\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop1\r
+\r
+ ret\r
+\r
+;\r
+; two byte character\r
+;\r
+shift2wide:\r
+ dec dx\r
+ dec dx\r
+EVEN\r
+@@loop2:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop2\r
+\r
+ ret\r
+\r
+;\r
+; three byte character\r
+;\r
+shift3wide:\r
+ sub dx,3\r
+EVEN\r
+@@loop3:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop3\r
+\r
+ ret\r
+\r
+\r
+;\r
+; four byte character\r
+;\r
+shift4wide:\r
+ sub dx,4\r
+EVEN\r
+@@loop4:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop4\r
+\r
+ ret\r
+\r
+\r
+;\r
+; five byte character\r
+;\r
+shift5wide:\r
+ sub dx,5\r
+EVEN\r
+@@loop5:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop5\r
+\r
+ ret\r
+\r
+;\r
+; six byte character\r
+;\r
+shift6wide:\r
+ sub dx,6\r
+EVEN\r
+@@loop6:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop6\r
+\r
+ ret\r
+\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+;==================\r
+\r
+CODESEG\r
+\r
+PROC VW_DrawPropString string:DWORD\r
+PUBLIC VW_DrawPropString\r
+USES SI,DI\r
+\r
+;\r
+; proportional spaceing, which clears the buffer ahead of it, so only\r
+; clear the first collumn\r
+;\r
+ mov al,0\r
+line = 0\r
+REPT BUFFHEIGHT\r
+ mov [BYTE databuffer+BUFFWIDTH*line],al\r
+line = line+1\r
+ENDM\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+@@shiftchars:\r
+ mov ax,[px]\r
+ and ax,3\r
+ shl ax,1 ;one pixel == two bits\r
+ mov [bufferbit],ax\r
+ mov [bufferbyte],0\r
+\r
+ mov ax,[WORD string]\r
+ mov [stringptr],ax\r
+ mov ax,[WORD string+2]\r
+ mov [stringptr+2],ax\r
+\r
+@@shiftone:\r
+ mov es,[stringptr+2]\r
+ mov bx,[stringptr]\r
+ inc [stringptr]\r
+ mov bx,[es:bx]\r
+ xor bh,bh\r
+ or bl,bl\r
+ jz @@allshifted\r
+ call ShiftPropChar\r
+ jmp @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+ mov bx,[py]\r
+ shl bx,1\r
+ mov di,[ylookup+bx]\r
+ add di,[bufferofs]\r
+ add di,[panadjust]\r
+\r
+ mov ax,[px]\r
+ shr ax,1\r
+ shr ax,1 ;x location in bytes\r
+ add di,ax\r
+ mov [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+ mov ax,[bufferbyte]\r
+ shl ax,1\r
+ shl ax,1\r
+ mov bx,[bufferbit]\r
+ shr bx,1 ;two bits == one pixel\r
+ or ax,bx\r
+ add [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+ mov ax,[bufferbyte]\r
+ test [bufferbit],7\r
+ jz @@go\r
+ inc ax ;so the partial byte also gets drawn\r
+@@go:\r
+ mov [bufferwidth],ax\r
+ mov es,[grsegs+STARTFONT*2]\r
+ mov ax,[es:pcharheight]\r
+ mov [bufferheight],ax\r
+\r
+ mov si,OFFSET databuffer\r
+ call VWL_XORBuffer\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+endif ;numfont\r
+\r
+;============================================================================\r
+;\r
+; MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfontm\r
+\r
+DATASEG\r
+\r
+mshiftdrawtable dw 0,mshift1wide,mshift2wide,mshift3wide\r
+\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftMPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC ShiftMPropChar NEAR\r
+\r
+ mov es,[grsegs+STARTFONTM*2] ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+ mov si,[es:charwidth+bx]\r
+ and si,0ffh ;SI hold width in pixels\r
+ shl bx,1\r
+ mov bx,[es:charloc+bx] ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+ mov di,[bufferbit]\r
+ shl di,1\r
+ mov bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+ mov di,OFFSET databuffer\r
+ add di,[bufferbyte] ;DI holds pointer to buffer\r
+\r
+;\r
+; advance position by character width\r
+;\r
+ mov cx,[bufferbit]\r
+ add cx,si ;new bit position\r
+ mov ax,cx\r
+ and ax,7\r
+ mov [bufferbit],ax ;new bit position\r
+ mov ax,cx\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1\r
+ add [bufferbyte],ax ;new byte position\r
+\r
+ add si,7\r
+ shr si,1\r
+ shr si,1\r
+ shr si,1 ;bytes the character is wide\r
+ shl si,1 ;*2 to look up in shiftdrawtable\r
+\r
+ mov cx,[es:pcharheight]\r
+ mov dx,BUFFWIDTH\r
+ jmp [ss:mshiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+mshift1wide:\r
+ dec dx\r
+\r
+EVEN\r
+@@loop1m:\r
+ SHIFTWITHXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop1m\r
+\r
+ mov cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop1:\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop1\r
+\r
+ ret\r
+\r
+;\r
+; two byte character\r
+;\r
+mshift2wide:\r
+ dec dx\r
+ dec dx\r
+EVEN\r
+@@loop2m:\r
+ SHIFTWITHXOR\r
+ SHIFTWITHXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop2m\r
+\r
+ mov cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop2:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop2\r
+\r
+ ret\r
+\r
+;\r
+; three byte character\r
+;\r
+mshift3wide:\r
+ sub dx,3\r
+EVEN\r
+@@loop3m:\r
+ SHIFTWITHXOR\r
+ SHIFTWITHXOR\r
+ SHIFTWITHXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop3m\r
+\r
+ mov cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop3:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop3\r
+\r
+ ret\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawMPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+;==================\r
+\r
+\r
+\r
+PROC VW_DrawMPropString string:DWORD\r
+PUBLIC VW_DrawMPropString\r
+USES SI,DI\r
+\r
+;\r
+; clear out the first byte of the buffer, the rest will automatically be\r
+; cleared as characters are drawn into it\r
+;\r
+ mov es,[grsegs+STARTFONTM*2]\r
+ mov dx,[es:pcharheight]\r
+ mov di,OFFSET databuffer\r
+ mov ax,ds\r
+ mov es,ax\r
+ mov bx,BUFFWIDTH-1\r
+\r
+ mov cx,dx\r
+ mov al,0ffh\r
+@@maskfill:\r
+ stosb ; fill the mask part with $ff\r
+ add di,bx\r
+ loop @@maskfill\r
+\r
+ mov cx,dx\r
+ xor al,al\r
+@@datafill:\r
+ stosb ; fill the data part with $0\r
+ add di,bx\r
+ loop @@datafill\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+ mov ax,[px]\r
+ and ax,7\r
+ mov [bufferbit],ax\r
+ mov [bufferbyte],0\r
+\r
+ mov ax,[WORD string]\r
+ mov [stringptr],ax\r
+ mov ax,[WORD string+2]\r
+ mov [stringptr+2],ax\r
+\r
+@@shiftone:\r
+ mov es,[stringptr+2]\r
+ mov bx,[stringptr]\r
+ inc [stringptr]\r
+ mov bx,[es:bx]\r
+ xor bh,bh\r
+ or bl,bl\r
+ jz @@allshifted\r
+ call ShiftMPropChar\r
+ jmp @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+ mov bx,[py]\r
+ shl bx,1\r
+ mov di,[ylookup+bx]\r
+ add di,[bufferofs]\r
+\r
+ mov ax,[px]\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1 ;x location in bytes\r
+ add di,ax\r
+ mov [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+ mov ax,[bufferbyte]\r
+ shl ax,1\r
+ shl ax,1\r
+ shl ax,1\r
+ or ax,[bufferbit]\r
+ add [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+ mov ax,[bufferbyte]\r
+ test [bufferbit],7\r
+ jz @@go\r
+ inc ax ;so the partial byte also gets drawn\r
+@@go:\r
+ mov [bufferwidth],ax\r
+ mov es,[grsegs+STARTFONTM*2]\r
+ mov ax,[es:pcharheight]\r
+ mov [bufferheight],ax\r
+\r
+ mov si,OFFSET databuffer\r
+ call BufferToScreen ; cut out mask\r
+ ; or in data\r
+ call BufferToScreen ; SI is still in the right position in buffer\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+endif ; if numfontm\r
+\r
+endif ; if fonts\r
--- /dev/null
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+;=================================\r
+;\r
+; EGA view manager routines\r
+;\r
+;=================================\r
+\r
+;============================================================================\r
+;\r
+; All EGA drawing routines that write out words need to have alternate forms\r
+; for starting on even and odd addresses, because writing a word at segment\r
+; offset 0xffff causes an exception! To work around this, write a single\r
+; byte out to make the address even, so it wraps cleanly at the end.\r
+;\r
+; All of these routines assume read/write mode 0, and will allways return\r
+; in that state.\r
+; The direction flag should be clear\r
+; readmap/writemask is left in an undefined state\r
+;\r
+;============================================================================\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Plot (int x,y,color)\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+plotpixels db 128,64,32,16,8,4,2,1\r
+\r
+CODESEG\r
+\r
+PROC VW_Plot x:WORD, y:WORD, color:WORD\r
+PUBLIC VW_Plot\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov dx,SC_INDEX\r
+ mov ax,SC_MAPMASK+15*256\r
+ WORDOUT\r
+\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_MODE+2*256 ;write mode 2\r
+ WORDOUT\r
+\r
+ mov di,[bufferofs]\r
+ mov bx,[y]\r
+ shl bx,1\r
+ add di,[ylookup+bx]\r
+ mov bx,[x]\r
+ mov ax,bx\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1\r
+ add di,ax ; di = byte on screen\r
+\r
+ and bx,7\r
+ mov ah,[plotpixels+bx]\r
+ mov al,GC_BITMASK ;mask off other pixels\r
+ WORDOUT\r
+\r
+ mov bl,[BYTE color]\r
+ xchg bl,[es:di] ; load latches and write pixel\r
+\r
+ mov dx,GC_INDEX\r
+ mov ah,0ffh ;no bit mask\r
+ WORDOUT\r
+ mov ax,GC_MODE+0*256 ;write mode 0\r
+ WORDOUT\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Vlin (int yl,yh,x,color)\r
+;\r
+;============================================================================\r
+\r
+PROC VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD\r
+PUBLIC VW_Vlin\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov dx,SC_INDEX\r
+ mov ax,SC_MAPMASK+15*256\r
+ WORDOUT\r
+\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_MODE+2*256 ;write mode 2\r
+ WORDOUT\r
+\r
+ mov di,[bufferofs]\r
+ mov bx,[yl]\r
+ shl bx,1\r
+ add di,[ylookup+bx]\r
+ mov bx,[x]\r
+ mov ax,bx\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1\r
+ add di,ax ; di = byte on screen\r
+\r
+ and bx,7\r
+ mov ah,[plotpixels+bx]\r
+ mov al,GC_BITMASK ;mask off other pixels\r
+ WORDOUT\r
+\r
+ mov cx,[yh]\r
+ sub cx,[yl]\r
+ inc cx ;number of pixels to plot\r
+\r
+ mov bh,[BYTE color]\r
+ mov dx,[linewidth]\r
+\r
+@@plot:\r
+ mov bl,bh\r
+ xchg bl,[es:di] ; load latches and write pixel\r
+ add di,dx\r
+\r
+ loop @@plot\r
+\r
+ mov dx,GC_INDEX\r
+ mov ah,0ffh ;no bit mask\r
+ WORDOUT\r
+ mov ax,GC_MODE+0*256 ;write mode 0\r
+ WORDOUT\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;===================\r
+;\r
+; VW_DrawTile8\r
+;\r
+; xcoord in bytes (8 pixels), ycoord in pixels\r
+; All Tile8s are in one grseg, so an offset is calculated inside it\r
+;\r
+;===================\r
+\r
+PROC VW_DrawTile8 xcoord:WORD, ycoord:WORD, tile:WORD\r
+PUBLIC VW_DrawTile8\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov di,[bufferofs]\r
+ add di,[xcoord]\r
+ mov bx,[ycoord]\r
+ shl bx,1\r
+ add di,[ylookup+bx]\r
+ mov [ss:screendest],di ;screen destination\r
+\r
+ mov bx,[linewidth]\r
+ dec bx\r
+\r
+ mov si,[tile]\r
+ shl si,1\r
+ shl si,1\r
+ shl si,1\r
+ shl si,1\r
+ shl si,1\r
+\r
+ mov ds,[grsegs+STARTTILE8*2] ; segment for all tile8s\r
+\r
+ mov cx,4 ;planes to draw\r
+ mov ah,0001b ;map mask\r
+\r
+ mov dx,SC_INDEX\r
+ mov al,SC_MAPMASK\r
+\r
+;\r
+; start drawing\r
+;\r
+\r
+@@planeloop:\r
+ WORDOUT\r
+ shl ah,1 ;shift plane mask over for next plane\r
+\r
+ mov di,[ss:screendest] ;start at same place in all planes\r
+\r
+REPT 7\r
+ movsb\r
+ add di,bx\r
+ENDM\r
+ movsb\r
+\r
+ loop @@planeloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MaskBlock\r
+;\r
+; Draws a masked block shape to the screen. bufferofs is NOT accounted for.\r
+; The mask comes first, then four planes of data.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+UNWOUNDMASKS = 10\r
+\r
+\r
+maskroutines dw mask0,mask0,mask1E,mask1E,mask2E,mask2O,mask3E,mask3O\r
+ dw mask4E,mask4O,mask5E,mask5O,mask6E,mask6O\r
+ dw mask7E,mask7O,mask8E,mask8O,mask9E,mask9O\r
+ dw mask10E,mask10O\r
+\r
+\r
+routinetouse dw ?\r
+\r
+CODESEG\r
+\r
+PROC VW_MaskBlock segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD\r
+PUBLIC VW_MaskBlock\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov [BYTE planemask],1\r
+ mov [BYTE planenum],0\r
+\r
+ mov di,[wide]\r
+ mov dx,[linewidth]\r
+ sub dx,[wide]\r
+ mov [linedelta],dx ;amount to add after drawing each line\r
+\r
+ mov bx,[planesize] ; si+bx = data location\r
+\r
+ cmp di,UNWOUNDMASKS\r
+ jbe @@unwoundroutine\r
+ mov [routinetouse],OFFSET generalmask\r
+ jmp NEAR @@startloop\r
+\r
+;=================\r
+;\r
+; use the unwound routines\r
+;\r
+;=================\r
+\r
+@@unwoundroutine:\r
+ mov cx,[dest]\r
+ shr cx,1\r
+ rcl di,1 ;shift a 1 in if destination is odd\r
+ shl di,1 ;to index into a word width table\r
+ mov ax,[maskroutines+di] ;call the right routine\r
+ mov [routinetouse],ax\r
+\r
+@@startloop:\r
+ mov ds,[segm]\r
+\r
+@@drawplane:\r
+ mov dx,SC_INDEX\r
+ mov al,SC_MAPMASK\r
+ mov ah,[ss:planemask]\r
+ WORDOUT\r
+ mov dx,GC_INDEX\r
+ mov al,GC_READMAP\r
+ mov ah,[ss:planenum]\r
+ WORDOUT\r
+\r
+ mov si,[ofs] ;start back at the top of the mask\r
+ mov di,[dest] ;start at same place in all planes\r
+ mov cx,[height] ;scan lines to draw\r
+ mov dx,[ss:linedelta]\r
+\r
+ jmp [ss:routinetouse] ;draw one plane\r
+planereturn: ;routine jmps back here\r
+\r
+ add bx,[ss:planesize] ;start of mask = start of next plane\r
+\r
+ inc [ss:planenum]\r
+ shl [ss:planemask],1 ;shift plane mask over for next plane\r
+ cmp [ss:planemask],10000b ;done all four planes?\r
+ jne @@drawplane\r
+\r
+mask0:\r
+ mov ax,ss\r
+ mov ds,ax\r
+ ret ;width of 0 = no drawing\r
+\r
+;==============\r
+;\r
+; General purpose masked block drawing. This could be optimised into\r
+; four routines to use words, but few play loop sprites should be this big!\r
+;\r
+;==============\r
+\r
+generalmask:\r
+ mov dx,cx\r
+\r
+@@lineloopgen:\r
+ mov cx,[wide]\r
+@@byteloop:\r
+ mov al,[es:di]\r
+ and al,[si]\r
+ or al,[bx+si]\r
+ inc si\r
+ stosb\r
+ loop @@byteloop\r
+\r
+ add di,[ss:linedelta]\r
+ dec dx\r
+ jnz @@lineloopgen\r
+ jmp planereturn\r
+\r
+;=================\r
+;\r
+; Horizontally unwound routines to draw certain masked blocks faster\r
+;\r
+;=================\r
+\r
+MACRO MASKBYTE\r
+ mov al,[es:di]\r
+ and al,[si]\r
+ or al,[bx+si]\r
+ inc si\r
+ stosb\r
+ENDM\r
+\r
+MACRO MASKWORD\r
+ mov ax,[es:di]\r
+ and ax,[si]\r
+ or ax,[bx+si]\r
+ inc si\r
+ inc si\r
+ stosw\r
+ENDM\r
+\r
+MACRO SPRITELOOP addr\r
+ add di,dx\r
+ loop addr\r
+ jmp planereturn\r
+ENDM\r
+\r
+\r
+EVEN\r
+mask1E:\r
+ MASKBYTE\r
+ SPRITELOOP mask1E\r
+\r
+EVEN\r
+mask2E:\r
+ MASKWORD\r
+ SPRITELOOP mask2E\r
+\r
+EVEN\r
+mask2O:\r
+ MASKBYTE\r
+ MASKBYTE\r
+ SPRITELOOP mask2O\r
+\r
+EVEN\r
+mask3E:\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask3E\r
+\r
+EVEN\r
+mask3O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ SPRITELOOP mask3O\r
+\r
+EVEN\r
+mask4E:\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask4E\r
+\r
+EVEN\r
+mask4O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask4O\r
+\r
+EVEN\r
+mask5E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask5E\r
+\r
+EVEN\r
+mask5O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask5O\r
+\r
+EVEN\r
+mask6E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask6E\r
+\r
+EVEN\r
+mask6O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask6O\r
+\r
+EVEN\r
+mask7E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask7E\r
+\r
+EVEN\r
+mask7O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask7O\r
+\r
+EVEN\r
+mask8E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask8E\r
+\r
+EVEN\r
+mask8O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask8O\r
+\r
+EVEN\r
+mask9E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask9E\r
+\r
+EVEN\r
+mask9O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask9O\r
+\r
+EVEN\r
+mask10E:\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ SPRITELOOP mask10E\r
+\r
+EVEN\r
+mask10O:\r
+ MASKBYTE\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKWORD\r
+ MASKBYTE\r
+ SPRITELOOP mask10O\r
+\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_ScreenToScreen\r
+;\r
+; Basic block copy routine. Copies one block of screen memory to another,\r
+; using write mode 1 (sets it and returns with write mode 0). bufferofs is\r
+; NOT accounted for.\r
+;\r
+;============================================================================\r
+\r
+PROC VW_ScreenToScreen source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToScreen\r
+USES SI,DI\r
+\r
+ pushf\r
+ cli\r
+\r
+ mov dx,SC_INDEX\r
+ mov ax,SC_MAPMASK+15*256\r
+ WORDOUT\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_MODE+1*256\r
+ WORDOUT\r
+\r
+ popf\r
+\r
+ mov bx,[linewidth]\r
+ sub bx,[wide]\r
+\r
+ mov ax,[screenseg]\r
+ mov es,ax\r
+ mov ds,ax\r
+\r
+ mov si,[source]\r
+ mov di,[dest] ;start at same place in all planes\r
+ mov dx,[height] ;scan lines to draw\r
+ mov ax,[wide]\r
+\r
+@@lineloop:\r
+ mov cx,ax\r
+ rep movsb\r
+ add si,bx\r
+ add di,bx\r
+\r
+ dec dx\r
+ jnz @@lineloop\r
+\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_MODE+0*256\r
+ WORDOUT\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; with four planes of width by height data, and draws it to dest in the\r
+; virtual screen, based on linewidth. bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+memtoscreentable dw eventoeven,eventoodd,oddtoeven,oddtoodd\r
+\r
+CODESEG\r
+\r
+\r
+PROC VW_MemToScreen source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov bx,[linewidth]\r
+ sub bx,[wide]\r
+\r
+ mov ds,[source]\r
+\r
+\r
+ xor si,si ;block is segment aligned\r
+\r
+ xor di,di\r
+ shr [wide],1 ;change wide to words, and see if carry is set\r
+ rcl di,1 ;1 if wide is odd\r
+ mov ax,[dest]\r
+ shr ax,1\r
+ rcl di,1 ;shift a 1 in if destination is odd\r
+ shl di,1 ;to index into a word width table\r
+ mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+ jmp [ss:memtoscreentable+di] ;call the right routine\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an even video address\r
+;\r
+;==============\r
+\r
+eventoeven:\r
+ mov dx,SC_INDEX\r
+ WORDOUT\r
+\r
+ mov di,[dest] ;start at same place in all planes\r
+ mov dx,[height] ;scan lines to draw\r
+\r
+@@lineloopEE:\r
+ mov cx,[wide]\r
+ rep movsw\r
+\r
+ add di,bx\r
+\r
+ dec dx\r
+ jnz @@lineloopEE\r
+\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ cmp ah,10000b ;done all four planes?\r
+ jne eventoeven\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an even video address\r
+;\r
+;==============\r
+\r
+oddtoeven:\r
+ mov dx,SC_INDEX\r
+ WORDOUT\r
+\r
+ mov di,[dest] ;start at same place in all planes\r
+ mov dx,[height] ;scan lines to draw\r
+\r
+@@lineloopOE:\r
+ mov cx,[wide]\r
+ rep movsw\r
+ movsb ;copy the last byte\r
+\r
+ add di,bx\r
+\r
+ dec dx\r
+ jnz @@lineloopOE\r
+\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ cmp ah,10000b ;done all four planes?\r
+ jne oddtoeven\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an odd video address\r
+;\r
+;==============\r
+\r
+eventoodd:\r
+ dec [wide] ;one word has to be handled seperately\r
+EOplaneloop:\r
+ mov dx,SC_INDEX\r
+ WORDOUT\r
+\r
+ mov di,[dest] ;start at same place in all planes\r
+ mov dx,[height] ;scan lines to draw\r
+\r
+@@lineloopEO:\r
+ movsb\r
+ mov cx,[wide]\r
+ rep movsw\r
+ movsb\r
+\r
+ add di,bx\r
+\r
+ dec dx\r
+ jnz @@lineloopEO\r
+\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ cmp ah,10000b ;done all four planes?\r
+ jne EOplaneloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an odd video address\r
+;\r
+;==============\r
+\r
+oddtoodd:\r
+ mov dx,SC_INDEX\r
+ WORDOUT\r
+\r
+ mov di,[dest] ;start at same place in all planes\r
+ mov dx,[height] ;scan lines to draw\r
+\r
+@@lineloopOO:\r
+ movsb\r
+ mov cx,[wide]\r
+ rep movsw\r
+\r
+ add di,bx\r
+\r
+ dec dx\r
+ jnz @@lineloopOO\r
+\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ cmp ah,10000b ;done all four planes?\r
+ jne oddtoodd\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+\r
+ENDP\r
+\r
+; MDM (GAMERS EDGE) begin\r
+\r
+\r
+MACRO XPAND_BYTE\r
+ test al,128 ; handle bit 7\r
+ jne @@over7\r
+ or [BYTE PTR es:di],11000000b\r
+@@over7:\r
+\r
+ test al,64 ; handle bit 6\r
+ jne @@over6\r
+ or [BYTE PTR es:di],00110000b\r
+@@over6:\r
+\r
+ test al,32 ; handle bit 5\r
+ jne @@over5\r
+ or [BYTE PTR es:di],00001100b\r
+@@over5:\r
+\r
+ test al,16 ; handle bit 4\r
+ jne @@over4\r
+ or [BYTE PTR es:di],00000011b\r
+@@over4:\r
+\r
+ inc di ; inc destination\r
+\r
+ test al,8 ; handle bit 3\r
+ jne @@over3\r
+ or [BYTE PTR es:di],11000000b\r
+@@over3:\r
+\r
+ test al,4 ; handle bit 2\r
+ jne @@over2\r
+ or [BYTE PTR es:di],00110000b\r
+@@over2:\r
+\r
+ test al,2 ; handle bit 1\r
+ jne @@over1\r
+ or [BYTE PTR es:di],00001100b\r
+@@over1:\r
+\r
+ test al,1 ; handle bit 0\r
+ jne @@over0\r
+ or [BYTE PTR es:di],00000011b\r
+@@over0:\r
+\r
+ inc si ; inc source\r
+ inc di ; inc destination\r
+ENDM\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen2x\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; with four planes of width by height data, and draws it to dest in the\r
+; virtual screen, based on linewidth. bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+xpandhorz db 00000000b,00000011b,00001100b,00001111b\r
+ db 00110000b,00110011b,00111100b,00111111b\r
+ db 11000000b,11000011b,11001100b,11001111b\r
+ db 11110000b,11110011b,11111100b,11111111b\r
+\r
+CODESEG\r
+\r
+\r
+PROC VW_MemToScreen2x source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen2x\r
+USES SI,DI\r
+\r
+ mov es,[screenseg]\r
+\r
+ mov bx,[linewidth]\r
+ sub bx,[wide]\r
+ sub bx,[wide]\r
+\r
+ mov ds,[source]\r
+\r
+\r
+ xor si,si ;block is segment aligned\r
+\r
+ mov ah,0001b ;map mask for plane 0\r
+\r
+@@depthloop:\r
+ mov al,SC_MAPMASK ;restore map mask in al\r
+ mov dx,SC_INDEX\r
+ WORDOUT\r
+\r
+ mov di,[dest] ;start at same place in all planes\r
+ mov dx,[height] ;scan lines to draw\r
+\r
+@@heightloop:\r
+ mov cx,[wide]\r
+@@widthloop:\r
+\r
+; handle first nybble\r
+;\r
+ push di\r
+ mov di,[si]\r
+ shr di,1\r
+ shr di,1\r
+ shr di,1\r
+ shr di,1\r
+ and di,15\r
+ mov al,[ss:xpandhorz+di]\r
+ pop di\r
+ mov [es:di],al\r
+ inc di\r
+\r
+; handle second nybble\r
+;\r
+ push di\r
+ mov di,[si]\r
+ and di,15\r
+ mov al,[ss:xpandhorz+di]\r
+ pop di\r
+ mov [es:di],al\r
+ inc si\r
+ inc di\r
+\r
+\r
+ dec cx\r
+ jne @@widthloop\r
+\r
+ add di,bx\r
+\r
+ dec dx\r
+ jnz @@heightloop\r
+\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ cmp ah,10000b ;done all four planes?\r
+ jne @@depthloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+ENDP\r
+; MDM (GAMERS EDGE) end\r
+\r
+;===========================================================================\r
+;\r
+; VW_ScreenToMem\r
+;\r
+; Copies a block of video memory to main memory, in order from planes 0-3.\r
+; This could be optimized along the lines of VW_MemToScreen to take advantage\r
+; of word copies, but this is an infrequently called routine.\r
+;\r
+;===========================================================================\r
+\r
+PROC VW_ScreenToMem source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToMem\r
+USES SI,DI\r
+\r
+ mov es,[dest]\r
+\r
+ mov bx,[linewidth]\r
+ sub bx,[wide]\r
+\r
+ mov ds,[screenseg]\r
+\r
+ mov ax,GC_READMAP ;read map for plane 0\r
+\r
+ xor di,di\r
+\r
+@@planeloop:\r
+ mov dx,GC_INDEX\r
+ WORDOUT\r
+\r
+ mov si,[source] ;start at same place in all planes\r
+ mov dx,[height] ;scan lines to draw\r
+\r
+@@lineloop:\r
+ mov cx,[wide]\r
+ rep movsb\r
+\r
+ add si,bx\r
+\r
+ dec dx\r
+ jnz @@lineloop\r
+\r
+ inc ah\r
+ cmp ah,4 ;done all four planes?\r
+ jne @@planeloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VWL_UpdateScreenBlocks\r
+;\r
+; Scans through the update matrix and copies any areas that have changed\r
+; to the visable screen, then zeros the update array\r
+;\r
+;============================================================================\r
+\r
+\r
+\r
+; AX 0/1 for scasb, temp for segment register transfers\r
+; BX width for block copies\r
+; CX REP counter\r
+; DX line width deltas\r
+; SI source for copies\r
+; DI scas dest / movsb dest\r
+; BP pointer to end of bufferblocks\r
+\r
+PROC VWL_UpdateScreenBlocks\r
+PUBLIC VWL_UpdateScreenBlocks\r
+USES SI,DI,BP\r
+\r
+ jmp SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+ mov dx,GC_INDEX ; restore write mode 0\r
+ mov ax,GC_MODE+0*256\r
+ WORDOUT\r
+\r
+ xor ax,ax ; clear out the update matrix\r
+ mov cx,UPDATEWIDE*UPDATEHIGH/2\r
+\r
+ mov di,[updateptr]\r
+ rep stosw\r
+\r
+ ret\r
+\r
+@@realstart:\r
+ mov dx,SC_INDEX\r
+ mov ax,SC_MAPMASK+15*256\r
+ WORDOUT\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_MODE+1*256\r
+ WORDOUT\r
+\r
+ mov di,[updateptr] ; start of floating update screen\r
+ mov bp,di\r
+ add bp,UPDATEWIDE*UPDATEHIGH+1 ; when di = bp, all tiles have been scanned\r
+\r
+ push di\r
+ mov cx,-1 ; definately scan the entire thing\r
+\r
+;\r
+; scan for a 1 in the update list, meaning a tile needs to be copied\r
+; from the master screen to the current screen\r
+;\r
+@@findtile:\r
+ pop di ; place to continue scaning from\r
+ mov ax,ss\r
+ mov es,ax ; search in the data segment\r
+ mov ds,ax\r
+ mov al,1\r
+ repne scasb\r
+ cmp di,bp\r
+ jae @@done\r
+\r
+ cmp [BYTE di],al\r
+ jne @@singletile\r
+ jmp @@tileblock\r
+\r
+;============\r
+;\r
+; copy a single tile\r
+;\r
+;============\r
+@@singletile:\r
+ inc di ; we know the next tile is nothing\r
+ push di ; save off the spot being scanned\r
+ sub di,[updateptr]\r
+ shl di,1\r
+ mov di,[blockstarts-4+di] ; start of tile location on screen\r
+ mov si,di\r
+ add si,[bufferofs]\r
+ add di,[displayofs]\r
+\r
+ mov dx,[linewidth]\r
+ sub dx,2\r
+ mov ax,[screenseg]\r
+ mov ds,ax\r
+ mov es,ax\r
+\r
+REPT 15\r
+ movsb\r
+ movsb\r
+ add si,dx\r
+ add di,dx\r
+ENDM\r
+ movsb\r
+ movsb\r
+\r
+ jmp @@findtile\r
+\r
+;============\r
+;\r
+; more than one tile in a row needs to be updated, so do it as a group\r
+;\r
+;============\r
+EVEN\r
+@@tileblock:\r
+ mov dx,di ; hold starting position + 1 in dx\r
+ inc di ; we know the next tile also gets updated\r
+ repe scasb ; see how many more in a row\r
+ push di ; save off the spot being scanned\r
+\r
+ mov bx,di\r
+ sub bx,dx ; number of tiles in a row\r
+ shl bx,1 ; number of bytes / row\r
+\r
+ mov di,dx ; lookup position of start tile\r
+ sub di,[updateptr]\r
+ shl di,1\r
+ mov di,[blockstarts-2+di] ; start of tile location\r
+ mov si,di\r
+ add si,[bufferofs]\r
+ add di,[displayofs]\r
+\r
+ mov dx,[linewidth]\r
+ sub dx,bx ; offset to next line on screen\r
+\r
+ mov ax,[screenseg]\r
+ mov ds,ax\r
+ mov es,ax\r
+\r
+REPT 15\r
+ mov cx,bx\r
+ rep movsb\r
+ add si,dx\r
+ add di,dx\r
+ENDM\r
+ mov cx,bx\r
+ rep movsb\r
+\r
+ dec cx ; was 0 from last rep movsb, now $ffff for scasb\r
+ jmp @@findtile\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+;\r
+; MISC EGA ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+;==============\r
+;\r
+; VW_SetScreen\r
+;\r
+;==============\r
+\r
+PROC VW_SetScreen crtc:WORD, pel:WORD\r
+PUBLIC VW_SetScreen\r
+\r
+if waitforvbl\r
+\r
+ mov dx,STATUS_REGISTER_1\r
+\r
+;\r
+; wait util the CRTC just starts scaning a diplayed line to set the CRTC start\r
+;\r
+ cli\r
+\r
+@@waitnodisplay:\r
+ in al,dx\r
+ test al,1\r
+ jz @@waitnodisplay\r
+\r
+; the display is now disabled (in a HBL / VBL)\r
+\r
+@@waitdisplay:\r
+ in al,dx\r
+ test al,1 ;1 = display is disabled (HBL / VBL)\r
+ jnz @@waitdisplay\r
+\r
+; the display was just enabled, so a full scan line is available for CRTC set\r
+\r
+endif\r
+\r
+;\r
+; set CRTC start\r
+;\r
+; for some reason, my XT's EGA card doesn't like word outs to the CRTC\r
+; index...\r
+;\r
+ mov cx,[crtc]\r
+ mov dx,CRTC_INDEX\r
+ mov al,0ch ;start address high register\r
+ out dx,al\r
+ inc dx\r
+ mov al,ch\r
+ out dx,al\r
+ dec dx\r
+ mov al,0dh ;start address low register\r
+ out dx,al\r
+ mov al,cl\r
+ inc dx\r
+ out dx,al\r
+\r
+if waitforvbl\r
+\r
+;\r
+; wait for a vertical retrace to set pel panning\r
+;\r
+ mov dx,STATUS_REGISTER_1\r
+@@waitvbl:\r
+ sti ;service interrupts\r
+ jmp $+2\r
+ cli\r
+ in al,dx\r
+ test al,00001000b ;look for vertical retrace\r
+ jz @@waitvbl\r
+\r
+endif\r
+\r
+;\r
+; set horizontal panning\r
+;\r
+\r
+ mov dx,ATR_INDEX\r
+ mov al,ATR_PELPAN or 20h\r
+ out dx,al\r
+ jmp $+2\r
+ mov al,[BYTE pel] ;pel pan value\r
+ out dx,al\r
+\r
+ sti\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+\r
+if NUMFONT+NUMFONTM\r
+\r
+;===========================================================================\r
+;\r
+; GENERAL FONT DRAWING ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+DATASEG\r
+\r
+px dw ? ; proportional character drawing coordinates\r
+py dw ?\r
+pdrawmode db 11000b ; 8 = OR, 24 = XOR, put in GC_DATAROTATE\r
+fontcolor db 15 ;0-15 mapmask value\r
+\r
+PUBLIC px,py,pdrawmode,fontcolor\r
+\r
+;\r
+; offsets in font structure\r
+;\r
+pcharheight = 0 ;lines high\r
+charloc = 2 ;pointers to every character\r
+charwidth = 514 ;every character's width in pixels\r
+\r
+\r
+propchar dw ? ; the character number to shift\r
+stringptr dw ?,?\r
+\r
+\r
+BUFFWIDTH = 82 ; MDM (GAMERS EDGE) - increased from 50\r
+BUFFHEIGHT = 20 ; must be twice as high as font for masked fonts\r
+\r
+databuffer db BUFFWIDTH*BUFFHEIGHT dup (?)\r
+\r
+bufferwidth dw ? ; bytes with valid info / line\r
+bufferheight dw ? ; number of lines currently used\r
+\r
+bufferbyte dw ?\r
+bufferbit dw ?\r
+\r
+screenspot dw ? ; where the buffer is going\r
+\r
+bufferextra dw ? ; add at end of a line copy\r
+screenextra dw ?\r
+\r
+PUBLIC bufferwidth,bufferheight,screenspot\r
+\r
+CODESEG\r
+\r
+;======================\r
+;\r
+; Macros to table shift a byte of font\r
+;\r
+;======================\r
+\r
+MACRO SHIFTNOXOR\r
+ mov al,[es:bx] ; source\r
+ xor ah,ah\r
+ shl ax,1\r
+ mov si,ax\r
+ mov ax,[bp+si] ; table shift into two bytes\r
+ or [di],al ; or with first byte\r
+ inc di\r
+ mov [di],ah ; replace next byte\r
+ inc bx ; next source byte\r
+ENDM\r
+\r
+MACRO SHIFTWITHXOR\r
+ mov al,[es:bx] ; source\r
+ xor ah,ah\r
+ shl ax,1\r
+ mov si,ax\r
+ mov ax,[bp+si] ; table shift into two bytes\r
+ not ax\r
+ and [di],al ; and with first byte\r
+ inc di\r
+ mov [di],ah ; replace next byte\r
+ inc bx ; next source byte\r
+ENDM\r
+\r
+\r
+;=======================\r
+;\r
+; BufferToScreen\r
+;\r
+; Pass buffer start in SI (somewhere in databuffer)\r
+; Draws the buffer to the EGA screen in the current write mode\r
+;\r
+;========================\r
+\r
+PROC BufferToScreen NEAR\r
+\r
+ mov es,[screenseg]\r
+ mov di,[screenspot]\r
+\r
+ mov bx,[bufferwidth] ;calculate offsets for end of each line\r
+ or bx,bx\r
+ jnz @@isthere\r
+ ret ;nothing to draw\r
+\r
+@@isthere:\r
+ mov ax,[linewidth]\r
+ sub ax,bx\r
+ mov [screenextra],ax\r
+ mov ax,BUFFWIDTH\r
+ sub ax,bx\r
+ mov [bufferextra],ax\r
+\r
+ mov bx,[bufferheight] ;lines to copy\r
+@@lineloop:\r
+ mov cx,[bufferwidth] ;bytes to copy\r
+@@byteloop:\r
+ lodsb ;get a byte from the buffer\r
+ xchg [es:di],al ;load latches and store back to screen\r
+ inc di\r
+\r
+ loop @@byteloop\r
+\r
+ add si,[bufferextra]\r
+ add di,[screenextra]\r
+\r
+ dec bx\r
+ jnz @@lineloop\r
+\r
+ ret\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; NON MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfont\r
+\r
+DATASEG\r
+\r
+shiftdrawtable dw 0,shift1wide,shift2wide,shift3wide,shift4wide\r
+ dw shift5wide\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC ShiftPropChar NEAR\r
+\r
+ mov si,[fontnumber]\r
+ shl si,1\r
+ mov es,[grsegs+STARTFONT*2+si] ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+ mov si,[es:charwidth+bx]\r
+ and si,0ffh ;SI hold width in pixels\r
+ shl bx,1\r
+ mov bx,[es:charloc+bx] ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+ mov di,[bufferbit]\r
+ shl di,1\r
+ mov bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+ mov di,OFFSET databuffer\r
+ add di,[bufferbyte] ;DI holds pointer to buffer\r
+\r
+;\r
+; advance position by character width\r
+;\r
+ mov cx,[bufferbit]\r
+ add cx,si ;new bit position\r
+ mov ax,cx\r
+ and ax,7\r
+ mov [bufferbit],ax ;new bit position\r
+ mov ax,cx\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1\r
+ add [bufferbyte],ax ;new byte position\r
+\r
+ add si,7\r
+ shr si,1\r
+ shr si,1\r
+ shr si,1 ;bytes the character is wide\r
+ shl si,1 ;*2 to look up in shiftdrawtable\r
+\r
+ mov cx,[es:pcharheight]\r
+ mov dx,BUFFWIDTH\r
+ jmp [ss:shiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+shift1wide:\r
+ dec dx\r
+EVEN\r
+@@loop1:\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop1\r
+ ret\r
+\r
+;\r
+; two byte character\r
+;\r
+shift2wide:\r
+ dec dx\r
+ dec dx\r
+EVEN\r
+@@loop2:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop2\r
+ ret\r
+\r
+;\r
+; three byte character\r
+;\r
+shift3wide:\r
+ sub dx,3\r
+EVEN\r
+@@loop3:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop3\r
+ ret\r
+\r
+;\r
+; four byte character\r
+;\r
+shift4wide:\r
+ sub dx,4\r
+EVEN\r
+@@loop4:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop4\r
+ ret\r
+\r
+;\r
+; five byte character\r
+;\r
+shift5wide:\r
+ sub dx,5\r
+EVEN\r
+@@loop5:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop5\r
+ ret\r
+\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+; Assumes write mode 0\r
+;\r
+;==================\r
+\r
+CODESEG\r
+\r
+PROC VW_DrawPropString string:DWORD\r
+PUBLIC VW_DrawPropString\r
+USES SI,DI\r
+\r
+;\r
+; proportional spaceing, which clears the buffer ahead of it, so only\r
+; clear the first collumn\r
+;\r
+ mov al,0\r
+line = 0\r
+REPT BUFFHEIGHT\r
+ mov [BYTE databuffer+BUFFWIDTH*line],al\r
+line = line+1\r
+ENDM\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+@@shiftchars:\r
+ mov ax,[px]\r
+ and ax,7\r
+ mov [bufferbit],ax\r
+ mov [bufferbyte],0\r
+\r
+ mov ax,[WORD string]\r
+ mov [stringptr],ax\r
+ mov ax,[WORD string+2]\r
+ mov [stringptr+2],ax\r
+\r
+@@shiftone:\r
+ mov es,[stringptr+2]\r
+ mov bx,[stringptr]\r
+ inc [stringptr]\r
+ mov bx,[es:bx]\r
+ xor bh,bh\r
+ or bl,bl\r
+ jz @@allshifted\r
+ call ShiftPropChar\r
+ jmp @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+ mov bx,[py]\r
+ shl bx,1\r
+ mov di,[ylookup+bx]\r
+ add di,[bufferofs]\r
+ add di,[panadjust]\r
+\r
+ mov ax,[px]\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1 ;x location in bytes\r
+ add di,ax\r
+ mov [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+ mov ax,[bufferbyte]\r
+ shl ax,1\r
+ shl ax,1\r
+ shl ax,1\r
+ or ax,[bufferbit]\r
+ add [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+\r
+; set xor/or mode\r
+ mov dx,GC_INDEX\r
+ mov al,GC_DATAROTATE\r
+ mov ah,[pdrawmode]\r
+ WORDOUT\r
+\r
+; set mapmask to color\r
+ mov dx,SC_INDEX\r
+ mov al,SC_MAPMASK\r
+ mov ah,[fontcolor]\r
+ WORDOUT\r
+\r
+ mov ax,[bufferbyte]\r
+ test [bufferbit],7\r
+ jz @@go\r
+ inc ax ;so the partial byte also gets drawn\r
+@@go:\r
+ mov [bufferwidth],ax\r
+ mov si,[fontnumber]\r
+ shl si,1\r
+ mov es,[grsegs+STARTFONT*2+si]\r
+ mov ax,[es:pcharheight]\r
+ mov [bufferheight],ax\r
+\r
+ mov si,OFFSET databuffer\r
+ call BufferToScreen\r
+\r
+; set copy mode\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_DATAROTATE\r
+ WORDOUT\r
+\r
+; set mapmask to all\r
+ mov dx,SC_INDEX\r
+ mov ax,SC_MAPMASK + 15*256\r
+ WORDOUT\r
+\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+endif ;numfont\r
+\r
+;============================================================================\r
+;\r
+; MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfontm\r
+\r
+DATASEG\r
+\r
+mshiftdrawtable dw 0,mshift1wide,mshift2wide,mshift3wide\r
+\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftMPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC ShiftMPropChar NEAR\r
+\r
+ mov si,[fontnumber]\r
+ shl si,1\r
+ mov es,[grsegs+STARTFONTM*2+si] ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+ mov si,[es:charwidth+bx]\r
+ and si,0ffh ;SI hold width in pixels\r
+ shl bx,1\r
+ mov bx,[es:charloc+bx] ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+ mov di,[bufferbit]\r
+ shl di,1\r
+ mov bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+ mov di,OFFSET databuffer\r
+ add di,[bufferbyte] ;DI holds pointer to buffer\r
+\r
+ mov cx,[bufferbit]\r
+ add cx,si ;new bit position\r
+ mov ax,cx\r
+ and ax,7\r
+ mov [bufferbit],ax ;new bit position\r
+ mov ax,cx\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1\r
+ add [bufferbyte],ax ;new byte position\r
+\r
+ add si,7\r
+ shr si,1\r
+ shr si,1\r
+ shr si,1 ;bytes the character is wide\r
+ shl si,1 ;*2 to look up in shiftdrawtable\r
+\r
+ mov cx,[es:pcharheight]\r
+ mov dx,BUFFWIDTH\r
+ jmp [ss:mshiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+mshift1wide:\r
+ dec dx\r
+\r
+EVEN\r
+@@loop1m:\r
+ SHIFTWITHXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop1m\r
+\r
+ mov cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop1:\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop1\r
+\r
+ ret\r
+\r
+;\r
+; two byte character\r
+;\r
+mshift2wide:\r
+ dec dx\r
+ dec dx\r
+EVEN\r
+@@loop2m:\r
+ SHIFTWITHXOR\r
+ SHIFTWITHXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop2m\r
+\r
+ mov cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop2:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop2\r
+\r
+ ret\r
+\r
+;\r
+; three byte character\r
+;\r
+mshift3wide:\r
+ sub dx,3\r
+EVEN\r
+@@loop3m:\r
+ SHIFTWITHXOR\r
+ SHIFTWITHXOR\r
+ SHIFTWITHXOR\r
+ add di,dx ; next line in buffer\r
+\r
+ loop @@loop3m\r
+\r
+ mov cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop3:\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ SHIFTNOXOR\r
+ add di,dx ; next line in buffer\r
+ loop @@loop3\r
+\r
+ ret\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawMPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+; Assumes write mode 0\r
+;\r
+;==================\r
+\r
+\r
+\r
+PROC VW_DrawMPropString string:DWORD\r
+PUBLIC VW_DrawMPropString\r
+USES SI,DI\r
+\r
+;\r
+; clear out the first byte of the buffer, the rest will automatically be\r
+; cleared as characters are drawn into it\r
+;\r
+ mov si,[fontnumber]\r
+ shl si,1\r
+ mov es,[grsegs+STARTFONTM*2+si]\r
+ mov dx,[es:pcharheight]\r
+ mov di,OFFSET databuffer\r
+ mov ax,ds\r
+ mov es,ax\r
+ mov bx,BUFFWIDTH-1\r
+\r
+ mov cx,dx\r
+ mov al,0ffh\r
+@@maskfill:\r
+ stosb ; fill the mask part with $ff\r
+ add di,bx\r
+ loop @@maskfill\r
+\r
+ mov cx,dx\r
+ xor al,al\r
+@@datafill:\r
+ stosb ; fill the data part with $0\r
+ add di,bx\r
+ loop @@datafill\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+ mov ax,[px]\r
+ and ax,7\r
+ mov [bufferbit],ax\r
+ mov [bufferbyte],0\r
+\r
+ mov ax,[WORD string]\r
+ mov [stringptr],ax\r
+ mov ax,[WORD string+2]\r
+ mov [stringptr+2],ax\r
+\r
+@@shiftone:\r
+ mov es,[stringptr+2]\r
+ mov bx,[stringptr]\r
+ inc [stringptr]\r
+ mov bx,[es:bx]\r
+ xor bh,bh\r
+ or bl,bl\r
+ jz @@allshifted\r
+ call ShiftMPropChar\r
+ jmp @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+ mov bx,[py]\r
+ shl bx,1\r
+ mov di,[ylookup+bx]\r
+ add di,[bufferofs]\r
+ add di,[panadjust]\r
+\r
+ mov ax,[px]\r
+ shr ax,1\r
+ shr ax,1\r
+ shr ax,1 ;x location in bytes\r
+ add di,ax\r
+ mov [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+ mov ax,[bufferbyte]\r
+ shl ax,1\r
+ shl ax,1\r
+ shl ax,1\r
+ or ax,[bufferbit]\r
+ add [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+ mov ax,[bufferbyte]\r
+ test [bufferbit],7\r
+ jz @@go\r
+ inc ax ;so the partial byte also gets drawn\r
+@@go:\r
+ mov [bufferwidth],ax\r
+ mov es,[grsegs+STARTFONTM*2]\r
+ mov ax,[es:pcharheight]\r
+ mov [bufferheight],ax\r
+\r
+; set AND mode to punch out the mask\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_DATAROTATE + 8*256\r
+ WORDOUT\r
+\r
+; set mapmask to all\r
+ mov dx,SC_INDEX\r
+ mov ax,SC_MAPMASK + 15*256\r
+ WORDOUT\r
+\r
+ mov si,OFFSET databuffer\r
+ call BufferToScreen\r
+\r
+; set OR mode to fill in the color\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_DATAROTATE + 16*256\r
+ WORDOUT\r
+\r
+; set mapmask to color\r
+ mov dx,SC_INDEX\r
+ mov al,SC_MAPMASK\r
+ mov ah,[fontcolor]\r
+ WORDOUT\r
+\r
+ call BufferToScreen ; SI is still in the right position in buffer\r
+\r
+; set copy mode\r
+ mov dx,GC_INDEX\r
+ mov ax,GC_DATAROTATE\r
+ WORDOUT\r
+\r
+; set mapmask to all\r
+ mov dx,SC_INDEX\r
+ mov ax,SC_MAPMASK + 15*256\r
+ WORDOUT\r
+\r
+\r
+ ret\r
+\r
+ENDP\r
+\r
+endif ; if numfontm\r
+\r
+endif ; if fonts\r
--- /dev/null
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; JABHACK.ASM\r
+\r
+.386C\r
+IDEAL\r
+MODEL MEDIUM\r
+\r
+EXTRN LDIV@:far\r
+\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+EXTRN _intaddr:word\r
+\r
+;============================================================================\r
+\r
+CODESEG\r
+\r
+; Hacked up Juan Jimenez's code a bit to just return 386/not 386\r
+PROC _CheckIs386\r
+PUBLIC _CheckIs386\r
+\r
+ pushf ; Save flag registers, we use them here\r
+ xor ax,ax ; Clear AX and...\r
+ push ax ; ...push it onto the stack\r
+ popf ; Pop 0 into flag registers (all bits to 0),\r
+ pushf ; attempting to set bits 12-15 of flags to 0's\r
+ pop ax ; Recover the save flags\r
+ and ax,08000h ; If bits 12-15 of flags are set to\r
+ cmp ax,08000h ; zero then it's 8088/86 or 80188/186\r
+ jz not386\r
+\r
+ mov ax,07000h ; Try to set flag bits 12-14 to 1's\r
+ push ax ; Push the test value onto the stack\r
+ popf ; Pop it into the flag register\r
+ pushf ; Push it back onto the stack\r
+ pop ax ; Pop it into AX for check\r
+ and ax,07000h ; if bits 12-14 are cleared then\r
+ jz not386 ; the chip is an 80286\r
+\r
+ mov ax,1 ; We now assume it's a 80386 or better\r
+ popf\r
+ retf\r
+\r
+not386:\r
+ xor ax,ax\r
+ popf\r
+ retf\r
+\r
+ ENDP\r
+\r
+\r
+PROC _jabhack2\r
+PUBLIC _jabhack2\r
+\r
+ jmp @@skip\r
+\r
+@@where:\r
+ int 060h\r
+ retf\r
+\r
+@@skip:\r
+ push es\r
+\r
+ mov ax,seg LDIV@\r
+ mov es,ax\r
+ mov ax,[WORD PTR @@where]\r
+ mov [WORD FAR es:LDIV@],ax\r
+ mov ax,[WORD PTR @@where+2]\r
+ mov [WORD FAR es:LDIV@+2],ax\r
+\r
+ mov ax,offset @@jabdiv\r
+ mov [_intaddr],ax\r
+ mov ax,seg @@jabdiv\r
+ mov [_intaddr+2],ax\r
+\r
+ pop es\r
+ retf\r
+\r
+@@jabdiv:\r
+ add sp,4 ;Nuke IRET address, but leave flags\r
+ push bp\r
+ mov bp,sp ;Save BP, and set it equal to stack\r
+ cli\r
+\r
+ mov eax,[bp+8]\r
+ cdq\r
+ idiv [DWORD PTR bp+12]\r
+ mov edx,eax\r
+ shr edx,16\r
+\r
+ pop bp ;Restore BP\r
+ popf ;Restore flags (from INT)\r
+ retf 8 ;Return to original caller\r
+\r
+ ENDP\r
+\r
+ END\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#pragma inline\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <fcntl.h>\r
+#include <dos.h>\r
+#include <io.h>\r
+\r
+#include "def.h"\r
+#include "gelib.h"\r
+#include "jampak.h"\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+// LOCAL DEFINATIONS\r
+//\r
+//\r
+//=========================================================================\r
+\r
+//#define COMPRESSION_CODE // Comment define in for COMPRESS routines\r
+\r
+\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+// LOCAL VARIABLES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+unsigned char far LZW_ring_buffer[LZW_N + LZW_F - 1];\r
+\r
+ // ring buffer of size LZW_N, with extra LZW_F-1 bytes to facilitate\r
+ // string comparison\r
+\r
+\r
+#ifdef COMPRESSION_CODE\r
+\r
+int LZW_match_pos,\r
+ LZW_match_len,\r
+\r
+ // MAtchLength of longest match. These are set by the InsertNode()\r
+ // procedure.\r
+\r
+ // left & right children & parents -- These constitute binary search trees. */\r
+\r
+ far LZW_left_child[LZW_N + 1],\r
+ far LZW_right_child[LZW_N + 257],\r
+ far LZW_parent[LZW_N + 1];\r
+\r
+#endif\r
+\r
+memptr segptr;\r
+BufferedIO lzwBIO;\r
+\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+// COMPRESSION SUPPORT ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+#ifdef COMPRESSION_CODE\r
+\r
+//---------------------------------------------------------------------------\r
+// InitLZWTree()\r
+//---------------------------------------------------------------------------\r
+void InitLZWTree(void) /* initialize trees */\r
+{\r
+ int i;\r
+\r
+ /* For i = 0 to LZW_N - 1, LZW_right_child[i] and LZW_left_child[i] will be the right and\r
+ left children of node i. These nodes need not be initialized.\r
+ Also, LZW_parent[i] is the parent of node i. These are initialized to\r
+ LZW_NIL (= LZW_N), which stands for 'not used.'\r
+ For i = 0 to 255, LZW_right_child[LZW_N + i + 1] is the root of the tree\r
+ for strings that begin with character i. These are initialized\r
+ to LZW_NIL. Note there are 256 trees. */\r
+\r
+ for (i = LZW_N + 1; i <= LZW_N + 256; i++)\r
+ LZW_right_child[i] = LZW_NIL;\r
+\r
+ for (i = 0; i < LZW_N; i++)\r
+ LZW_parent[i] = LZW_NIL;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// InsertLZWNode()\r
+//---------------------------------------------------------------------------\r
+void InsertLZWNode(unsigned long r)\r
+\r
+ /* Inserts string of length LZW_F, LZW_ring_buffer[r..r+LZW_F-1], into one of the\r
+ trees (LZW_ring_buffer[r]'th tree) and returns the longest-match position\r
+ and length via the global variables LZW_match_pos and LZW_match_len.\r
+ If LZW_match_len = LZW_F, then removes the old node in favor of the new\r
+ one, because the old one will be deleted sooner.\r
+ Note r plays double role, as tree node and position in buffer. */\r
+{\r
+ int i, p, cmp;\r
+ unsigned char *key;\r
+\r
+ cmp = 1;\r
+ key = &LZW_ring_buffer[r];\r
+ p = LZW_N + 1 + key[0];\r
+ LZW_right_child[r] = LZW_left_child[r] = LZW_NIL;\r
+ LZW_match_len = 0;\r
+\r
+ for ( ; ; )\r
+ {\r
+ if (cmp >= 0)\r
+ {\r
+ if (LZW_right_child[p] != LZW_NIL)\r
+ p = LZW_right_child[p];\r
+ else\r
+ {\r
+ LZW_right_child[p] = r;\r
+ LZW_parent[r] = p;\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (LZW_left_child[p] != LZW_NIL)\r
+ p = LZW_left_child[p];\r
+ else\r
+ {\r
+ LZW_left_child[p] = r;\r
+ LZW_parent[r] = p;\r
+ return;\r
+ }\r
+ }\r
+\r
+ for (i = 1; i < LZW_F; i++)\r
+ if ((cmp = key[i] - LZW_ring_buffer[p + i]) != 0)\r
+ break;\r
+\r
+ if (i > LZW_match_len)\r
+ {\r
+ LZW_match_pos = p;\r
+ if ((LZW_match_len = i) >= LZW_F)\r
+ break;\r
+ }\r
+ }\r
+\r
+ LZW_parent[r] = LZW_parent[p];\r
+ LZW_left_child[r] = LZW_left_child[p];\r
+ LZW_right_child[r] = LZW_right_child[p];\r
+ LZW_parent[LZW_left_child[p]] = r;\r
+ LZW_parent[LZW_right_child[p]] = r;\r
+\r
+ if (LZW_right_child[LZW_parent[p]] == p)\r
+ LZW_right_child[LZW_parent[p]] = r;\r
+ else\r
+ LZW_left_child[LZW_parent[p]] = r;\r
+\r
+ LZW_parent[p] = LZW_NIL; /* remove p */\r
+}\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// DeleteLZWNode()\r
+//---------------------------------------------------------------------------\r
+void DeleteLZWNode(unsigned long p) /* deletes node p from tree */\r
+{\r
+ int q;\r
+\r
+ if (LZW_parent[p] == LZW_NIL)\r
+ return; /* not in tree */\r
+\r
+ if (LZW_right_child[p] == LZW_NIL)\r
+ q = LZW_left_child[p];\r
+ else\r
+ if (LZW_left_child[p] == LZW_NIL)\r
+ q = LZW_right_child[p];\r
+ else\r
+ {\r
+ q = LZW_left_child[p];\r
+ if (LZW_right_child[q] != LZW_NIL)\r
+ {\r
+ do {\r
+\r
+ q = LZW_right_child[q];\r
+\r
+ } while (LZW_right_child[q] != LZW_NIL);\r
+\r
+ LZW_right_child[LZW_parent[q]] = LZW_left_child[q];\r
+ LZW_parent[LZW_left_child[q]] = LZW_parent[q];\r
+ LZW_left_child[q] = LZW_left_child[p];\r
+ LZW_parent[LZW_left_child[p]] = q;\r
+ }\r
+\r
+ LZW_right_child[q] = LZW_right_child[p];\r
+ LZW_parent[LZW_right_child[p]] = q;\r
+ }\r
+\r
+ LZW_parent[q] = LZW_parent[p];\r
+ if (LZW_right_child[LZW_parent[p]] == p)\r
+ LZW_right_child[LZW_parent[p]] = q;\r
+ else\r
+ LZW_left_child[LZW_parent[p]] = q;\r
+\r
+ LZW_parent[p] = LZW_NIL;\r
+}\r
+#endif\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+// GENERAL FILE to FILE compression routines\r
+//\r
+// * Mainly for example usage of PTR/PTR (de)compression routines.\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+//\r
+// CompressFILEtoFILE() -- Compresses one file stream to another file stream\r
+//\r
+\r
+#ifdef COMPRESSION_CODE\r
+\r
+unsigned long CompressFILEtoFILE(FILE *infile, FILE *outfile,unsigned long DataLength)\r
+{\r
+ unsigned long returnval;\r
+\r
+ fwrite(COMP,4,1,outfile);\r
+ fwrite((char *)&DataLength,4,1,outfile);\r
+\r
+ returnval = 8+lzwCompress(infile,outfile,DataLength,(SRC_FFILE|DEST_FFILE));\r
+\r
+ return(returnval);\r
+}\r
+\r
+#endif\r
+\r
+#if 0\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// DecompressFILEtoFILE()\r
+//\r
+void DecompressFILEtoFILE(FILE *infile, FILE *outfile)\r
+{\r
+ unsigned char Buffer[8];\r
+ unsigned long DataLength;\r
+\r
+ fread(Buffer,1,4,infile);\r
+\r
+ if (strncmp(Buffer,COMP,4))\r
+ {\r
+ printf("\nNot a JAM Compressed File!\n");\r
+ return;\r
+ }\r
+\r
+ fread((void *)&DataLength,1,4,infile);\r
+\r
+ lzwDecompress(infile,outfile,DataLength,(SRC_FFILE|DEST_FFILE));\r
+}\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+//==========================================================================\r
+//\r
+//\r
+// WRITE/READ PTR ROUTINES\r
+//\r
+//\r
+//==========================================================================\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// WritePtr() -- Outputs data to a particular ptr type\r
+//\r
+// PtrType MUST be of type DEST_TYPE.\r
+//\r
+// NOTE : For PtrTypes DEST_MEM a ZERO (0) is always returned.\r
+//\r
+//---------------------------------------------------------------------------\r
+int WritePtr(long outfile, unsigned char data, unsigned PtrType)\r
+{\r
+ int returnval = 0;\r
+\r
+ switch (PtrType & DEST_TYPES)\r
+ {\r
+ case DEST_FILE:\r
+ write(*(int far *)outfile,(char *)&data,1);\r
+ break;\r
+\r
+ case DEST_FFILE:\r
+ returnval = putc(data, *(FILE **)outfile);\r
+ break;\r
+\r
+ case DEST_MEM:\r
+// *(*(char far **)outfile++) = data; // Do NOT delete\r
+ *((char far *)*(char far **)outfile)++ = data;\r
+ break;\r
+\r
+ default:\r
+ TrashProg("WritePtr() : Unknown DEST_PTR type");\r
+ break;\r
+ }\r
+\r
+ return(returnval);\r
+\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// ReadPtr() -- Reads data from a particular ptr type\r
+//\r
+// PtrType MUST be of type SRC_TYPE.\r
+//\r
+// RETURNS :\r
+// The char read in or EOF for SRC_FFILE type of reads.\r
+//\r
+//\r
+//---------------------------------------------------------------------------\r
+int ReadPtr(long infile, unsigned PtrType)\r
+{\r
+ int returnval = 0;\r
+\r
+ switch (PtrType & SRC_TYPES)\r
+ {\r
+ case SRC_FILE:\r
+ read(*(int far *)infile,(char *)&returnval,1);\r
+ break;\r
+\r
+ case SRC_FFILE:\r
+// JIM - JTR - is the following correct? "fgetc()" uses a near pointer.\r
+//\r
+ returnval = fgetc((FILE far *)*(FILE far **)infile);\r
+ break;\r
+\r
+ case SRC_BFILE:\r
+ returnval = bio_readch((BufferedIO *)*(void far **)infile);\r
+ break;\r
+\r
+ case SRC_MEM:\r
+ returnval = (char)*(*(char far **)infile++);\r
+// returnval = *((char far *)*(char far **)infile)++; // DO NOT DELETE!\r
+ break;\r
+\r
+ default:\r
+ TrashProg("ReadPtr() : Unknown SRC_PTR type");\r
+ break;\r
+ }\r
+\r
+ return(returnval);\r
+}\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+// COMPRESSION & DECOMPRESSION ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// lzwCompress() - Compresses data from an input ptr to a dest ptr\r
+//\r
+// PARAMS:\r
+// infile - Pointer at the BEGINNING of the data to compress\r
+// outfile - Pointer to the destination (no header).\r
+// DataLength - Number of bytes to compress.\r
+// PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).\r
+//\r
+// RETURNS:\r
+// Length of compressed data.\r
+//\r
+// COMPTYPE : ct_LZW\r
+//\r
+// NOTES : Does not write ANY header information!\r
+//\r
+#ifdef COMPRESSION_CODE\r
+unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+ short i;\r
+ short c, len, r, s, last_LZW_match_len, code_buf_ptr;\r
+ unsigned char far code_buf[17], mask;\r
+ unsigned long complen = 0;\r
+\r
+ // initialize trees\r
+\r
+ InitLZWTree();\r
+\r
+ code_buf[0] = 0;\r
+\r
+ //\r
+ // code_buf[1..16] saves eight units of code, and code_buf[0] works\r
+ // as eight flags, "1" representing that the unit is an unencoded\r
+ // letter (1 byte), "0" a position-and-length pair (2 bytes). Thus,\r
+ // eight units require at most 16 bytes of code.\r
+ //\r
+\r
+ code_buf_ptr = mask = 1;\r
+ s = 0;\r
+ r = LZW_N - LZW_F;\r
+\r
+ // Clear the buffer with any character that will appear often.\r
+ //\r
+\r
+ for (i = s; i < r; i++)\r
+ LZW_ring_buffer[i] = ' ';\r
+\r
+ // Read LZW_F bytes into the last LZW_F bytes of the buffer\r
+ //\r
+\r
+ for (len = 0; (len < LZW_F) && DataLength; len++)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes);\r
+ DataLength--;\r
+\r
+ // text of size zero\r
+\r
+ LZW_ring_buffer[r + len] = c;\r
+ }\r
+\r
+ if (!(len && DataLength))\r
+ return(0);\r
+\r
+ //\r
+ // Insert the LZW_F strings, each of which begins with one or more\r
+ // 'space' characters. Note the order in which these strings\r
+ // are inserted. This way, degenerate trees will be less likely\r
+ // to occur.\r
+ //\r
+\r
+ for (i = 1; i <= LZW_F; i++)\r
+ InsertLZWNode(r - i);\r
+\r
+ //\r
+ // Finally, insert the whole string just read. The global\r
+ // variables LZW_match_len and LZW_match_pos are set. */\r
+ //\r
+\r
+ InsertLZWNode(r);\r
+\r
+ do {\r
+ // LZW_match_len may be spuriously long near the end of text.\r
+ //\r
+\r
+ if (LZW_match_len > len)\r
+ LZW_match_len = len;\r
+\r
+ if (LZW_match_len <= LZW_THRESHOLD)\r
+ {\r
+ // Not long enough match. Send one byte.\r
+ //\r
+\r
+ LZW_match_len = 1;\r
+\r
+ // 'send one byte' flag\r
+ //\r
+\r
+ code_buf[0] |= mask;\r
+\r
+ // Send uncoded.\r
+ //\r
+\r
+ code_buf[code_buf_ptr++] = LZW_ring_buffer[r];\r
+ }\r
+ else\r
+ {\r
+ code_buf[code_buf_ptr++] = (unsigned char) LZW_match_pos;\r
+ code_buf[code_buf_ptr++] = (unsigned char) (((LZW_match_pos >> 4) & 0xf0) | (LZW_match_len - (LZW_THRESHOLD + 1)));\r
+\r
+ // Send position and length pair.\r
+ // Note LZW_match_len > LZW_THRESHOLD.\r
+ }\r
+\r
+ if ((mask <<= 1) == 0)\r
+ {\r
+ // Shift mask left one bit.\r
+ // Send at most 8 units of data\r
+\r
+ for (i = 0; i < code_buf_ptr; i++)\r
+ WritePtr((long)&outfile,code_buf[i],PtrTypes);\r
+\r
+ complen += code_buf_ptr;\r
+ code_buf[0] = 0;\r
+ code_buf_ptr = mask = 1;\r
+ }\r
+\r
+ last_LZW_match_len = LZW_match_len;\r
+\r
+ for (i = 0; i < last_LZW_match_len && DataLength; i++)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes);\r
+ DataLength--;\r
+\r
+ DeleteLZWNode(s); // Delete old strings and\r
+ LZW_ring_buffer[s] = c; // read new bytes\r
+\r
+ // If the position is near the end of buffer, extend the\r
+ // buffer to make string comparison easier.\r
+\r
+ if (s < LZW_F - 1)\r
+ LZW_ring_buffer[s + LZW_N] = c;\r
+\r
+ // Since this is a ring buffer, inc the position modulo LZW_N.\r
+ //\r
+\r
+ s = (s + 1) & (LZW_N - 1);\r
+ r = (r + 1) & (LZW_N - 1);\r
+\r
+ // Register the string in LZW_ring_buffer[r..r+LZW_F-1]\r
+ //\r
+\r
+ InsertLZWNode(r);\r
+ }\r
+\r
+ while (i++ < last_LZW_match_len)\r
+ {\r
+ // After the end of text,\r
+ DeleteLZWNode(s); // no need to read, but\r
+\r
+ s = (s + 1) & (LZW_N - 1);\r
+ r = (r + 1) & (LZW_N - 1);\r
+\r
+ if (--len)\r
+ InsertLZWNode(r); // buffer may not be empty.\r
+ }\r
+\r
+ } while (len > 0); // until length of string to be processed is zero\r
+\r
+\r
+ if (code_buf_ptr > 1)\r
+ {\r
+ // Send remaining code.\r
+ //\r
+\r
+ for (i = 0; i < code_buf_ptr; i++)\r
+ WritePtr((long)&outfile,code_buf[i],PtrTypes);\r
+\r
+ complen += code_buf_ptr;\r
+ }\r
+\r
+ return(complen);\r
+}\r
+#endif\r
+\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// lzwDecompress() - Compresses data from an input ptr to a dest ptr\r
+//\r
+// PARAMS:\r
+// infile - Pointer at the BEGINNING of the compressed data (no header!)\r
+// outfile - Pointer to the destination.\r
+// DataLength - Length of compressed data.\r
+// PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).\r
+//\r
+// RETURNS:\r
+// Length of compressed data.\r
+//\r
+// COMPTYPE : ct_LZW\r
+//\r
+// NOTES : Does not write ANY header information!\r
+//\r
+void far lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+ int i, j, k, r, c;\r
+ unsigned int flags;\r
+\r
+ for (i = 0; i < LZW_N - LZW_F; i++)\r
+ LZW_ring_buffer[i] = ' ';\r
+\r
+ r = LZW_N - LZW_F;\r
+ flags = 0;\r
+\r
+ for ( ; ; )\r
+ {\r
+ if (((flags >>= 1) & 256) == 0)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes);\r
+ if (!DataLength--)\r
+ return;\r
+\r
+ flags = c | 0xff00; // uses higher byte cleverly to count 8\r
+ }\r
+\r
+ if (flags & 1)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes); // Could test for EOF iff FFILE type\r
+ if (!DataLength--)\r
+ return;\r
+\r
+ WritePtr((long)&outfile,c,PtrTypes);\r
+\r
+ LZW_ring_buffer[r++] = c;\r
+ r &= (LZW_N - 1);\r
+ }\r
+ else\r
+ {\r
+ i = ReadPtr((long)&infile,PtrTypes);\r
+ if (!DataLength--)\r
+ return;\r
+\r
+ j = ReadPtr((long)&infile,PtrTypes);\r
+ if (!DataLength--)\r
+ return;\r
+\r
+ i |= ((j & 0xf0) << 4);\r
+ j = (j & 0x0f) + LZW_THRESHOLD;\r
+\r
+ for (k = 0; k <= j; k++)\r
+ {\r
+ c = LZW_ring_buffer[(i + k) & (LZW_N - 1)];\r
+\r
+ WritePtr((long)&outfile,c,PtrTypes);\r
+\r
+ LZW_ring_buffer[r++] = c;\r
+ r &= (LZW_N - 1);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+\r
+\r
+#if 0\r
+//=========================================================================\r
+//\r
+//\r
+// BUFFERED I/O ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// InitBufferedIO()\r
+//--------------------------------------------------------------------------\r
+memptr InitBufferedIO(int handle, BufferedIO *bio)\r
+{\r
+ bio->handle = handle;\r
+ bio->offset = BIO_BUFFER_LEN;\r
+ bio->status = 0;\r
+ MM_GetPtr(&bio->buffer,BIO_BUFFER_LEN);\r
+\r
+ return(bio->buffer);\r
+}\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// FreeBufferedIO()\r
+//--------------------------------------------------------------------------\r
+void FreeBufferedIO(BufferedIO *bio)\r
+{\r
+ if (bio->buffer)\r
+ MM_FreePtr(&bio->buffer);\r
+}\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// bio_readch()\r
+//--------------------------------------------------------------------------\r
+byte bio_readch(BufferedIO *bio)\r
+{\r
+ byte far *buffer;\r
+\r
+ if (bio->offset == BIO_BUFFER_LEN)\r
+ {\r
+ bio->offset = 0;\r
+ bio_fillbuffer(bio);\r
+ }\r
+\r
+ buffer = MK_FP(bio->buffer,bio->offset++);\r
+\r
+ return(*buffer);\r
+}\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// bio_fillbuffer()\r
+//\r
+// BUGS (Not really bugs... More like RULES!)\r
+//\r
+// 1) This code assumes BIO_BUFFER_LEN is no smaller than\r
+// NEAR_BUFFER_LEN!!\r
+//\r
+// 2) BufferedIO.status should be altered by this code to report\r
+// read errors, end of file, etc... If you know how big the file\r
+// is you're reading, determining EOF should be no problem.\r
+//\r
+//--------------------------------------------------------------------------\r
+void bio_fillbuffer(BufferedIO *bio)\r
+{\r
+ #define NEAR_BUFFER_LEN (64)\r
+ byte near_buffer[NEAR_BUFFER_LEN];\r
+ short bio_length,bytes_read,bytes_requested;\r
+\r
+ bytes_read = 0;\r
+ bio_length = BIO_BUFFER_LEN;\r
+ while (bio_length)\r
+ {\r
+ if (bio_length > NEAR_BUFFER_LEN-1)\r
+ bytes_requested = NEAR_BUFFER_LEN;\r
+ else\r
+ bytes_requested = bio_length;\r
+\r
+ read(bio->handle,near_buffer,bytes_requested);\r
+ _fmemcpy(MK_FP(bio->buffer,bytes_read),near_buffer,bytes_requested);\r
+\r
+ bio_length -= bytes_requested;\r
+ bytes_read += bytes_requested;\r
+ }\r
+}\r
+\r
+\r
+#endif\r
+\r
+//=========================================================================\r
+//\r
+//\r
+// GENERAL LOAD ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// BLoad()\r
+//--------------------------------------------------------------------------\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr)\r
+{\r
+ int handle;\r
+\r
+ memptr SrcPtr;\r
+ longword i, j, k, r, c;\r
+ word flags;\r
+ byte Buffer[8];\r
+ longword DstLen, SrcLen;\r
+ boolean comp;\r
+\r
+ if ((handle = open(SourceFile, O_RDONLY|O_BINARY)) == -1)\r
+ return(0);\r
+\r
+ // Look for 'COMP' header\r
+ //\r
+ read(handle,Buffer,4);\r
+ comp = !strncmp(Buffer,COMP,4);\r
+\r
+ // Get source and destination length.\r
+ //\r
+ if (comp)\r
+ {\r
+ SrcLen = Verify(SourceFile);\r
+ read(handle,(void *)&DstLen,4);\r
+ MM_GetPtr(DstPtr,DstLen);\r
+ if (!*DstPtr)\r
+ return(0);\r
+ }\r
+ else\r
+ DstLen = Verify(SourceFile);\r
+\r
+ // LZW decompress OR simply load the file.\r
+ //\r
+ if (comp)\r
+ {\r
+\r
+ if (MM_TotalFree() < SrcLen)\r
+ {\r
+ if (!InitBufferedIO(handle,&lzwBIO))\r
+ TrashProg("No memory for buffered I/O.");\r
+ lzwDecompress(&lzwBIO,MK_FP(*DstPtr,0),SrcLen,(SRC_BFILE|DEST_MEM));\r
+ FreeBufferedIO(&lzwBIO);\r
+ }\r
+ else\r
+ {\r
+ CA_LoadFile(SourceFile,&SrcPtr);\r
+ lzwDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),SrcLen,(SRC_MEM|DEST_MEM));\r
+ MM_FreePtr(&SrcPtr);\r
+ }\r
+ }\r
+ else\r
+ CA_LoadFile(SourceFile,DstPtr);\r
+\r
+ close(handle);\r
+ return(DstLen);\r
+}\r
+\r
+\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// LoadLIBShape()\r
+//\r
+int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP)\r
+{\r
+ #define CHUNK(Name) (*ptr == *Name) && \\r
+ (*(ptr+1) == *(Name+1)) && \\r
+ (*(ptr+2) == *(Name+2)) && \\r
+ (*(ptr+3) == *(Name+3))\r
+\r
+\r
+ int RT_CODE;\r
+ FILE *fp;\r
+ char CHUNK[5];\r
+ char far *ptr;\r
+ memptr IFFfile = NULL;\r
+ unsigned long FileLen, size, ChunkLen;\r
+ int loop;\r
+\r
+\r
+ RT_CODE = 1;\r
+\r
+ // Decompress to ram and return ptr to data and return len of data in\r
+ // passed variable...\r
+\r
+ if (!LoadLIBFile(SLIB_Filename,Filename,&IFFfile))\r
+ TrashProg("Error Loading Compressed lib shape!");\r
+\r
+ // Evaluate the file\r
+ //\r
+ ptr = MK_FP(IFFfile,0);\r
+ if (!CHUNK("FORM"))\r
+ goto EXIT_FUNC;\r
+ ptr += 4;\r
+\r
+ FileLen = *(long far *)ptr;\r
+ SwapLong((long far *)&FileLen);\r
+ ptr += 4;\r
+\r
+ if (!CHUNK("ILBM"))\r
+ goto EXIT_FUNC;\r
+ ptr += 4;\r
+\r
+ FileLen += 4;\r
+ while (FileLen)\r
+ {\r
+ ChunkLen = *(long far *)(ptr+4);\r
+ SwapLong((long far *)&ChunkLen);\r
+ ChunkLen = (ChunkLen+1) & 0xFFFFFFFE;\r
+\r
+ if (CHUNK("BMHD"))\r
+ {\r
+ ptr += 8;\r
+ SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w;\r
+ SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h;\r
+ SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x;\r
+ SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y;\r
+ SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d;\r
+ SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans;\r
+ SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp;\r
+ SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad;\r
+ SwapWord(&SHP->bmHdr.w);\r
+ SwapWord(&SHP->bmHdr.h);\r
+ SwapWord(&SHP->bmHdr.x);\r
+ SwapWord(&SHP->bmHdr.y);\r
+ ptr += ChunkLen;\r
+ }\r
+ else\r
+ if (CHUNK("BODY"))\r
+ {\r
+ ptr += 4;\r
+ size = *((long far *)ptr);\r
+ ptr += 4;\r
+ SwapLong((long far *)&size);\r
+ SHP->BPR = (SHP->bmHdr.w+7) >> 3;\r
+ MM_GetPtr(&SHP->Data,size);\r
+ if (!SHP->Data)\r
+ goto EXIT_FUNC;\r
+ movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size);\r
+ ptr += ChunkLen;\r
+\r
+ break;\r
+ }\r
+ else\r
+ ptr += ChunkLen+8;\r
+\r
+ FileLen -= ChunkLen+8;\r
+ }\r
+\r
+ RT_CODE = 0;\r
+\r
+EXIT_FUNC:;\r
+ if (IFFfile)\r
+ {\r
+// segptr = (memptr)FP_SEG(IFFfile);\r
+ MM_FreePtr(&IFFfile);\r
+ }\r
+\r
+ return (RT_CODE);\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//----------------------------------------------------------------------------\r
+// LoadLIBFile() -- Copies a file from an existing archive to dos.\r
+//\r
+// PARAMETERS :\r
+//\r
+// LibName - Name of lib file created with SoftLib V1.0\r
+//\r
+// FileName - Name of file to load from lib file.\r
+//\r
+// MemPtr - (IF !NULL) - Pointer to memory to load into ..\r
+// (IF NULL) - Routine allocates necessary memory and\r
+// returns a MEM(SEG) pointer to memory allocated.\r
+//\r
+// RETURN :\r
+//\r
+// (IF !NULL) - A pointer to the loaded data.\r
+// (IF NULL) - Error!\r
+//\r
+//----------------------------------------------------------------------------\r
+memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr)\r
+{\r
+ int handle;\r
+ unsigned long header;\r
+ struct ChunkHeader Header;\r
+ unsigned long ChunkLen;\r
+ short x;\r
+ struct FileEntryHdr FileEntry; // Storage for file once found\r
+ struct FileEntryHdr FileEntryHeader; // Header used durring searching\r
+ struct SoftLibHdr LibraryHeader; // Library header - Version Checking\r
+ boolean FileFound = false;\r
+ unsigned long id_slib = ID_SLIB;\r
+ unsigned long id_chunk = ID_CHUNK;\r
+\r
+\r
+ //\r
+ // OPEN SOFTLIB FILE\r
+ //\r
+\r
+ if ((handle = open(LibName,O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+ return(NULL);\r
+\r
+\r
+ //\r
+ // VERIFY it is a SOFTLIB (SLIB) file\r
+ //\r
+\r
+ if (read(handle,&header,4) == -1)\r
+ {\r
+ close(handle);\r
+ return(NULL);\r
+ }\r
+\r
+ if (header != id_slib)\r
+ {\r
+ close(handle);\r
+ return(NULL);\r
+ }\r
+\r
+\r
+ //\r
+ // CHECK LIBRARY HEADER VERSION NUMBER\r
+ //\r
+\r
+ if (read(handle, &LibraryHeader,sizeof(struct SoftLibHdr)) == -1)\r
+ TrashProg("read error in LoadSLIBFile()\n%c",7);\r
+\r
+ if (LibraryHeader.Version > SOFTLIB_VER)\r
+ TrashProg("Unsupported file ver %d",LibraryHeader.Version);\r
+\r
+\r
+ //\r
+ // MANAGE FILE ENTRY HEADERS...\r
+ //\r
+\r
+ for (x = 1;x<=LibraryHeader.FileCount;x++)\r
+ {\r
+ if (read(handle, &FileEntryHeader,sizeof(struct FileEntryHdr)) == -1)\r
+ {\r
+ close(handle);\r
+ return(NULL);\r
+ }\r
+\r
+ if (!stricmp(FileEntryHeader.FileName,FileName))\r
+ {\r
+ FileEntry = FileEntryHeader;\r
+ FileFound = true;\r
+ }\r
+ }\r
+\r
+ //\r
+ // IF FILE HAS BEEN FOUND THEN SEEK TO POSITION AND EXTRACT\r
+ // ELSE RETURN WITH ERROR CODE...\r
+ //\r
+\r
+ if (FileFound)\r
+ {\r
+ if (lseek(handle,FileEntry.Offset,SEEK_CUR) == -1)\r
+ {\r
+ close(handle);\r
+ return(NULL);\r
+ }\r
+\r
+ //\r
+ // READ CHUNK HEADER - Verify we are at the beginning of a chunk..\r
+ //\r
+\r
+ if (read(handle,(char *)&Header,sizeof(struct ChunkHeader)) == -1)\r
+ TrashProg("LIB File - Unable to read Header!");\r
+\r
+ if (Header.HeaderID != id_chunk)\r
+ TrashProg("LIB File - BAD HeaderID!");\r
+\r
+ //\r
+ // Allocate memory if Necessary...\r
+ //\r
+\r
+\r
+ if (!*MemPtr)\r
+ MM_GetPtr(MemPtr,FileEntry.OrginalLength);\r
+\r
+ //\r
+ // Calculate the length of the data (without the chunk header).\r
+ //\r
+\r
+ ChunkLen = FileEntry.ChunkLen - sizeof(struct ChunkHeader);\r
+\r
+\r
+ //\r
+ // Extract Data from file\r
+ //\r
+\r
+ switch (Header.Compression)\r
+ {\r
+ case ct_LZW:\r
+ if (!InitBufferedIO(handle,&lzwBIO))\r
+ TrashProg("No memory for buffered I/O.");\r
+ lzwDecompress(&lzwBIO,MK_FP(*MemPtr,0),ChunkLen,(SRC_BFILE|DEST_MEM));\r
+ FreeBufferedIO(&lzwBIO);\r
+ break;\r
+\r
+ case ct_NONE:\r
+ if (!CA_FarRead(handle,MK_FP(*MemPtr,0),ChunkLen))\r
+ {\r
+ close(handle);\r
+ *MemPtr = NULL;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ close(handle);\r
+ TrashProg("Uknown Chunk.Compression Type!");\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ *MemPtr = NULL;\r
+\r
+ close(handle);\r
+ return(*MemPtr);\r
+}\r
+\r
+\r
+\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//\r
+//\r
+//\r
+//\r
+\r
+\r
+#define LZW_N 4096\r
+#define LZW_F 18\r
+\r
+\r
+// LZW_THRESHOLD :encode string into position and length if match_length is\r
+// greater than this\r
+\r
+#define LZW_THRESHOLD 2\r
+\r
+// index for root of binary search trees\r
+//\r
+\r
+#define LZW_NIL LZW_N\r
+\r
+\r
+//\r
+// FILE CHUNK IDs\r
+//\r
+// NOTE: The only reason for changing from COMP to CMP2 and having multi\r
+// comp header structs is for downward compatablity.\r
+//\r
+\r
+#define COMP ("COMP") // Comp type is ct_LZW ALWAYS!\r
+#define CMP2 ("CMP2") // Comp type is determined in header.\r
+\r
+\r
+//\r
+// COMPRESSION TYPES\r
+//\r
+\r
+#if 0\r
+//\r
+// FILE CHUNK HEADER FORMATS\r
+//\r
+\r
+struct COMPStruct\r
+{\r
+ unsigned long DecompLen;\r
+\r
+};\r
+\r
+\r
+struct CMP2Header\r
+{\r
+ unsigned CompType;\r
+ unsigned long DecompLen;\r
+\r
+};\r
+#endif\r
+\r
+memptr segptr;\r
+extern BufferedIO lzwBIO;\r
+\r
+\r
+//\r
+// PARAMETER PASSING TYPES (POINTER TYPES)\r
+//\r
+\r
+#define SRC_FILE (0x0001) // C's non-buffered file i/o\r
+#define SRC_FFILE (0x0002) // C's buffered ffile i/o\r
+#define SRC_MEM (0x0004) // FAR memory Ptrs\r
+#define SRC_BFILE (0x0008) // Buffered File I/O\r
+\r
+#define SRC_TYPES (SRC_FILE | SRC_FFILE | SRC_MEM | SRC_BFILE)\r
+\r
+#define DEST_FILE (0x0100) // C's non-buffered file i/o\r
+#define DEST_FFILE (0x0200) // C's buffered ffile i/o\r
+#define DEST_MEM (0x0400) // FAR memory Ptrs\r
+\r
+#define DEST_TYPES (DEST_FILE | DEST_FFILE | DEST_MEM)\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// FUNCTION PROTOTYPEING\r
+//---------------------------------------------------------------------------\r
+\r
+//void DecompressFILEtoFILE(FILE *infile, FILE *outfile);\r
+//unsigned long CompressFILEtoFILE(FILE *infile, FILE *outfile,unsigned long DataLength);\r
+\r
+\r
+unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+\r
+int WritePtr(long outfile, unsigned char data, unsigned PtrType);\r
+int ReadPtr(long infile, unsigned PtrType);\r
+\r
+//memptr InitBufferedIO(int handle, BufferedIO *bio);\r
+//void FreeBufferedIO(BufferedIO *bio);\r
+//byte bio_readch(BufferedIO *bio);\r
+\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr);\r
+memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr);\r
+int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP);\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <fcntl.h>\r
+#include <dos.h>\r
+#include <io.h>\r
+\r
+#include "def.h"\r
+#include "gelib.h"\r
+#include "jam_io.h"\r
+\r
+//----------------------------------------------------------------------------\r
+//\r
+// PTR/PTR COMPRESSION ROUTINES\r
+//\r
+//\r
+//----------------------------------------------------------------------------\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// WritePtr() -- Outputs data to a particular ptr type\r
+//\r
+// PtrType MUST be of type DEST_TYPE.\r
+//\r
+// NOTE : For PtrTypes DEST_MEM a ZERO (0) is always returned.\r
+//\r
+//---------------------------------------------------------------------------\r
+char WritePtr(long outfile, unsigned char data, unsigned PtrType)\r
+{\r
+ int returnval = 0;\r
+\r
+ switch (PtrType & DEST_TYPES)\r
+ {\r
+ case DEST_FILE:\r
+ write(*(int far *)outfile,(char *)&data,1);\r
+ break;\r
+\r
+ case DEST_FFILE:\r
+ returnval = putc(data, *(FILE **)outfile);\r
+ break;\r
+\r
+ case DEST_IMEM:\r
+ printf("WritePtr - unsupported ptr type\n");\r
+ exit(0);\r
+ break;\r
+\r
+ case DEST_MEM:\r
+ *((char far *)*(char far **)outfile)++ = data;\r
+ break;\r
+ }\r
+\r
+ return(returnval);\r
+\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// ReadPtr() -- Reads data from a particular ptr type\r
+//\r
+// PtrType MUST be of type SRC_TYPE.\r
+//\r
+// RETURNS :\r
+// The char read in or EOF for SRC_FFILE type of reads.\r
+//\r
+//\r
+//---------------------------------------------------------------------------\r
+int ReadPtr(long infile, unsigned PtrType)\r
+{\r
+ int returnval = 0;\r
+\r
+ switch (PtrType & SRC_TYPES)\r
+ {\r
+ case SRC_FILE:\r
+ read(*(int far *)infile,(char *)&returnval,1);\r
+ break;\r
+\r
+ case SRC_FFILE:\r
+ returnval = getc(*(FILE far **)infile\r
+ );\r
+ break;\r
+\r
+ case SRC_BFILE:\r
+ returnval = bio_readch((BufferedIO *)*(void far **)infile);\r
+ break;\r
+\r
+// case SRC_IMEM:\r
+// printf("WritePtr - unsupported ptr type\n");\r
+// exit(0);\r
+// break;\r
+\r
+ case SRC_MEM:\r
+ returnval = (unsigned char)*((char far *)*(char far **)infile)++;\r
+ break;\r
+ }\r
+\r
+ return(returnval);\r
+}\r
+\r
+\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// UNIT : JAM_IO.h\r
+//\r
+// FUNCTION : General defines, prototypes, typedefs used in all the\r
+// supported compression techniques used in JAMPAK Ver x.x\r
+//\r
+//\r
+\r
+\r
+\r
+\r
+//==========================================================================\r
+//\r
+// PARAMETER PASSING TYPES\r
+//\r
+//\r
+ // SOURCE PARAMS (LO BYTE)\r
+\r
+#define SRC_FILE (0x0001) // GE Buffered IO\r
+#define SRC_FFILE (0x0002) // Stdio File IO (fwrite etc.)\r
+#define SRC_MEM (0x0004) // Std void ptr (alloc etc)\r
+#define SRC_BFILE (0x0008) // Buffered File I/O\r
+\r
+#define SRC_TYPES (SRC_FILE | SRC_FFILE | SRC_MEM | SRC_BFILE)\r
+\r
+ // DESTINATION PARAMS (HI BYTE)\r
+\r
+#define DEST_FILE (0x0100) // GE Buffered IO\r
+#define DEST_FFILE (0x0200) // Stdio File IO (fwrite etc.)\r
+#define DEST_MEM (0x0400) // Std void ptr (alloc etc)\r
+#define DEST_IMEM (0x0800) // ID Memory alloc\r
+\r
+#define DEST_TYPES (DEST_FILE | DEST_FFILE | DEST_MEM | DEST_IMEM)\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+// FILE CHUNK IDs\r
+//\r
+// NOTE: The only reason for changing from COMP to CMP1 and having multi\r
+// comp header structs is for downward compatablity.\r
+//\r
+\r
+#define COMP ("COMP") // Comp type is ct_LZW ALWAYS!\r
+#define CMP1 ("CMP1") // Comp type is determined in header.\r
+\r
+\r
+//\r
+// COMPRESSION TYPES\r
+//\r
+typedef enum ct_TYPES\r
+{\r
+ ct_NONE = 0, // No compression - Just data..Rarely used!\r
+ ct_LZW, // LZW data compression\r
+ ct_LZH,\r
+\r
+} ct_TYPES;\r
+\r
+//\r
+// FILE CHUNK HEADER FORMATS\r
+//\r
+\r
+struct COMPStruct\r
+{\r
+ unsigned long DecompLen;\r
+\r
+};\r
+\r
+\r
+struct CMP1Header\r
+{\r
+ unsigned CompType; // SEE: ct_TYPES above for list of pos.\r
+ unsigned long OrginalLen; // Orginal FileLength of compressed Data.\r
+ unsigned long CompressLen; // Length of data after compression (A MUST for LZHUFF!)\r
+};\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// FUNCTION PROTOTYPEING\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+char WritePtr(long outfile, unsigned char data, unsigned PtrType);\r
+int ReadPtr(long infile, unsigned PtrType);\r
+\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//===========================================================================\r
+//\r
+// LZHUFF COMPRESSION ROUTINES\r
+// VERSION 1.0\r
+//\r
+// Compression algrythim by Haruhiko OKUMURA\r
+// Implementation by Jim T. Row\r
+//\r
+//\r
+// Copyright (c) 1992 - Softdisk Publishing inc. - All rights reserved\r
+//\r
+//===========================================================================\r
+//\r
+// Compiler #ifdef switches\r
+//\r
+// LZHUFF_COMPRESSION & LZHUFF_DECOMPRESSION - not yet functional!\r
+//\r
+// Usage Explanition :\r
+//\r
+// if LZHUFF_COMPRESSION is defined then the compression code & data is\r
+// compiled and so-forth for the decompression code.\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+\r
+\r
+#include <fcntl.h>\r
+#include <io.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <dos.h>\r
+\r
+#include "lzhuff.h"\r
+#include "jam_io.h"\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// SWITCHES\r
+//\r
+// NOTE : Make sure the appropriate switches are set in SOFT.c for Softlib\r
+// archive support.\r
+//\r
+//===========================================================================\r
+\r
+\r
+#define INCLUDE_LZH_COMP 0\r
+#define INCLUDE_LZH_DECOMP 1\r
+\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// DEFINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+#define EXIT_OK 0\r
+#define EXIT_FAILED -1\r
+\r
+/* LZSS Parameters */\r
+\r
+#define N 4096 /* Size of string buffer */\r
+#define F 30 /* Size of look-ahead buffer */\r
+#define THRESHOLD 2\r
+#define NIL N /* End of tree's node */\r
+\r
+/* Huffman coding parameters */\r
+\r
+#define N_CHAR (256 - THRESHOLD + F) /* character code (= 0..N_CHAR-1) */\r
+#define T (N_CHAR * 2 - 1) /* Size of table */\r
+#define R (T - 1) /* root position */\r
+#define MAX_FREQ 0x8000 /* update when cumulative frequency */\r
+ /* reaches to this value */\r
+\r
+\r
+//==========================================================================\r
+//\r
+// LOCAL PROTOTYPES\r
+//\r
+//==========================================================================\r
+\r
+\r
+static void StartHuff();\r
+static void reconst();\r
+static void update(int c);\r
+\r
+\r
+static void DeleteNode(int p); /* Deleting node from the tree */\r
+static void InsertNode(int r); /* Inserting node to the tree */\r
+static void InitTree(void); /* Initializing tree */\r
+static void Putcode(long outfile_ptr, int l, unsigned c,unsigned PtrTypes); /* output c bits */\r
+static void EncodeChar(long outfile_ptr, unsigned c, unsigned PtrTypes);\r
+static void EncodePosition(long outfile_ptr, unsigned c, unsigned PtrTypes);\r
+static void EncodeEnd(long outfile_ptr,unsigned PtrTypes);\r
+\r
+\r
+static int GetByte(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes);\r
+static int GetBit(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes); /* get one bit */\r
+static int DecodeChar(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes);\r
+static int DecodePosition(long infile_ptr,unsigned long *CompressLength, unsigned PtrTypes);\r
+\r
+\r
+\r
+\r
+//==========================================================================\r
+//\r
+// USER AVAILABLE VECTORS\r
+//\r
+//==========================================================================\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// LZHUFF DISPLAY VECTORS\r
+//\r
+// These vectors allow you to hook up any form of display you desire for\r
+// displaying the compression/decompression status.\r
+//\r
+// These routines are called inside of the compression/decompression routines\r
+// and pass the orginal size of data and current position within that\r
+// data. This allows for any kind of "% Done" messages.\r
+//\r
+// Your functions MUST have the following parameters in this order...\r
+//\r
+// void VectorRoutine(unsigned long OrginalSize,unsigned long CurPosition)\r
+//\r
+//\r
+\r
+#if INCLUDE_LZH_COMP\r
+void (*LZH_CompressDisplayVector)() = NULL;\r
+#endif\r
+\r
+#if INCLUDE_LZH_DECOMP\r
+void (*LZH_DecompressDisplayVector)() = NULL;\r
+#endif\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// GLOBAL VARIABLES\r
+//\r
+//===========================================================================\r
+ /* pointing children nodes (son[], son[] + 1)*/\r
+\r
+int far son[T];\r
+unsigned code, len;\r
+\r
+ //\r
+ // pointing parent nodes.\r
+ // area [T..(T + N_CHAR - 1)] are pointers for leaves\r
+ //\r
+\r
+int far prnt[T + N_CHAR];\r
+\r
+unsigned far freq[T + 1]; /* cumulative freq table */\r
+\r
+unsigned long textsize = 0, codesize = 0, printcount = 0,datasize;\r
+unsigned char far text_buf[N + F - 1];\r
+\r
+\r
+\r
+ //\r
+ // COMPRESSION VARIABLES\r
+ //\r
+\r
+#if INCLUDE_LZH_COMP\r
+\r
+static int match_position,match_length, lson[N + 1], rson[N + 257], dad[N + 1];\r
+unsigned putbuf = 0;\r
+unsigned char putlen = 0;\r
+\r
+ //\r
+ // Tables for encoding/decoding upper 6 bits of\r
+ // sliding dictionary pointer\r
+ //\r
+\r
+ //\r
+ // encoder table\r
+ //\r
+\r
+unsigned char far p_len[64] = {\r
+ 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,\r
+ 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06,\r
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08\r
+};\r
+\r
+unsigned char far p_code[64] = {\r
+ 0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68,\r
+ 0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C,\r
+ 0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC,\r
+ 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE,\r
+ 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE,\r
+ 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,\r
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,\r
+ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF\r
+};\r
+#endif\r
+\r
+\r
+ //\r
+ // DECOMPRESSION VARIABLES\r
+ //\r
+\r
+\r
+ //\r
+ // decoder table\r
+ //\r
+\r
+#if INCLUDE_LZH_DECOMP\r
+\r
+unsigned char far d_code[256] = {\r
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,\r
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,\r
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,\r
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,\r
+ 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,\r
+ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,\r
+ 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,\r
+ 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,\r
+ 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,\r
+ 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,\r
+ 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,\r
+ 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,\r
+ 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,\r
+ 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,\r
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,\r
+};\r
+\r
+unsigned char far d_len[256] = {\r
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
+};\r
+\r
+unsigned getbuf = 0;\r
+unsigned char getlen = 0;\r
+\r
+#endif\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// COMPRESSION & DECOMPRESSION ROUTINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// StartHuff /* initialize freq tree */\r
+//---------------------------------------------------------------------------\r
+static void StartHuff()\r
+{\r
+ int i, j;\r
+\r
+ for (i = 0; i < N_CHAR; i++) {\r
+ freq[i] = 1;\r
+ son[i] = i + T;\r
+ prnt[i + T] = i;\r
+ }\r
+ i = 0; j = N_CHAR;\r
+ while (j <= R) {\r
+ freq[j] = freq[i] + freq[i + 1];\r
+ son[j] = i;\r
+ prnt[i] = prnt[i + 1] = j;\r
+ i += 2; j++;\r
+ }\r
+ freq[T] = 0xffff;\r
+ prnt[R] = 0;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// reconst /* reconstruct freq tree */\r
+//---------------------------------------------------------------------------\r
+static void reconst()\r
+{\r
+ int i, j, k;\r
+ unsigned f, l;\r
+\r
+ /* halven cumulative freq for leaf nodes */\r
+\r
+ j = 0;\r
+\r
+ for (i = 0; i < T; i++)\r
+ {\r
+ if (son[i] >= T)\r
+ {\r
+ freq[j] = (freq[i] + 1) / 2;\r
+ son[j] = son[i];\r
+ j++;\r
+ }\r
+ }\r
+\r
+ /* make a tree : first, connect children nodes */\r
+\r
+ for (i = 0, j = N_CHAR; j < T; i += 2, j++)\r
+ {\r
+ k = i + 1;\r
+ f = freq[j] = freq[i] + freq[k];\r
+\r
+ for (k = j - 1;f < freq[k]; k--);\r
+\r
+ k++;\r
+ l = (j - k) * 2;\r
+\r
+ (void)memmove(&freq[k + 1], &freq[k], l);\r
+ freq[k] = f;\r
+\r
+ (void)memmove(&son[k + 1], &son[k], l);\r
+ son[k] = i;\r
+ }\r
+\r
+ /* connect parent nodes */\r
+\r
+ for (i = 0; i < T; i++)\r
+ {\r
+ if ((k = son[i]) >= T)\r
+ {\r
+ prnt[k] = i;\r
+ }\r
+ else\r
+ {\r
+ prnt[k] = prnt[k + 1] = i;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// update() update freq tree\r
+//---------------------------------------------------------------------------\r
+static void update(int c)\r
+{\r
+ int i, j, k, l;\r
+\r
+ if (freq[R] == MAX_FREQ)\r
+ {\r
+ reconst();\r
+ }\r
+\r
+ c = prnt[c + T];\r
+\r
+ do {\r
+ k = ++freq[c];\r
+\r
+ //\r
+ // swap nodes to keep the tree freq-ordered\r
+ //\r
+\r
+ if (k > freq[l = c + 1])\r
+ {\r
+ while (k > freq[++l]);\r
+\r
+ l--;\r
+ freq[c] = freq[l];\r
+ freq[l] = k;\r
+\r
+ i = son[c];\r
+ prnt[i] = l;\r
+ if (i < T)\r
+ prnt[i + 1] = l;\r
+\r
+ j = son[l];\r
+ son[l] = i;\r
+\r
+ prnt[j] = c;\r
+ if (j < T)\r
+ prnt[j + 1] = c;\r
+\r
+ son[c] = j;\r
+\r
+ c = l;\r
+ }\r
+ } while ((c = prnt[c]) != 0); /* do it until reaching the root */\r
+}\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// COMPRESSION ROUTINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+\r
+\r
+\r
+\r
+#if INCLUDE_LZH_COMP\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// DeleteNode\r
+//---------------------------------------------------------------------------\r
+static void DeleteNode(int p) /* Deleting node from the tree */\r
+{\r
+ int q;\r
+\r
+ if (dad[p] == NIL)\r
+ return; /* unregistered */\r
+\r
+ if (rson[p] == NIL)\r
+ q = lson[p];\r
+ else\r
+ if (lson[p] == NIL)\r
+ q = rson[p];\r
+ else\r
+ {\r
+ q = lson[p];\r
+ if (rson[q] != NIL)\r
+ {\r
+ do {\r
+ q = rson[q];\r
+ } while (rson[q] != NIL);\r
+\r
+ rson[dad[q]] = lson[q];\r
+ dad[lson[q]] = dad[q];\r
+ lson[q] = lson[p];\r
+ dad[lson[p]] = q;\r
+ }\r
+\r
+ rson[q] = rson[p];\r
+ dad[rson[p]] = q;\r
+ }\r
+\r
+ dad[q] = dad[p];\r
+\r
+ if (rson[dad[p]] == p)\r
+ rson[dad[p]] = q;\r
+ else\r
+ lson[dad[p]] = q;\r
+\r
+ dad[p] = NIL;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// InsertNode\r
+//---------------------------------------------------------------------------\r
+static void InsertNode(int r) /* Inserting node to the tree */\r
+{\r
+ int i, p, cmp;\r
+ unsigned char *key;\r
+ unsigned c;\r
+\r
+ cmp = 1;\r
+ key = &text_buf[r];\r
+ p = N + 1 + key[0];\r
+ rson[r] = lson[r] = NIL;\r
+ match_length = 0;\r
+ for ( ; ; )\r
+ {\r
+ if (cmp >= 0)\r
+ {\r
+ if (rson[p] != NIL)\r
+ p = rson[p];\r
+ else\r
+ {\r
+ rson[p] = r;\r
+ dad[r] = p;\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (lson[p] != NIL)\r
+ p = lson[p];\r
+ else\r
+ {\r
+ lson[p] = r;\r
+ dad[r] = p;\r
+ return;\r
+ }\r
+ }\r
+\r
+\r
+ for (i = 1; i < F; i++)\r
+ if ((cmp = key[i] - text_buf[p + i]) != 0)\r
+ break;\r
+\r
+ if (i > THRESHOLD)\r
+ {\r
+ if (i > match_length)\r
+ {\r
+ match_position = ((r - p) & (N - 1)) - 1;\r
+ if ((match_length = i) >= F)\r
+ break;\r
+ }\r
+\r
+ if (i == match_length)\r
+ {\r
+ if ((c = ((r - p) & (N - 1)) - 1) < match_position)\r
+ {\r
+ match_position = c;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ dad[r] = dad[p];\r
+ lson[r] = lson[p];\r
+ rson[r] = rson[p];\r
+ dad[lson[p]] = r;\r
+ dad[rson[p]] = r;\r
+\r
+ if (rson[dad[p]] == p)\r
+ rson[dad[p]] = r;\r
+ else\r
+ lson[dad[p]] = r;\r
+\r
+ dad[p] = NIL; /* remove p */\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// InitTree\r
+//---------------------------------------------------------------------------\r
+static void InitTree(void) /* Initializing tree */\r
+{\r
+ int i;\r
+\r
+ for (i = N + 1; i <= N + 256; i++)\r
+ rson[i] = NIL; /* root */\r
+\r
+ for (i = 0; i < N; i++)\r
+ dad[i] = NIL; /* node */\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// Putcode\r
+//---------------------------------------------------------------------------\r
+static void Putcode(long outfile_ptr, int l, unsigned c,unsigned PtrTypes) /* output c bits */\r
+{\r
+ putbuf |= c >> putlen;\r
+\r
+ if ((putlen += l) >= 8)\r
+ {\r
+ WritePtr(outfile_ptr, putbuf >> 8, PtrTypes);\r
+ codesize++;\r
+\r
+ if ((putlen -= 8) >= 8)\r
+ {\r
+ WritePtr(outfile_ptr, putbuf, PtrTypes);\r
+ codesize++;\r
+\r
+ putlen -= 8;\r
+ putbuf = c << (l - putlen);\r
+ }\r
+ else\r
+ {\r
+ putbuf <<= 8;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// EncodeChar\r
+//---------------------------------------------------------------------------\r
+static void EncodeChar(long outfile_ptr, unsigned c, unsigned PtrTypes)\r
+{\r
+ unsigned i;\r
+ int j, k;\r
+\r
+ i = 0;\r
+ j = 0;\r
+ k = prnt[c + T];\r
+\r
+ /* search connections from leaf node to the root */\r
+\r
+ do {\r
+ i >>= 1;\r
+\r
+ //\r
+ // if node's address is odd, output 1 else output 0\r
+ //\r
+\r
+ if (k & 1)\r
+ i += 0x8000;\r
+\r
+ j++;\r
+ } while ((k = prnt[k]) != R);\r
+\r
+ Putcode(outfile_ptr, j, i, PtrTypes);\r
+\r
+ code = i;\r
+ len = j;\r
+ update(c);\r
+}\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// EncodePosition\r
+//---------------------------------------------------------------------------\r
+static void EncodePosition(long outfile_ptr, unsigned c, unsigned PtrTypes)\r
+{\r
+ unsigned i;\r
+\r
+ //\r
+ // output upper 6 bits with encoding\r
+ //\r
+\r
+ i = c >> 6;\r
+ Putcode(outfile_ptr, p_len[i], (unsigned)p_code[i] << 8,PtrTypes);\r
+\r
+ //\r
+ // output lower 6 bits directly\r
+ //\r
+\r
+ Putcode(outfile_ptr, 6, (c & 0x3f) << 10,PtrTypes);\r
+}\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// EncodeEnd\r
+//---------------------------------------------------------------------------\r
+static void EncodeEnd(long outfile_ptr,unsigned PtrTypes)\r
+{\r
+ if (putlen)\r
+ {\r
+ WritePtr(outfile_ptr,(putbuf >> 8),PtrTypes);\r
+ codesize++;\r
+ }\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// DECOMPRESSION ROUTINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+\r
+#if INCLUDE_LZH_DECOMP\r
+\r
+//---------------------------------------------------------------------------\r
+// GetByte\r
+//---------------------------------------------------------------------------\r
+static int GetByte(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes)\r
+{\r
+ unsigned i;\r
+\r
+ while (getlen <= 8)\r
+ {\r
+ if (*CompressLength)\r
+ {\r
+ i = ReadPtr(infile_ptr,PtrTypes);\r
+ (*CompressLength)--;\r
+ }\r
+ else\r
+ i = 0;\r
+\r
+ getbuf |= i << (8 - getlen);\r
+ getlen += 8;\r
+ }\r
+\r
+ i = getbuf;\r
+ getbuf <<= 8;\r
+ getlen -= 8;\r
+ return i>>8;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// GetBit\r
+//---------------------------------------------------------------------------\r
+static int GetBit(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes) /* get one bit */\r
+{\r
+ int i;\r
+\r
+ while (getlen <= 8)\r
+ {\r
+ if (*CompressLength)\r
+ {\r
+ i = ReadPtr(infile_ptr,PtrTypes);\r
+ (*CompressLength)--;\r
+ }\r
+ else\r
+ i = 0;\r
+\r
+ getbuf |= i << (8 - getlen);\r
+ getlen += 8;\r
+ }\r
+\r
+ i = getbuf;\r
+ getbuf <<= 1;\r
+ getlen--;\r
+ return (i < 0);\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// DecodeChar\r
+//---------------------------------------------------------------------------\r
+static int DecodeChar(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes)\r
+{\r
+ unsigned c;\r
+\r
+ c = son[R];\r
+\r
+ /*\r
+ * start searching tree from the root to leaves.\r
+ * choose node #(son[]) if input bit == 0\r
+ * else choose #(son[]+1) (input bit == 1)\r
+ */\r
+\r
+ while (c < T)\r
+ {\r
+ c += GetBit(infile_ptr,CompressLength,PtrTypes);\r
+ c = son[c];\r
+ }\r
+\r
+ c -= T;\r
+ update(c);\r
+ return c;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// DecodePosition\r
+//---------------------------------------------------------------------------\r
+static int DecodePosition(long infile_ptr,unsigned long *CompressLength, unsigned PtrTypes)\r
+{\r
+ unsigned i, j, c;\r
+\r
+ //\r
+ // decode upper 6 bits from given table\r
+ //\r
+\r
+ i = GetByte(infile_ptr, CompressLength, PtrTypes);\r
+ c = (unsigned)d_code[i] << 6;\r
+ j = d_len[i];\r
+\r
+ //\r
+ // input lower 6 bits directly\r
+ //\r
+\r
+ j -= 2;\r
+ while (j--)\r
+ {\r
+ i = (i << 1) + GetBit(infile_ptr, CompressLength, PtrTypes);\r
+ }\r
+\r
+ return c | i & 0x3f;\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// EXTERNAL REFERENCED\r
+// COMPRESSION & DECOMPRESSION\r
+// ROUTINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+\r
+\r
+#if INCLUDE_LZH_DECOMP\r
+\r
+//---------------------------------------------------------------------------\r
+// lzhDecompress()\r
+//---------------------------------------------------------------------------\r
+long lzhDecompress(void far *infile, void far *outfile, unsigned long OrginalLength, unsigned long CompressLength, unsigned PtrTypes)\r
+{\r
+ int i, j, k, r, c;\r
+ long count;\r
+\r
+ datasize = textsize = OrginalLength;\r
+ getbuf = 0;\r
+ getlen = 0;\r
+\r
+ if (textsize == 0)\r
+ return;\r
+\r
+ StartHuff();\r
+ for (i = 0; i < N - F; i++)\r
+ text_buf[i] = ' ';\r
+\r
+ r = N - F;\r
+\r
+ for (count = 0; count < textsize; )\r
+ {\r
+ c = DecodeChar((long)&infile,&CompressLength,PtrTypes);\r
+\r
+ if (c < 256)\r
+ {\r
+ WritePtr((long)&outfile,c,PtrTypes);\r
+ datasize--; // Dec # of bytes to write\r
+\r
+ text_buf[r++] = c;\r
+ r &= (N - 1);\r
+ count++; // inc count of bytes written\r
+ }\r
+ else\r
+ {\r
+ i = (r - DecodePosition((long)&infile,&CompressLength,PtrTypes) - 1) & (N - 1);\r
+ j = c - 255 + THRESHOLD;\r
+\r
+ for (k = 0; k < j; k++)\r
+ {\r
+ c = text_buf[(i + k) & (N - 1)];\r
+\r
+ WritePtr((long)&outfile,c,PtrTypes);\r
+ datasize--; // dec count of bytes to write\r
+\r
+ text_buf[r++] = c;\r
+ r &= (N - 1);\r
+ count++; // inc count of bytes written\r
+ }\r
+ }\r
+\r
+ if (LZH_DecompressDisplayVector && (count > printcount))\r
+ {\r
+ LZH_DecompressDisplayVector(OrginalLength,OrginalLength-datasize);\r
+ printcount += 1024;\r
+ }\r
+ }\r
+\r
+// printf("%12ld\n", count);\r
+\r
+ return(count);\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+#if INCLUDE_LZH_COMP\r
+\r
+//---------------------------------------------------------------------------\r
+// lzhCompress()\r
+//---------------------------------------------------------------------------\r
+long lzhCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+ int i, c, len, r, s, last_match_length;\r
+\r
+ textsize = DataLength;\r
+\r
+ if (textsize == 0)\r
+ return;\r
+\r
+ getbuf = 0;\r
+ getlen = 0;\r
+ textsize = 0; /* rewind and rescan */\r
+ codesize = 0;\r
+ datasize = 0; // Init our counter of ReadData...\r
+ StartHuff();\r
+ InitTree();\r
+\r
+ s = 0;\r
+ r = N - F;\r
+\r
+ for (i = s; i < r; i++)\r
+ text_buf[i] = ' ';\r
+\r
+ for (len = 0; len < F && (DataLength > datasize); len++)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes);\r
+ datasize++; // Dec num of bytes to compress\r
+ text_buf[r + len] = c;\r
+ }\r
+\r
+ textsize = len;\r
+\r
+ for (i = 1; i <= F; i++)\r
+ InsertNode(r - i);\r
+\r
+ InsertNode(r);\r
+\r
+ do {\r
+ if (match_length > len)\r
+ match_length = len;\r
+\r
+ if (match_length <= THRESHOLD)\r
+ {\r
+ match_length = 1;\r
+ EncodeChar((long)&outfile,text_buf[r],PtrTypes);\r
+ }\r
+ else\r
+ {\r
+ EncodeChar((long)&outfile, 255 - THRESHOLD + match_length,PtrTypes);\r
+ EncodePosition((long)&outfile, match_position,PtrTypes);\r
+ }\r
+\r
+ last_match_length = match_length;\r
+\r
+ for (i = 0; i < last_match_length && (DataLength > datasize); i++)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes);\r
+ datasize++;\r
+\r
+ DeleteNode(s);\r
+ text_buf[s] = c;\r
+\r
+ if (s < F - 1)\r
+ text_buf[s + N] = c;\r
+\r
+ s = (s + 1) & (N - 1);\r
+ r = (r + 1) & (N - 1);\r
+ InsertNode(r);\r
+ }\r
+\r
+ if (LZH_CompressDisplayVector && ((textsize += i) > printcount))\r
+ {\r
+ LZH_CompressDisplayVector(DataLength,datasize);\r
+ printcount += 1024;\r
+ }\r
+\r
+\r
+ while (i++ < last_match_length)\r
+ {\r
+ DeleteNode(s);\r
+ s = (s + 1) & (N - 1);\r
+ r = (r + 1) & (N - 1);\r
+ if (--len)\r
+ InsertNode(r);\r
+ }\r
+\r
+ } while (len > 0);\r
+\r
+ EncodeEnd((long)&outfile,PtrTypes);\r
+\r
+ return(codesize);\r
+\r
+}\r
+\r
+\r
+#endif
\ No newline at end of file
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+extern void (*LZH_CompressDisplayVector)();\r
+extern void (*LZH_DecompressDisplayVector)();\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// PROTOTYPES\r
+//\r
+//===========================================================================\r
+\r
+\r
+long lzhCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+long lzhDecompress(void far *infile, void far *outfile, unsigned long OrginalLength, unsigned long CompressLength, unsigned PtrTypes);\r
+\r
+\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//===========================================================================\r
+//\r
+// LZW COMPRESSION ROUTINES\r
+// VERSION 1.1\r
+//\r
+// Compression algrythim by Haruhiko OKUMURA\r
+// Implementation by Jim T. Row\r
+//\r
+//\r
+// Copyright (c) 1992 - Softdisk Publishing inc. - All rights reserved\r
+//\r
+//===========================================================================\r
+//\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <fcntl.h>\r
+#include <dos.h>\r
+#include <io.h>\r
+\r
+#include "lzw.h"\r
+#include "jam_io.h"\r
+\r
+\r
+//===========================================================================\r
+//\r
+// SWITCHES\r
+//\r
+// NOTE : Make sure the appropriate switches are set in SOFT.c for Softlib\r
+// archive support.\r
+//\r
+//===========================================================================\r
+\r
+#define INCLUDE_LZW_COMP 0\r
+#define INCLUDE_LZW_DECOMP 1\r
+\r
+\r
+//===========================================================================\r
+//\r
+// DEFINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+#define LZW_N 4096\r
+#define LZW_F 18\r
+\r
+#define LZW_THRESHOLD 2 // encode string into position and\r
+ // length if match_length is greater\r
+ // than this\r
+\r
+#define LZW_NIL LZW_N // index for root of bin search trees\r
+\r
+\r
+//============================================================================\r
+//\r
+// GLOBAL VARIABLES NECESSARY FOR\r
+//\r
+// COMP/DECOMP ROUTINES.\r
+//\r
+//============================================================================\r
+\r
+\r
+\r
+unsigned char far LZW_ring_buffer[LZW_N + LZW_F - 1]; // ring buffer of size\r
+ // LZW_N, with extra\r
+ // LZW_F-1 bytes to\r
+ // facilitate\r
+ // string comparison\r
+\r
+#if INCLUDE_LZW_COMP\r
+\r
+int LZW_match_pos, // MAtchLength of longest match. These are set by the\r
+ // InsertNode() procedure.\r
+ LZW_match_len,\r
+\r
+ // left & right children & parents -- These constitute binary search trees. */\r
+\r
+ LZW_left_child[LZW_N + 1],\r
+ LZW_right_child[LZW_N + 257],\r
+ LZW_parent[LZW_N + 1];\r
+\r
+#endif\r
+\r
+\r
+//============================================================================\r
+//\r
+// LZW DISPLAY VECTORS\r
+//\r
+// These vectors allow you to hook up any form of display you desire for\r
+// displaying the compression/decompression status.\r
+//\r
+// These routines are called inside of the compression/decompression routines\r
+// and pass the orginal size of data and current position within that\r
+// data. This allows for any kind of "% Done" messages.\r
+//\r
+// Your functions MUST have the following parameters in this order...\r
+//\r
+// void VectorRoutine(unsigned long OrginalSize,unsigned long CurPosition)\r
+//\r
+//\r
+\r
+void (*LZW_CompressDisplayVector)() = NULL;\r
+void (*LZW_DecompressDisplayVector)() = NULL;\r
+\r
+\r
+\r
+\r
+//============================================================================\r
+//\r
+// SUPPORT ROUTINES FOR LZW COMPRESSION\r
+//\r
+//============================================================================\r
+\r
+\r
+#if INCLUDE_LZW_COMP\r
+\r
+static void InitLZWTree(void) /* initialize trees */\r
+{\r
+ int i;\r
+\r
+ /* For i = 0 to LZW_N - 1, LZW_right_child[i] and LZW_left_child[i] will be the right and\r
+ left children of node i. These nodes need not be initialized.\r
+ Also, LZW_parent[i] is the parent of node i. These are initialized to\r
+ LZW_NIL (= LZW_N), which stands for 'not used.'\r
+ For i = 0 to 255, LZW_right_child[LZW_N + i + 1] is the root of the tree\r
+ for strings that begin with character i. These are initialized\r
+ to LZW_NIL. Note there are 256 trees. */\r
+\r
+ for (i = LZW_N + 1; i <= LZW_N + 256; i++)\r
+ LZW_right_child[i] = LZW_NIL;\r
+\r
+ for (i = 0; i < LZW_N; i++)\r
+ LZW_parent[i] = LZW_NIL;\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+\r
+static void InsertLZWNode(unsigned long r)\r
+\r
+ /* Inserts string of length LZW_F, LZW_ring_buffer[r..r+LZW_F-1], into one of the\r
+ trees (LZW_ring_buffer[r]'th tree) and returns the longest-match position\r
+ and length via the global variables LZW_match_pos and LZW_match_len.\r
+ If LZW_match_len = LZW_F, then removes the old node in favor of the new\r
+ one, because the old one will be deleted sooner.\r
+ Note r plays double role, as tree node and position in buffer. */\r
+{\r
+ int i, p, cmp;\r
+ unsigned char *key;\r
+\r
+ cmp = 1;\r
+ key = &LZW_ring_buffer[r];\r
+ p = LZW_N + 1 + key[0];\r
+ LZW_right_child[r] = LZW_left_child[r] = LZW_NIL;\r
+ LZW_match_len = 0;\r
+\r
+ for ( ; ; )\r
+ {\r
+ if (cmp >= 0)\r
+ {\r
+ if (LZW_right_child[p] != LZW_NIL)\r
+ p = LZW_right_child[p];\r
+ else\r
+ {\r
+ LZW_right_child[p] = r;\r
+ LZW_parent[r] = p;\r
+ return;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (LZW_left_child[p] != LZW_NIL)\r
+ p = LZW_left_child[p];\r
+ else\r
+ {\r
+ LZW_left_child[p] = r;\r
+ LZW_parent[r] = p;\r
+ return;\r
+ }\r
+ }\r
+\r
+ for (i = 1; i < LZW_F; i++)\r
+ if ((cmp = key[i] - LZW_ring_buffer[p + i]) != 0)\r
+ break;\r
+\r
+ if (i > LZW_match_len)\r
+ {\r
+ LZW_match_pos = p;\r
+ if ((LZW_match_len = i) >= LZW_F)\r
+ break;\r
+ }\r
+ }\r
+\r
+ LZW_parent[r] = LZW_parent[p];\r
+ LZW_left_child[r] = LZW_left_child[p];\r
+ LZW_right_child[r] = LZW_right_child[p];\r
+ LZW_parent[LZW_left_child[p]] = r;\r
+ LZW_parent[LZW_right_child[p]] = r;\r
+\r
+ if (LZW_right_child[LZW_parent[p]] == p)\r
+ LZW_right_child[LZW_parent[p]] = r;\r
+ else\r
+ LZW_left_child[LZW_parent[p]] = r;\r
+\r
+ LZW_parent[p] = LZW_NIL; /* remove p */\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+\r
+static void DeleteLZWNode(unsigned long p) /* deletes node p from tree */\r
+{\r
+ int q;\r
+\r
+ if (LZW_parent[p] == LZW_NIL)\r
+ return; /* not in tree */\r
+\r
+ if (LZW_right_child[p] == LZW_NIL)\r
+ q = LZW_left_child[p];\r
+ else\r
+ if (LZW_left_child[p] == LZW_NIL)\r
+ q = LZW_right_child[p];\r
+ else\r
+ {\r
+ q = LZW_left_child[p];\r
+ if (LZW_right_child[q] != LZW_NIL)\r
+ {\r
+ do {\r
+\r
+ q = LZW_right_child[q];\r
+\r
+ } while (LZW_right_child[q] != LZW_NIL);\r
+\r
+ LZW_right_child[LZW_parent[q]] = LZW_left_child[q];\r
+ LZW_parent[LZW_left_child[q]] = LZW_parent[q];\r
+ LZW_left_child[q] = LZW_left_child[p];\r
+ LZW_parent[LZW_left_child[p]] = q;\r
+ }\r
+\r
+ LZW_right_child[q] = LZW_right_child[p];\r
+ LZW_parent[LZW_right_child[p]] = q;\r
+ }\r
+\r
+ LZW_parent[q] = LZW_parent[p];\r
+ if (LZW_right_child[LZW_parent[p]] == p)\r
+ LZW_right_child[LZW_parent[p]] = q;\r
+ else\r
+ LZW_left_child[LZW_parent[p]] = q;\r
+\r
+ LZW_parent[p] = LZW_NIL;\r
+}\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// lzwCompress() - Compresses data from an input ptr to a dest ptr\r
+//\r
+// PARAMS:\r
+// infile - Pointer at the BEGINNING of the data to compress\r
+// outfile - Pointer to the destination (no header).\r
+// DataLength - Number of bytes to compress.\r
+// PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).\r
+//\r
+// RETURNS:\r
+// Length of compressed data.\r
+//\r
+// COMPTYPE : ct_LZW\r
+//\r
+// NOTES : Does not write ANY header information!\r
+//\r
+unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+ short i;\r
+ short c, len, r, s, last_LZW_match_len, code_buf_ptr;\r
+ unsigned char code_buf[17], mask;\r
+ unsigned long complen = 0;\r
+\r
+ unsigned CodeCount = 0;\r
+ unsigned long OrgDataLen = DataLength;\r
+\r
+ // initialize trees\r
+\r
+ InitLZWTree();\r
+\r
+ code_buf[0] = 0;\r
+\r
+ //\r
+ // code_buf[1..16] saves eight units of code, and code_buf[0] works\r
+ // as eight flags, "1" representing that the unit is an unencoded\r
+ // letter (1 byte), "0" a position-and-length pair (2 bytes). Thus,\r
+ // eight units require at most 16 bytes of code.\r
+ //\r
+\r
+ code_buf_ptr = mask = 1;\r
+ s = 0;\r
+ r = LZW_N - LZW_F;\r
+\r
+ // Clear the buffer with any character that will appear often.\r
+ //\r
+\r
+ for (i = s; i < r; i++)\r
+ LZW_ring_buffer[i] = ' ';\r
+\r
+ // Read LZW_F bytes into the last LZW_F bytes of the buffer\r
+ //\r
+\r
+ for (len = 0; (len < LZW_F) && DataLength; len++)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes);\r
+ DataLength--;\r
+\r
+ // text of size zero\r
+\r
+ LZW_ring_buffer[r + len] = c;\r
+ }\r
+\r
+ if (!(len && DataLength))\r
+ return(0);\r
+\r
+ //\r
+ // Insert the LZW_F strings, each of which begins with one or more\r
+ // 'space' characters. Note the order in which these strings\r
+ // are inserted. This way, degenerate trees will be less likely\r
+ // to occur.\r
+ //\r
+\r
+ for (i = 1; i <= LZW_F; i++)\r
+ InsertLZWNode(r - i);\r
+\r
+ //\r
+ // Finally, insert the whole string just read. The global\r
+ // variables LZW_match_len and LZW_match_pos are set. */\r
+ //\r
+\r
+ InsertLZWNode(r);\r
+\r
+ do {\r
+ // LZW_match_len may be spuriously long near the end of text.\r
+ //\r
+\r
+ if (LZW_match_len > len)\r
+ LZW_match_len = len;\r
+\r
+ if (LZW_match_len <= LZW_THRESHOLD)\r
+ {\r
+ // Not long enough match. Send one byte.\r
+ //\r
+\r
+ LZW_match_len = 1;\r
+\r
+ // 'send one byte' flag\r
+ //\r
+\r
+ code_buf[0] |= mask;\r
+\r
+ // Send uncoded.\r
+ //\r
+\r
+ code_buf[code_buf_ptr++] = LZW_ring_buffer[r];\r
+ }\r
+ else\r
+ {\r
+ code_buf[code_buf_ptr++] = (unsigned char) LZW_match_pos;\r
+ code_buf[code_buf_ptr++] = (unsigned char) (((LZW_match_pos >> 4) & 0xf0) | (LZW_match_len - (LZW_THRESHOLD + 1)));\r
+\r
+ // Send position and length pair.\r
+ // Note LZW_match_len > LZW_THRESHOLD.\r
+ }\r
+\r
+ if ((mask <<= 1) == 0)\r
+ {\r
+ // Shift mask left one bit.\r
+ // Send at most 8 units of data\r
+\r
+ for (i = 0; i < code_buf_ptr; i++)\r
+ WritePtr((long)&outfile,code_buf[i],PtrTypes);\r
+\r
+ complen += code_buf_ptr;\r
+ code_buf[0] = 0;\r
+ code_buf_ptr = mask = 1;\r
+ }\r
+\r
+ last_LZW_match_len = LZW_match_len;\r
+\r
+ for (i = 0; i < last_LZW_match_len && DataLength; i++)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes);\r
+ DataLength--;\r
+\r
+ DeleteLZWNode(s); // Delete old strings and\r
+ LZW_ring_buffer[s] = c; // read new bytes\r
+\r
+ // If the position is near the end of buffer, extend the\r
+ // buffer to make string comparison easier.\r
+\r
+ if (s < LZW_F - 1)\r
+ LZW_ring_buffer[s + LZW_N] = c;\r
+\r
+ // Since this is a ring buffer, inc the position modulo LZW_N.\r
+ //\r
+\r
+ s = (s + 1) & (LZW_N - 1);\r
+ r = (r + 1) & (LZW_N - 1);\r
+\r
+ // Register the string in LZW_ring_buffer[r..r+LZW_F-1]\r
+ //\r
+\r
+ InsertLZWNode(r);\r
+ }\r
+\r
+\r
+ //\r
+ // MANAGE DISPLAY VECTOR\r
+ //\r
+\r
+ if (LZW_CompressDisplayVector)\r
+ {\r
+ // Update display every 1k!\r
+ //\r
+\r
+ if ((CodeCount += i) > 1024)\r
+ {\r
+ LZW_CompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);\r
+ CodeCount = 0;\r
+ }\r
+ }\r
+\r
+\r
+ //\r
+ // Manage Compression tree..\r
+ //\r
+\r
+ while (i++ < last_LZW_match_len)\r
+ {\r
+ // After the end of text,\r
+ DeleteLZWNode(s); // no need to read, but\r
+\r
+ s = (s + 1) & (LZW_N - 1);\r
+ r = (r + 1) & (LZW_N - 1);\r
+\r
+ if (--len)\r
+ InsertLZWNode(r); // buffer may not be empty.\r
+ }\r
+\r
+ } while (len > 0); // until length of string to be processed is zero\r
+\r
+\r
+ if (code_buf_ptr > 1)\r
+ {\r
+ // Send remaining code.\r
+ //\r
+\r
+ for (i = 0; i < code_buf_ptr; i++)\r
+ WritePtr((long)&outfile,code_buf[i],PtrTypes);\r
+\r
+ complen += code_buf_ptr;\r
+ }\r
+\r
+ if (LZW_CompressDisplayVector)\r
+ {\r
+ if ((CodeCount += i) > 1024)\r
+ {\r
+ LZW_CompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);\r
+ }\r
+ }\r
+\r
+ return(complen);\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+//============================================================================\r
+//\r
+// SUPPORT ROUTINES FOR LZW DECOMPRESSION\r
+//\r
+//============================================================================\r
+\r
+#if INCLUDE_LZW_DECOMP\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// lzwDecompress() - Compresses data from an input ptr to a dest ptr\r
+//\r
+// PARAMS:\r
+// infile - Pointer at the BEGINNING of the compressed data (no header!)\r
+// outfile - Pointer to the destination.\r
+// DataLength - Number of bytes to decompress.\r
+// PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).\r
+//\r
+// RETURNS:\r
+// Length of compressed data.\r
+//\r
+// COMPTYPE : ct_LZW\r
+//\r
+// NOTES : Does not write ANY header information!\r
+//\r
+void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+ int i, j, k, r, c;\r
+ unsigned int flags;\r
+ unsigned char Buffer[8];\r
+// unsigned char LZW_ring_buffer[LZW_N + LZW_F - 1];\r
+\r
+ unsigned CodeCount = 0;\r
+ unsigned long OrgDataLen = DataLength;\r
+\r
+ for (i = 0; i < LZW_N - LZW_F; i++)\r
+ LZW_ring_buffer[i] = ' ';\r
+\r
+ r = LZW_N - LZW_F;\r
+ flags = 0;\r
+\r
+ for ( ; ; )\r
+ {\r
+ if (((flags >>= 1) & 256) == 0)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes);\r
+\r
+ flags = c | 0xff00; // uses higher byte cleverly to count 8\r
+ }\r
+\r
+ if (flags & 1)\r
+ {\r
+ c = ReadPtr((long)&infile,PtrTypes); // Could test for EOF iff FFILE type\r
+\r
+ WritePtr((long)&outfile,c,PtrTypes);\r
+\r
+ if (!--DataLength)\r
+ return;\r
+\r
+ LZW_ring_buffer[r++] = c;\r
+ r &= (LZW_N - 1);\r
+ }\r
+ else\r
+ {\r
+ i = ReadPtr((long)&infile,PtrTypes);\r
+\r
+ j = ReadPtr((long)&infile,PtrTypes);\r
+\r
+ i |= ((j & 0xf0) << 4);\r
+ j = (j & 0x0f) + LZW_THRESHOLD;\r
+\r
+ for (k = 0; k <= j; k++)\r
+ {\r
+ c = LZW_ring_buffer[(i + k) & (LZW_N - 1)];\r
+\r
+ WritePtr((long)&outfile,c,PtrTypes);\r
+\r
+ if (!--DataLength)\r
+ return;\r
+\r
+ LZW_ring_buffer[r++] = c;\r
+ r &= (LZW_N - 1);\r
+ }\r
+ }\r
+\r
+\r
+\r
+ //\r
+ // MANAGE DISPLAY VECTOR\r
+ //\r
+\r
+ if (LZW_DecompressDisplayVector)\r
+ {\r
+ //\r
+ // Update DisplayVector every 1K\r
+ //\r
+\r
+ if ((CodeCount+=k) > 1024)\r
+ {\r
+ LZW_DecompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);\r
+ CodeCount = 0;\r
+ }\r
+ }\r
+\r
+ }\r
+}\r
+\r
+#endif\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// EXTERN DEFINITIONS FOR DISPLAY VEC ROUTINES\r
+//\r
+//--------------------------------------------------------------------------\r
+\r
+\r
+extern void (*LZW_CompressDisplayVector)();\r
+extern void (*LZW_DecompressDisplayVector)();\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+// FUNCTION PROTOTYPEING\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+\r
+\r
+unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+///////////////////////////////////////\r
+//\r
+// TED5 Map Header for ARM\r
+//\r
+///////////////////////////////////////\r
+\r
+//\r
+// Map Names\r
+//\r
+typedef enum {\r
+ TOWN_MORBIDITY_MAP, // 0\r
+ DARK_FOREST_MAP, // 1\r
+ GARDEN_OF_SOULS_MAP, // 2\r
+ LOST_CITY_DAMND_MAP, // 3\r
+ TEMPLE_OF_VIPER_MAP, // 4\r
+ TORTURE_CHAMBER_MAP, // 5\r
+ DEMONS_HOLD_MAP, // 6\r
+ COLONY_FIRE_ANT_MAP, // 7\r
+ HALL_WRETCH_POX_MAP, // 8\r
+ LAIR_OF_SUCUBUS_MAP, // 9\r
+ BLOOD_CHAMB_EYE_MAP, // 10\r
+ FLAMING_INFERNO_MAP, // 11\r
+ SUBTERR_RIVER_MAP, // 12\r
+ CRYSTAL_MAZE_MG_MAP, // 13\r
+ RAMPARTS_OF_NEM_MAP, // 14\r
+ FORTRESS_OF_NEM_MAP, // 15\r
+ PASSAGE_TO_SURF_MAP, // 16\r
+ LASTMAP\r
+ } mapnames;\r
+\r
+//\r
+// TILEINFO offsets\r
+//\r
+#define ANIM 402\r
+#define FLAGS (ANIM+NUMTILE16)\r
--- /dev/null
+The Catacomb Armageddon
+=======================
+
+This repository contains the source code for The Catacomb Armageddon. The
+source code is designed for Borland C++ 2.0, but compiled fine with Borland C++
+3.1 at the time of this release.
+
+It is released under the GNU GPLv2. Please see COPYING for license details.
+
+This release does not affect the licensing for the game data files. You will
+need to legally acquire the game data in order to use the exe built from this
+source code.
+
+To run the executable, the following command must be used or the check must be
+disabled in C5_MAIN.C:
+ armgame.exe ^(a@&r`
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef _SL_FILE_H\r
+#define _SL_FILE_H\r
+\r
+\r
+//==========================================================================\r
+//\r
+// DEFINES\r
+//\r
+//==========================================================================\r
+\r
+#ifndef MakeID\r
+#define MakeID(a,b,c,d) (((long)(d)<<24L)|((long)(c)<<16L)|((long)(b)<<8L)|(long)(a))\r
+#endif\r
+\r
+\r
+#define ID_SLIB MakeID('S','L','I','B')\r
+#define SLIB ("SLIB")\r
+#define SOFTLIB_VER 2\r
+#define ID_CHUNK MakeID('C','U','N','K')\r
+\r
+\r
+\r
+//==========================================================================\r
+//\r
+// TYPES\r
+//\r
+//==========================================================================\r
+\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// SOFTLIB File Entry Types\r
+//--------------------------------------------------------------------------\r
+typedef enum LibFileTypes\r
+{\r
+ lib_DATA =0, // Just streight raw data\r
+// lib_AUDIO, // Multi chunk audio file\r
+\r
+} LibFileTypes;\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// SOFTLIB Library File header..\r
+//\r
+// * This header will NEVER change! *\r
+//--------------------------------------------------------------------------\r
+\r
+typedef struct SoftLibHdr\r
+{\r
+ unsigned Version; // Library Version Num\r
+ unsigned FileCount;\r
+} SoftlibHdr;\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// SOFTLIB Directory Entry Hdr\r
+//\r
+// This can change according to Version of SoftLib (Make sure we are\r
+// always downward compatable!\r
+//--------------------------------------------------------------------------\r
+\r
+#define SL_FILENAMESIZE 16\r
+\r
+typedef struct FileEntryHdr\r
+{\r
+ char FileName[SL_FILENAMESIZE]; // NOTE : May not be null terminated!\r
+ unsigned long Offset;\r
+ unsigned long ChunkLen;\r
+ unsigned long OrginalLength;\r
+ short Compression; // ct_TYPES\r
+} FileEntryHdr;\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// SOFTLIB Entry Chunk Header\r
+//--------------------------------------------------------------------------\r
+\r
+typedef struct ChunkHeader\r
+{\r
+ unsigned long HeaderID;\r
+ unsigned long OrginalLength;\r
+ short Compression; // ct_TYPES\r
+} ChunkHeader;\r
+\r
+\r
+\r
+#endif
\ No newline at end of file
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <fcntl.h>\r
+#include <dos.h>\r
+#include <io.h>\r
+\r
+#include "def.h"\r
+#include "gelib.h"\r
+#include "soft.h"\r
+#include "lzw.h"\r
+#include "lzhuff.h"\r
+#include "jam_io.h"\r
+\r
+\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+// SWITCHES\r
+//\r
+//===========================================================================\r
+\r
+#define LZH_SUPPORT 1\r
+#define LZW_SUPPORT 0\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+// GENERAL LOAD ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// BLoad() -- THIS HAS NOT BEEN FULLY TESTED!\r
+//\r
+// NOTICE : This version of BLOAD is compatable with JAMPak V3.0 and the\r
+// new fileformat...\r
+//--------------------------------------------------------------------------\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr)\r
+{\r
+ int handle;\r
+\r
+ memptr SrcPtr;\r
+ unsigned long i, j, k, r, c;\r
+ word flags;\r
+ byte Buffer[8];\r
+ unsigned long SrcLen,DstLen;\r
+ struct CMP1Header CompHeader;\r
+ boolean Compressed = false;\r
+\r
+\r
+ memset((void *)&CompHeader,0,sizeof(struct CMP1Header));\r
+\r
+ //\r
+ // Open file to load....\r
+ //\r
+\r
+ if ((handle = open(SourceFile, O_RDONLY|O_BINARY)) == -1)\r
+ return(0);\r
+\r
+ //\r
+ // Look for JAMPAK headers\r
+ //\r
+\r
+ read(handle,Buffer,4);\r
+\r
+ if (!strncmp(Buffer,COMP,4))\r
+ {\r
+ //\r
+ // Compressed under OLD file format\r
+ //\r
+\r
+ Compressed = true;\r
+ SrcLen = Verify(SourceFile);\r
+\r
+ read(handle,(void *)&CompHeader.OrginalLen,4);\r
+ CompHeader.CompType = ct_LZW;\r
+ MM_GetPtr(DstPtr,CompHeader.OrginalLen);\r
+ if (!*DstPtr)\r
+ return(0);\r
+ }\r
+ else\r
+ if (!strncmp(Buffer,CMP1,4))\r
+ {\r
+ //\r
+ // Compressed under new file format...\r
+ //\r
+\r
+ Compressed = true;\r
+ SrcLen = Verify(SourceFile);\r
+\r
+ read(handle,(void *)&CompHeader,sizeof(struct CMP1Header));\r
+ MM_GetPtr(DstPtr,CompHeader.OrginalLen);\r
+ if (!*DstPtr)\r
+ return(0);\r
+ }\r
+ else\r
+ DstLen = Verify(SourceFile);\r
+\r
+\r
+ //\r
+ // Load the file in memory...\r
+ //\r
+\r
+ if (Compressed)\r
+ {\r
+ DstLen = CompHeader.OrginalLen;\r
+\r
+ if ((MM_TotalFree() < SrcLen) && (CompHeader.CompType))\r
+ {\r
+ if (!InitBufferedIO(handle,&lzwBIO))\r
+ TrashProg("No memory for buffered I/O.");\r
+\r
+ switch (CompHeader.CompType)\r
+ {\r
+ #if LZW_SUPPORT\r
+ case ct_LZW:\r
+ lzwDecompress(&lzwBIO,MK_FP(*DstPtr,0),CompHeader.OrginalLen,(SRC_BFILE|DEST_MEM));\r
+ break;\r
+ #endif\r
+\r
+ #if LZH_SUPPORT\r
+ case ct_LZH:\r
+ lzhDecompress(&lzwBIO,MK_FP(*DstPtr,0),CompHeader.OrginalLen,CompHeader.CompressLen,(SRC_BFILE|DEST_MEM));\r
+ break;\r
+ #endif\r
+\r
+ default:\r
+ TrashProg("BLoad() - Unrecognized/Supported compression");\r
+ break;\r
+ }\r
+\r
+ FreeBufferedIO(&lzwBIO);\r
+ }\r
+ else\r
+ {\r
+ CA_LoadFile(SourceFile,&SrcPtr);\r
+ switch (CompHeader.CompType)\r
+ {\r
+ #if LZW_SUPPORT\r
+ case ct_LZW:\r
+ lzwDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),CompHeader.OrginalLen,(SRC_MEM|DEST_MEM));\r
+ break;\r
+ #endif\r
+\r
+ #if LZH_SUPPORT\r
+ case ct_LZH:\r
+ lzhDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),CompHeader.OrginalLen,CompHeader.CompressLen,(SRC_MEM|DEST_MEM));\r
+ break;\r
+ #endif\r
+\r
+ default:\r
+ TrashProg("BLoad() - Unrecognized/Supported compression");\r
+ break;\r
+ }\r
+ MM_FreePtr(&SrcPtr);\r
+ }\r
+ }\r
+ else\r
+ CA_LoadFile(SourceFile,DstPtr);\r
+\r
+ close(handle);\r
+ return(DstLen);\r
+}\r
+\r
+\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// LoadLIBShape()\r
+//\r
+int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP)\r
+{\r
+ #define CHUNK(Name) (*ptr == *Name) && \\r
+ (*(ptr+1) == *(Name+1)) && \\r
+ (*(ptr+2) == *(Name+2)) && \\r
+ (*(ptr+3) == *(Name+3))\r
+\r
+\r
+ int RT_CODE;\r
+ FILE *fp;\r
+ char CHUNK[5];\r
+ char far *ptr;\r
+ memptr IFFfile = NULL;\r
+ unsigned long FileLen, size, ChunkLen;\r
+ int loop;\r
+\r
+\r
+ RT_CODE = 1;\r
+\r
+ // Decompress to ram and return ptr to data and return len of data in\r
+ // passed variable...\r
+\r
+ if (!LoadLIBFile(SLIB_Filename,Filename,&IFFfile))\r
+ TrashProg("Error Loading Compressed lib shape!");\r
+\r
+ // Evaluate the file\r
+ //\r
+ ptr = MK_FP(IFFfile,0);\r
+ if (!CHUNK("FORM"))\r
+ goto EXIT_FUNC;\r
+ ptr += 4;\r
+\r
+ FileLen = *(long far *)ptr;\r
+ SwapLong((long far *)&FileLen);\r
+ ptr += 4;\r
+\r
+ if (!CHUNK("ILBM"))\r
+ goto EXIT_FUNC;\r
+ ptr += 4;\r
+\r
+ FileLen += 4;\r
+ while (FileLen)\r
+ {\r
+ ChunkLen = *(long far *)(ptr+4);\r
+ SwapLong((long far *)&ChunkLen);\r
+ ChunkLen = (ChunkLen+1) & 0xFFFFFFFE;\r
+\r
+ if (CHUNK("BMHD"))\r
+ {\r
+ ptr += 8;\r
+ SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w;\r
+ SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h;\r
+ SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x;\r
+ SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y;\r
+ SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d;\r
+ SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans;\r
+ SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp;\r
+ SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad;\r
+ SwapWord(&SHP->bmHdr.w);\r
+ SwapWord(&SHP->bmHdr.h);\r
+ SwapWord(&SHP->bmHdr.x);\r
+ SwapWord(&SHP->bmHdr.y);\r
+ ptr += ChunkLen;\r
+ }\r
+ else\r
+ if (CHUNK("BODY"))\r
+ {\r
+ ptr += 4;\r
+ size = *((long far *)ptr);\r
+ ptr += 4;\r
+ SwapLong((long far *)&size);\r
+ SHP->BPR = (SHP->bmHdr.w+7) >> 3;\r
+ MM_GetPtr(&SHP->Data,size);\r
+ if (!SHP->Data)\r
+ goto EXIT_FUNC;\r
+ movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size);\r
+ ptr += ChunkLen;\r
+\r
+ break;\r
+ }\r
+ else\r
+ ptr += ChunkLen+8;\r
+\r
+ FileLen -= ChunkLen+8;\r
+ }\r
+\r
+ RT_CODE = 0;\r
+\r
+EXIT_FUNC:;\r
+ if (IFFfile)\r
+ {\r
+// segptr = (memptr)FP_SEG(IFFfile);\r
+ MM_FreePtr(&IFFfile);\r
+ }\r
+\r
+ return (RT_CODE);\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//----------------------------------------------------------------------------\r
+// LoadLIBFile() -- Copies a file from an existing archive to dos.\r
+//\r
+// PARAMETERS :\r
+//\r
+// LibName - Name of lib file created with SoftLib V1.0\r
+//\r
+// FileName - Name of file to load from lib file.\r
+//\r
+// MemPtr - (IF !NULL) - Pointer to memory to load into ..\r
+// (IF NULL) - Routine allocates necessary memory and\r
+// returns a MEM(SEG) pointer to memory allocated.\r
+//\r
+// RETURN :\r
+//\r
+// (IF !NULL) - A pointer to the loaded data.\r
+// (IF NULL) - Error!\r
+//\r
+//----------------------------------------------------------------------------\r
+memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr)\r
+{\r
+ int handle;\r
+ unsigned long header;\r
+ struct ChunkHeader Header;\r
+ unsigned long ChunkLen;\r
+ short x;\r
+ struct FileEntryHdr FileEntry; // Storage for file once found\r
+ struct FileEntryHdr FileEntryHeader; // Header used durring searching\r
+ struct SoftLibHdr LibraryHeader; // Library header - Version Checking\r
+ boolean FileFound = false;\r
+ unsigned long id_slib = ID_SLIB;\r
+ unsigned long id_chunk = ID_CHUNK;\r
+\r
+\r
+ //\r
+ // OPEN SOFTLIB FILE\r
+ //\r
+\r
+ if ((handle = open(LibName,O_RDONLY|O_BINARY, S_IREAD)) == -1)\r
+ return(NULL);\r
+\r
+\r
+ //\r
+ // VERIFY it is a SOFTLIB (SLIB) file\r
+ //\r
+\r
+ if (read(handle,&header,4) == -1)\r
+ {\r
+ close(handle);\r
+ return(NULL);\r
+ }\r
+\r
+ if (header != id_slib)\r
+ {\r
+ close(handle);\r
+ return(NULL);\r
+ }\r
+\r
+\r
+ //\r
+ // CHECK LIBRARY HEADER VERSION NUMBER\r
+ //\r
+\r
+ if (read(handle, &LibraryHeader,sizeof(struct SoftLibHdr)) == -1)\r
+ TrashProg("read error in LoadSLIBFile()\n%c",7);\r
+\r
+ if (LibraryHeader.Version > SOFTLIB_VER)\r
+ TrashProg("Unsupported file ver %d",LibraryHeader.Version);\r
+\r
+\r
+ //\r
+ // MANAGE FILE ENTRY HEADERS...\r
+ //\r
+\r
+ for (x = 1;x<=LibraryHeader.FileCount;x++)\r
+ {\r
+ if (read(handle, &FileEntryHeader,sizeof(struct FileEntryHdr)) == -1)\r
+ {\r
+ close(handle);\r
+ return(NULL);\r
+ }\r
+\r
+ if (!stricmp(FileEntryHeader.FileName,FileName))\r
+ {\r
+ FileEntry = FileEntryHeader;\r
+ FileFound = true;\r
+ }\r
+ }\r
+\r
+ //\r
+ // IF FILE HAS BEEN FOUND THEN SEEK TO POSITION AND EXTRACT\r
+ // ELSE RETURN WITH ERROR CODE...\r
+ //\r
+\r
+ if (FileFound)\r
+ {\r
+ if (lseek(handle,FileEntry.Offset,SEEK_CUR) == -1)\r
+ {\r
+ close(handle);\r
+ return(NULL);\r
+ }\r
+\r
+ //\r
+ // READ CHUNK HEADER - Verify we are at the beginning of a chunk..\r
+ //\r
+\r
+ if (read(handle,(char *)&Header,sizeof(struct ChunkHeader)) == -1)\r
+ TrashProg("LIB File - Unable to read Header!");\r
+\r
+ if (Header.HeaderID != id_chunk)\r
+ TrashProg("LIB File - BAD HeaderID!");\r
+\r
+ //\r
+ // Allocate memory if Necessary...\r
+ //\r
+\r
+\r
+ if (!*MemPtr)\r
+ MM_GetPtr(MemPtr,FileEntry.OrginalLength);\r
+\r
+ //\r
+ // Calculate the length of the data (without the chunk header).\r
+ //\r
+\r
+ ChunkLen = FileEntry.ChunkLen - sizeof(struct ChunkHeader);\r
+\r
+\r
+ //\r
+ // Extract Data from file\r
+ //\r
+\r
+ switch (Header.Compression)\r
+ {\r
+\r
+ #if LZW_SUPPORT\r
+ case ct_LZW:\r
+ if (!InitBufferedIO(handle,&lzwBIO))\r
+ TrashProg("No memory for buffered I/O.");\r
+ lzwDecompress(&lzwBIO,MK_FP(*MemPtr,0),FileEntry.OrginalLength,(SRC_BFILE|DEST_MEM));\r
+ FreeBufferedIO(&lzwBIO);\r
+ break;\r
+ #endif\r
+\r
+ #if LZH_SUPPORT\r
+ case ct_LZH:\r
+ if (!InitBufferedIO(handle,&lzwBIO))\r
+ TrashProg("No memory for buffered I/O.");\r
+ lzhDecompress(&lzwBIO, MK_FP(*MemPtr,0), FileEntry.OrginalLength, ChunkLen, (SRC_BFILE|DEST_MEM));\r
+ FreeBufferedIO(&lzwBIO);\r
+ break;\r
+ #endif\r
+\r
+ case ct_NONE:\r
+ if (!CA_FarRead(handle,MK_FP(*MemPtr,0),ChunkLen))\r
+ {\r
+// close(handle);\r
+ *MemPtr = NULL;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ close(handle);\r
+ TrashProg("Unknown Chunk.Compression Type!");\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ *MemPtr = NULL;\r
+\r
+ close(handle);\r
+ return(*MemPtr);\r
+}\r
+\r
+\r
+\r
+\r
--- /dev/null
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//memptr InitBufferedIO(int handle, BufferedIO *bio);\r
+//void FreeBufferedIO(BufferedIO *bio);\r
+//byte bio_readch(BufferedIO *bio);\r
+\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr);\r
+memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr);\r
+int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP);\r
--- /dev/null
+c2e.convert_special: 0
+e2c.convert_num: 0
+openfiles: /dos/z/cawat/ID_HEADS.H:1260:233:0:
+openfiles: /dos/z/cawat/16_mm.c:3604:3205:0:
+openfiles: /dos/z/cawat/16_mm.h:1364:1519:0:
+openfiles: /dos/z/cawat/ID_CA.C:1203:822:0:
+openfiles: /dos/z/cawat/ID_CA.H:857:343:0:
+openfiles: /dos/z/cawat/16_in.c:1341:982:0:
+openfiles: /dos/z/cawat/16_in.h:4659:4181:1:
+openfiles: /dos/z/cawat/lib_head.h:1091:173:0:
+openfiles: /dos/z/cawat/types.h:240:0:0:
+openfiles: /dos/z/16/src/lib/ems.c:3881:957:0:
+openfiles: /dos/z/16/src/lib/ems.h:944:379:0:
+openfiles: /dos/z/keen-src/id_mm.c:3423:3343:0:
+snr_recursion_level: 0
+convertcolumn_horizontally: 0
+adv_open_matchname: 0
+show_mbhl: 1
+view_line_numbers: 1
+fb_show_backup_f: 0
+htmlbar_thumbnailwidth: 300
+view_left_panel: 0
+default_mime_type: text/plain
+e2c.convert_xml: 1
+c2e.convert_iso: 0
+opendir: file:///dos/z
+wrap_text_default: 0
+bookmarks_filename_mode: 1
+ssearch_text: _seg
+snr_casesens: 0
+view_blocks: 1
+name: cawatcom
+replacelist: /*_1seg*/
+replacelist: /*_1seg*/
+replacelist: \t\t
+fb_show_hidden_f: 0
+editor_tab_width: 4
+show_visible_spacing: 0
+view_statusbar: 1
+display_right_margin: 0
+c2e.IE_apos_workaround: 0
+outputb_scroll_mode: 0
+leftpanel_active_tab: 0
+enable_syntax_scan: 1
+ssearch_regex: 0
+e2c.convert_iso: 0
+ssearch_casesens: 0
+charmap_block: 1
+recent_files: file:///dos/z/16/src/lib/ems.c
+recent_files: file:///dos/z/16/src/lib/ems.h
+recent_files: file:///dos/z/keen-src/id_mm.c
+recent_files: file:///dos/z/bakapee.asm
+recent_files: file:///dos/z/cawat/types.h
+recent_files: file:///dos/z/cawat/ID_CA.C
+recent_files: file:///dos/z/cawat/lib_head.h
+snr_replacetype: 0
+savedir: file:///dos/z/cawat
+spell_check_default: 1
+spell_insert_entities: 0
+last_filefilter:
+htmlbar_notebooktab: 0
+view_blockstack: 1
+snr_escape_chars: 1
+htmlbar_view: 1
+spell_lang: en
+ssearch_dotmatchall: 0
+searchlist: _seg
+searchlist: _1seg
+searchlist: p_1seg
+searchlist: p1seg
+searchlist: pseg
+searchlist: peg
+searchlist: pg
+searchlist: _1
+searchlist: _seg
+searchlist: asm\t
+searchlist: lo
+searchlist: offset
+searchlist: emmname
+searchlist: seg
+searchlist: _seg
+autocomplete: 1
+outputb_show_all_output: 0
+bookmarks_show_mode: 0
+snippets_show_as_menu: 1
+adv_open_recursive: 0
+encoding: SHIFT_JIS
+e2c.convert_special: 0
+autoindent: 1
+fb_viewmode: 0
+recent_dirs: file:///dos/z/cawat
+fb_focus_follow: 1
+ssearch_unescape: 0
+c2e.convert_symbol: 0
+snr_dotmatchall: 0
+c2e.convert_xml: 1
+editor_indent_wspaces: 0
+view_cline: 0
+snr_type: 0
+snr_scope: 0
+bmarksearchmode: 0
+view_main_toolbar: 1
+e2c.convert_symbol: 0
--- /dev/null
+/*
+ * Copyright (C) 2012-2015 sparky4 & pngwen & andrius4669
+ *
+ * This file is part of Project 16.
+ *
+ * Project 16 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Project 16 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, see <http://www.gnu.org/licenses/>, or
+ * write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+/*\r
+ * Just some handy typedefs that make it easier to think about the low\r
+ * level code\r
+ */
+
+#ifndef _LIB_HEAD_H_
+#define _LIB_HEAD_H_
+
+#include <malloc.h>
+#include <dos.h>
+//#include ""
+//#include ""
+//#include "ID_CA.H"
+//#include "AUDIOARM.H"
+#include "types.h"\r
+
+#endif/*_LIB_HEAD_H_*/
--- /dev/null
+/*======================================================================
+ portable.h v1.00 Written by Scott Robert Ladd.
+
+ _MSC_VER Microsoft C 6.0 and later
+ _QC Microsoft Quick C 2.51 and later
+ __TURBOC__ Borland Turbo C and Turbo C++
+ __ZTC_ Zortech C and C++
+ __WATCOMC__ WATCOM C
+=========================================================================*/
+
+/* prevent multiple inclusions of this header file*/
+#if !defined(PORTABLE_H)
+#define PORTABLE_H
+
+/*-----------------------------------------------------------------------
+ Pointer-related macros
+
+ MK_FP creates a far pointer from segment and offset values
+------------------------------------------------------------------------*/
+
+#if !defined(MK_FP)
+ #define MK_FP(seg,off) ((void far *)(((long)(seg) << 16)|(unsigned)(off)))
+#endif
+
+/*-----------------------------------------------------------------------
+ Directory search macros and data structures
+
+ DOSFileData MS-DOS file data structure
+ FIND_FIRST MS-DOS function 0x4E -- find first file matching spec
+ FIND_NEXT MS-DOS function 0x4F -- find subsequent files
+-----------------------------------------------------------------------*/
+
+/* make sure the structure is packed on byte boundary */
+#if defined(_MSC_VER) || defined(_QC) || defined(__WATCOMC__)
+ #pragma pack(1)
+#elif defined(__ZTC__)
+ #pragma align 1
+#elif defined(__TURBOC__)
+ #pragma option -a-
+#endif
+
+/* use this structure in place of compiler-defined file structure */
+typedef struct
+ {
+ char reserved[21];
+ char attrib;
+ unsigned time;
+ unsigned date;
+ long size;
+ char name[13];
+ }
+ DOSFileData;
+
+/* set structure alignment to default */
+#if defined (_MSC_VER) || defined(_QC) || defined(__WATCOMC__)
+ #pragma pack()
+#elif defined(__ZTC__)
+ #pragma align
+#elif defined(__TURBOC__)
+ #pragma option -a.
+#endif
+
+/* include proper header files and create macros */
+#if defined(_MSC_VER) || defined(_QC) || defined(__WATCOMC__)
+ #include "direct.h"
+ #define FIND_FIRST(spec, attr, buf) \
+_dos_findfirst(spec, attr, (struct find_t *)buf) \
+ #define FIND_NEXT(buf) _dos_findnex((struct find_t *)buf)
+#elif defined(__TURBOC__)
+ #include "dir.h"
+ #define FIND_FIRST(spec, attr, buf)\
+findfirst(spec, (struct ffblk *)buf, attr)
+ #define FIND_NEXT(buf) findnext((struct ffblk *)buf)
+#elif defined(__ZTC__)
+ #include "dos.h"
+ #define FIND_FIRST(spec, attr, buf) \
+dos_findfirst(spec, attr, (struct DOS_FIND *)buf)
+ #define FIND_NEXT(buf) dos_findnext((struct DOS_FIND *)buf)
+#endif
+
+/*-----------------------------------------------------------------------
+ I/O Port Macros
+
+ IN_PORT read byte from I/O port
+ IN_PORTW read word from I/O port
+ OUT_PORT write byte to I/O port
+ OUT_PORTW write word to I/O port
+-----------------------------------------------------------------------*/
+
+#if defined(__TURBOC__)
+ #include "dos.h"
+ #define IN_PORT(port) inportb(port)
+ #define IN_PORTW(port) inport(port)
+ #define OUT_PORT(port,val) noutportb(port,val)
+ #define OUT_PORTW(port,val) outport(port,val)
+#else
+ #include "conio.h"
+
+ #define IN_PORT(port) inp(port)
+ #define IN_PORTW(port) inpw(port)
+ #define OUT_PORT(port,val) outp(port,val)
+ #define OUT_PORTW(port,val) outpw(port,val)
+#endif
+
+/*-----------------------------------------------------------------------
+ Borland psuedo register macros
+
+ These macros replace references to Borland's psuedo register
+ variables and geninterrupt() function with traditional struct
+ REGS/int86 references.
+-----------------------------------------------------------------------*/
+
+#if !defined(__TURBOC__)
+ #include "dos.h"
+
+ struct REGS CPURegs;
+
+ #define _AX CPURegs.x.ax
+ #define _BX CPURegs.x.bx
+ #define _CX CPURegs.x.cx
+ #define _DX CPURegs.x.dx
+
+ #define _AH CPURegs.h.ah
+ #define _AL CPURegs.h.al
+ #define _BH CPURegs.h.bh
+ #define _BL CPURegs.h.bl
+ #define _CH CPURegs.h.ch
+ #define _CL CPURegs.h.cl
+ #define _DH CPURegs.h.dh
+ #define _DL CPURegs.h.dl
+
+ #define geninterrupt(n) int86(n,&CPURegs,&CPURegs);
+#endif
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _TYPE_H_
+#define _TYPE_H_
+
+typedef enum {false,true} boolean;
+#define nil ((void *)0)
+
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long dword;
+typedef signed char sbyte;
+typedef signed short sword;
+typedef signed long sdword;
+
+#endif